如何从代码更新Blazor静态布局

时间:2020-05-13 15:55:20

标签: c# authorization blazor

我有一个带有侧边栏的主布局,该侧边栏对于授权用户和未授权用户而言都不同。我要更新的地方看起来像这样

        <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()方法,但由于它只是更新了触发它的组件,所以它不能那样工作。但是,如果您在登录后手动重新加载页面,一切正常。有什么想法可以通过代码更新布局吗?

2 个答案:

答案 0 :(得分:1)

我不确定您的MainLayout的布局,因此让我们假设,答案是将AuthorizeView组件嵌入到NavMenu组件中,而NavMenu组件本身也嵌入在MainLayout组件中。

您想从登录页面刷新嵌入在MainLayout组件中的NavMenu组件的内容,对吗?

您可以使用多种方法来实现此目的。以下解决方案基于应用程序状态模式。

首先,我们必须创建一个可以从NavMenu组件和Login组件访问的服务类。这是课程:

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();
}

此类定义了一个名为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中Authorized的内容。

启动类

services.AddSingleton<AppState>();

答案 1 :(得分:0)

如果生成事件的类是静态的,则不需要 DI。一个例子。假设您想从 NavMenu 中的组件交换语言。在 root 中创建一个类 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更新状态...我只说明了解决所咨询问题的部分