如何从代码更新 Blazor 静态布局
How to update Blazor static layout from code
我有一个主布局,它的边栏对于授权用户和非授权用户是不同的。我要更新的地方是这样的
<AuthorizeView>
<Authorized>
// Personal information matches in this component (it's just one more div this some code in it)
<UserInfo />
</Authorized>
<NotAuthorized>
<div class="sidebar-unathorized">
<span>
To get all privileges, <a href="/register"><strong>register</strong></a> or <a href="/login"><strong>login</strong></a> please
</span>
</div>
</NotAuthorized>
</AuthorizeView>
用户通过授权后,我想让他看到他的个人信息,所以在我的登录方法中我做了一些事情,比如
public async void HandleValidSubmit()
{
...
((CustomAuthenticationStateProvider)authenticationStateProvider).AuthenticateUser(authorizedUser);
navigationManager.NavigateTo("/");
//here I want to update the layout
...
return;
}
在我的 CustomAuthenticationStateProvider 中设置当前用户后
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
我希望这足以让所有基于授权的组件进行更新。
但事实并非如此。我尝试了 StateHasChanged() 方法,但显然它不能像那样工作,因为它只是更新触发它的组件。但是,如果您在登录后手动重新加载页面,一切都会好的。有什么想法可以从代码更新布局吗?
我不确定您的 MainLayout 的布局,所以让我们假设 AuthorizeView 组件嵌入在 NavMenu 组件中,而 NavMenu 组件本身嵌入 MainLayout 组件中...
您想从登录页面刷新嵌入在 MainLayout 组件中的 NavMenu 组件的内容,对吗?
您可以使用多种方法来实现这一点。以下解决方案基于应用程序状态模式。
首先,我们必须创建一个可以从 NavMenu 组件和 Login 组件访问的服务 class。这是 class:
public class AppState
{
private bool _loggedIn;
public event Action OnChange;
public bool LoggedIn
{
get { return _loggedIn; }
set {
if (_loggedIn != value)
{
_loggedIn = value;
NotifyStateChanged();
}
}
}
private void NotifyStateChanged() => OnChange?.Invoke();
}
这个 class 定义了一个名为 OnChange 的事件委托,它应该封装将刷新 NavMenu 的方法。当布尔值 属性 LoggedIn 的值更改时调用此委托。 LoggedIn 属性 的值可能会在登录页面中更改,当用户已登录时,因此该委托的任何订阅者(在我们的例子中为 NavMenu)都将收到通知。
登录页面
@inject AppState AppState
注意上面将 AppState 注入登录页面。放在页面顶部
AppState.LoggedIn = true;
该代码应放在登录过程的末尾。这将启动 OnChange 委托的触发。
NavMenu 组件
@inject AppState AppState
@implements IDisposable
*
protected override void OnInitialized()
{
AppState.OnChange += StateHasChanged;
}
public void Dispose()
{
AppState.OnChange -= StateHasChanged;
}
现在,每当您登录时,AppState 服务都会通知 NavMenu 组件重新呈现,以便呈现 AuthorizeView 中授权的内容。
启动class
services.AddSingleton<AppState>();
如果生成事件的 class 是静态的,则不需要 DI。一个例子。假设您想在 NavMenu 中从一个组件交换语言。在 root
中创建一个 class AppStatus
public static class AppStatus
{
public static event Action OnChange;
// sample
public static string Culture { get; set; } = "en-US";
public static void UpdateLanguage() => NotifyStateChanged();
static void NotifyStateChanged() => OnChange?.Invoke();
}
NavMenu.razor:
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">Blazor Multilanguage @AppStatus.Culture</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
// ...
// add item
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<li class="nav-item px-3">
<NavLink class="nav-link" href="set-lang">
<span class="oi oi-list-rich" aria-hidden="true"></span> Set Language
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu;
// subscribe event
protected override void OnInitialized() => AppStatus.OnChange += StateHasChanged;
public void Dispose() => AppStatus.OnChange -= StateHasChanged;
}
创建组件SetLanguage.razor:
@page "/set-lang"
<h3>SetLanguage</h3>
<button class="btn btn-primary" @onclick="ChangeLanguage">Toggle Language</button>
@code {
void ChangeLanguage()
{
AppStatus.Culture = AppStatus.Culture == "en-US" ? "ru-RU": "en-US";
AppStatus.UpdateLanguage();
}
}
当点击按钮时,NavMenu更新状态...我只说明了解决问题的部分
我有一个主布局,它的边栏对于授权用户和非授权用户是不同的。我要更新的地方是这样的
<AuthorizeView>
<Authorized>
// Personal information matches in this component (it's just one more div this some code in it)
<UserInfo />
</Authorized>
<NotAuthorized>
<div class="sidebar-unathorized">
<span>
To get all privileges, <a href="/register"><strong>register</strong></a> or <a href="/login"><strong>login</strong></a> please
</span>
</div>
</NotAuthorized>
</AuthorizeView>
用户通过授权后,我想让他看到他的个人信息,所以在我的登录方法中我做了一些事情,比如
public async void HandleValidSubmit()
{
...
((CustomAuthenticationStateProvider)authenticationStateProvider).AuthenticateUser(authorizedUser);
navigationManager.NavigateTo("/");
//here I want to update the layout
...
return;
}
在我的 CustomAuthenticationStateProvider 中设置当前用户后
NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));
我希望这足以让所有基于授权的组件进行更新。
但事实并非如此。我尝试了 StateHasChanged() 方法,但显然它不能像那样工作,因为它只是更新触发它的组件。但是,如果您在登录后手动重新加载页面,一切都会好的。有什么想法可以从代码更新布局吗?
我不确定您的 MainLayout 的布局,所以让我们假设 AuthorizeView 组件嵌入在 NavMenu 组件中,而 NavMenu 组件本身嵌入 MainLayout 组件中...
您想从登录页面刷新嵌入在 MainLayout 组件中的 NavMenu 组件的内容,对吗?
您可以使用多种方法来实现这一点。以下解决方案基于应用程序状态模式。
首先,我们必须创建一个可以从 NavMenu 组件和 Login 组件访问的服务 class。这是 class:
public class AppState
{
private bool _loggedIn;
public event Action OnChange;
public bool LoggedIn
{
get { return _loggedIn; }
set {
if (_loggedIn != value)
{
_loggedIn = value;
NotifyStateChanged();
}
}
}
private void NotifyStateChanged() => OnChange?.Invoke();
}
这个 class 定义了一个名为 OnChange 的事件委托,它应该封装将刷新 NavMenu 的方法。当布尔值 属性 LoggedIn 的值更改时调用此委托。 LoggedIn 属性 的值可能会在登录页面中更改,当用户已登录时,因此该委托的任何订阅者(在我们的例子中为 NavMenu)都将收到通知。
登录页面
@inject AppState AppState
注意上面将 AppState 注入登录页面。放在页面顶部AppState.LoggedIn = true;
该代码应放在登录过程的末尾。这将启动 OnChange 委托的触发。
NavMenu 组件
@inject AppState AppState
@implements IDisposable
*
protected override void OnInitialized()
{
AppState.OnChange += StateHasChanged;
}
public void Dispose()
{
AppState.OnChange -= StateHasChanged;
}
现在,每当您登录时,AppState 服务都会通知 NavMenu 组件重新呈现,以便呈现 AuthorizeView 中授权的内容。
启动class
services.AddSingleton<AppState>();
如果生成事件的 class 是静态的,则不需要 DI。一个例子。假设您想在 NavMenu 中从一个组件交换语言。在 root
中创建一个 class AppStatuspublic static class AppStatus
{
public static event Action OnChange;
// sample
public static string Culture { get; set; } = "en-US";
public static void UpdateLanguage() => NotifyStateChanged();
static void NotifyStateChanged() => OnChange?.Invoke();
}
NavMenu.razor:
<div class="top-row pl-4 navbar navbar-dark">
<a class="navbar-brand" href="">Blazor Multilanguage @AppStatus.Culture</a>
<button class="navbar-toggler" @onclick="ToggleNavMenu">
<span class="navbar-toggler-icon"></span>
</button>
</div>
// ...
// add item
<div class="@NavMenuCssClass" @onclick="ToggleNavMenu">
<li class="nav-item px-3">
<NavLink class="nav-link" href="set-lang">
<span class="oi oi-list-rich" aria-hidden="true"></span> Set Language
</NavLink>
</li>
</ul>
</div>
@code {
private bool collapseNavMenu = true;
private string NavMenuCssClass => collapseNavMenu ? "collapse" : null;
private void ToggleNavMenu() => collapseNavMenu = !collapseNavMenu;
// subscribe event
protected override void OnInitialized() => AppStatus.OnChange += StateHasChanged;
public void Dispose() => AppStatus.OnChange -= StateHasChanged;
}
创建组件SetLanguage.razor:
@page "/set-lang"
<h3>SetLanguage</h3>
<button class="btn btn-primary" @onclick="ChangeLanguage">Toggle Language</button>
@code {
void ChangeLanguage()
{
AppStatus.Culture = AppStatus.Culture == "en-US" ? "ru-RU": "en-US";
AppStatus.UpdateLanguage();
}
}
当点击按钮时,NavMenu更新状态...我只说明了解决问题的部分