Я изучаю и пытаюсь использовать MudBlazor для разработки своего веб-сайта. Но когда я добавляю свою пользовательскую страницу (все еще используя пути к страницам в MudTemplate), меню навигации невозможно закрыть. На некоторых сайтах он может быть закрыт. Когда я пытаюсь отладить функцию, которая устанавливает значение параметра, определяющего открытие и закрытие меню навигации, как DrawerToggle, на некоторых страницах это работает, на некоторых страницах - нет. Я провел весь день, пытаясь найти ответ, но безуспешно. Что я должен делать?
Вот мой MainLayout:
@using MUDTEMPLATE.Components
@using MUDTEMPLATE.DBData
@inherits LayoutComponentBase
@inject IConfiguration config
<MudThemeProvider @rendermode = "InteractiveServer" />
<MudDialogProvider @rendermode = "InteractiveServer" />
<MudSnackbarProvider @rendermode = "InteractiveServer" />
<MudLayout>
<MudAppBar Elevation = "0">
<MudIconButton Icon = "@Icons.Material.Filled.Menu" Color = "Color.Inherit" Edge = "Edge.Start" OnClick = "@((e) => DrawerToggle())" />
<MudSpacer />
<MudIconButton Icon = "@Icons.Material.Filled.MoreVert" Color = "Color.Inherit" Target = "_blank" />
</MudAppBar>
<MudDrawer @bind-Open = "_drawerOpen" Elevation = "1" Variant = "DrawerVariant.Temporary">
<MudDrawerHeader>
<MudText Typo = "Typo.h6">DATAVIET - MART</MudText>
</MudDrawerHeader>
<NavMenu />
</MudDrawer>
<MudMainContent>
<MudContainer MaxWidth = "MaxWidth.ExtraExtraLarge" Class = "my-1 pt-1">
@Body
</MudContainer>
</MudMainContent>
</MudLayout>
<div id = "blazor-error-ui">
An unhandled error has occurred.
<a href = "" class = "reload">Reload</a>
<a class = "dismiss">🗙</a>
</div>
@code {
bool _drawerOpen = true;
protected override Task OnInitializedAsync()
{
DB.Set_Connect(config.GetConnectionString("DefaultConnection"));
return base.OnInitializedAsync();
}
void DrawerToggle()
{
_drawerOpen = !_drawerOpen;
}
}
Вот мое NavMenu:
@implements IDisposable
@inject NavigationManager NavigationManager
<MudNavMenu>
<AuthorizeView>
<Authorized>
<MudNavGroup Title = "System" Icon = "@Icons.Material.Filled.Dashboard" Expanded = "true">
<MudNavLink Href = "/Account/Register" Icon = "@Icons.Material.Filled.PeopleAlt">Register</MudNavLink>
<MudNavLink Href = "counter" Match = "NavLinkMatch.Prefix" Icon = "@Icons.Material.Filled.List">Counter</MudNavLink>
<MudNavLink Href = "weather" Match = "NavLinkMatch.Prefix" Icon = "@Icons.Material.Filled.List">Weather</MudNavLink>
<MudNavLink Href = "Account/Manage" Match = "NavLinkMatch.Prefix" Icon = "@Icons.Material.Filled.List">@context.User.Identity?.Name</MudNavLink>
<div class = "nav-item px-3">
<form action = "Account/Logout" method = "post">
<AntiforgeryToken />
<input type = "hidden" name = "ReturnUrl" value = "" />
<button type = "submit" class = "nav-link">
<span class = "bi bi-arrow-bar-left-nav-menu" aria-hidden = "true"></span> Logout
</button>
</form>
</div>
</Authorized>
<NotAuthorized>
@* <MudNavLink Href = "Account/Register" Match = "NavLinkMatch.Prefix" Icon = "@Icons.Material.Filled.List">Register</MudNavLink> *@
<MudNavLink Href = "Account/Login" Match = "NavLinkMatch.Prefix" Icon = "@Icons.Material.Filled.List">Login</MudNavLink>
</NotAuthorized>
</AuthorizeView>
</MudNavMenu>
@code {
private string? currentUrl;
protected override void OnInitialized()
{
currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
NavigationManager.LocationChanged += OnLocationChanged;
}
private void OnLocationChanged(object? sender, LocationChangedEventArgs e)
{
currentUrl = NavigationManager.ToBaseRelativePath(e.Location);
StateHasChanged();
}
public void Dispose()
{
NavigationManager.LocationChanged -= OnLocationChanged;
}
}
Вот моя страница счетчика (здесь работает навигационное меню):
@page "/counter"
@using Microsoft.AspNetCore.Authorization
@attribute [Authorize]
<PageTitle>Counter</PageTitle>
<MudText Typo = "Typo.h3" GutterBottom = "true">Counter</MudText>
<MudText Class = "mb-4">Current count: @currentCount</MudText>
<MudButton Color = "Color.Primary" Variant = "Variant.Filled" @onclick = "IncrementCount">Click me</MudButton>
@code {
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Вот моя страница регистрации (навигационное меню не работает):
@page "/Account/Register"
@using System.ComponentModel.DataAnnotations
@using System.Text
@using System.Text.Encodings.Web
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.WebUtilities
@using MUDTEMPLATE.Data
@inject UserManager<ApplicationUser> UserManager
@inject IUserStore<ApplicationUser> UserStore
@inject SignInManager<ApplicationUser> SignInManager
@inject IEmailSender<ApplicationUser> EmailSender
@inject ILogger<Register> Logger
@inject NavigationManager NavigationManager
@inject IdentityRedirectManager RedirectManager
<PageTitle>Register</PageTitle>
<h1>Register</h1>
<div class = "row">
<div class = "col-md-4">
<StatusMessage Message = "@Message" />
<EditForm Model = "Input" asp-route-returnUrl = "@ReturnUrl" method = "post" OnValidSubmit = "RegisterUser" FormName = "register">
<DataAnnotationsValidator />
<h2>Create a new account.</h2>
<hr />
<ValidationSummary class = "text-danger" role = "alert" />
<div class = "form-floating mb-3">
<InputText @bind-Value = "Input.Email" class = "form-control" autocomplete = "username" aria-required = "true" placeholder = "[email protected]" />
<label for = "email">Email</label>
<ValidationMessage For = "() => Input.Email" class = "text-danger" />
</div>
<div class = "form-floating mb-3">
<InputText type = "password" @bind-Value = "Input.Password" class = "form-control" autocomplete = "new-password" aria-required = "true" placeholder = "password" />
<label for = "password">Password</label>
<ValidationMessage For = "() => Input.Password" class = "text-danger" />
</div>
<div class = "form-floating mb-3">
<InputText type = "password" @bind-Value = "Input.ConfirmPassword" class = "form-control" autocomplete = "new-password" aria-required = "true" placeholder = "password" />
<label for = "confirm-password">Confirm Password</label>
<ValidationMessage For = "() => Input.ConfirmPassword" class = "text-danger" />
</div>
<button type = "submit" class = "w-100 btn btn-lg btn-primary">Register</button>
</EditForm>
</div>
<div class = "col-md-6 col-md-offset-2">
<section>
<h3>Use another service to register.</h3>
<hr />
<ExternalLoginPicker />
</section>
</div>
</div>
@code {
private IEnumerable<IdentityError>? identityErrors;
[SupplyParameterFromForm]
private InputModel Input { get; set; } = new();
[SupplyParameterFromQuery]
private string? ReturnUrl { get; set; }
private string? Message => identityErrors is null ? null : $"Error: {string.Join(", ", identityErrors.Select(error => error.Description))}";
public async Task RegisterUser(EditContext editContext)
{
var user = CreateUser();
await UserStore.SetUserNameAsync(user, Input.Email, CancellationToken.None);
var emailStore = GetEmailStore();
await emailStore.SetEmailAsync(user, Input.Email, CancellationToken.None);
var result = await UserManager.CreateAsync(user, Input.Password);
if (!result.Succeeded)
{
identityErrors = result.Errors;
return;
}
Logger.LogInformation("User created a new account with password.");
var userId = await UserManager.GetUserIdAsync(user);
var code = await UserManager.GenerateEmailConfirmationTokenAsync(user);
code = WebEncoders.Base64UrlEncode(Encoding.UTF8.GetBytes(code));
var callbackUrl = NavigationManager.GetUriWithQueryParameters(
NavigationManager.ToAbsoluteUri("Account/ConfirmEmail").AbsoluteUri,
new Dictionary<string, object?> { ["userId"] = userId, ["code"] = code, ["returnUrl"] = ReturnUrl });
await EmailSender.SendConfirmationLinkAsync(user, Input.Email, HtmlEncoder.Default.Encode(callbackUrl));
if (UserManager.Options.SignIn.RequireConfirmedAccount)
{
RedirectManager.RedirectTo(
"Account/RegisterConfirmation",
new() { ["email"] = Input.Email, ["returnUrl"] = ReturnUrl });
}
await SignInManager.SignInAsync(user, isPersistent: false);
RedirectManager.RedirectTo(ReturnUrl);
}
private ApplicationUser CreateUser()
{
try
{
return Activator.CreateInstance<ApplicationUser>();
}
catch
{
throw new InvalidOperationException($"Can't create an instance of '{nameof(ApplicationUser)}'. " +
$"Ensure that '{nameof(ApplicationUser)}' is not an abstract class and has a parameterless constructor.");
}
}
private IUserEmailStore<ApplicationUser> GetEmailStore()
{
if (!UserManager.SupportsUserEmail)
{
throw new NotSupportedException("The default UI requires a user store with email support.");
}
return (IUserEmailStore<ApplicationUser>)UserStore;
}
private sealed class InputModel
{
[Required]
[EmailAddress]
[Display(Name = "Email")]
public string Email { get; set; } = "";
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; } = "";
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; } = "";
}
}
Страницы /Account/xxx
— это Static Server Side Rendered
страницы. Важная часть — статическая. После визуализации статической страницы методы C# на этой странице больше не выполняются, и интерактивность больше не доступна.
Это стандартное поведение Blazor, и интерактивные компоненты не будут работать на этих страницах без дополнительного JavaScript.
Как я уже говорил ранее, это стандартное поведение Blazor для страниц учетной записи. Логин также является страницей аккаунта. Формы — единственные компоненты, которые работают на статических страницах. Ничего из этого не связано с MudBlazor, и исправлять здесь нечего, он ведет себя так, как ожидалось.
Я обнаружил, что все компоненты, в которых не работает навигационное меню, находятся в местах, где страницы используют отдельный макет, унаследованный от MainLayout. Мой вопрос: почему он наследует навигационное меню в основном макете, а метод открытия и закрытия — нет?
Потому что страницы отображаются статически. Все страницы аккаунта статичны. Вы можете удалить меню с этих страниц, если пожелаете.
Извините за мой глупый вопрос. Но я вижу это на примере существующего шаблона MudBlazor, который я использую. При входе в систему появится компонент Nav Menu, который при нажатии на него перенесет пользователя на страницу управления информацией об учетной записи. Это нормально, что пользователи попадают туда и застревают там, потому что не могут открыть меню навигации и перейти на другие страницы? Или есть какой-то другой способ решения подобных проблем?
1. Я не использую шаблоны, поэтому не совсем понимаю, о чем вы говорите. 2. Я могу только предположить, что вы говорите о размере экрана, при котором меню автоматически скрывается... разве вы не можете просто нажать кнопку «Назад»? 3. Шаблон представляет собой простой пример, основанный на шаблонах Microsoft... если что-то вам не нравится, просто измените это.
Я только начал изучать Blazor и Mud не так давно, поэтому вопрос кажется немного глупым, но дело в том, что я использовал готовую страницу при создании шаблона Mudblazor, отредактировал ее содержимое, и теперь она стала статической страницей? Можете ли вы показать мне, как это исправить? Потому что даже на моей странице входа есть проблема: я не могу взаимодействовать с навигационным меню, хотя оно может выполнять обычную функцию входа в систему, которую я считаю динамической.