【问题标题】:How to update Blazor static layout from code如何从代码更新 Blazor 静态布局
【发布时间】:2020-05-13 15:55:20
【问题描述】:

我有一个带有侧边栏的主布局,对于授权用户和非授权用户来说是不同的。我要更新的地方是这样的

        <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() 方法,但可以理解的是它不能像那样工作,因为它只是更新触发它的组件。但是如果你在登录后手动重新加载页面就可以了。任何想法如何从代码更新布局?

【问题讨论】:

  • 查看 Jeremy Likeness 的 this blog post。他讨论了一种创建包装器以基于模型更新调用 StateHasChanged 的​​方法,并且由于作用域服务被视为特定于用户的单例,因此您可以将其与 Jeremy 的 ViewRegion 组件一起使用来绑定事件并触发 StateHasChanged 进一步向上 DOM 树。

标签: c# authorization blazor


【解决方案1】:

我不确定您的 MainLayout 的布局,所以让我们假设 AuthorizeView 组件嵌入在 NavMenu 组件中,它本身嵌入在 MainLayout 组件中......

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

您可以使用各种方法来实现这一点。以下解决方案基于 App State Pattern。

首先,我们必须创建一个可以从 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>();

【讨论】:

  • 非常感谢您的回答。这真的很简单,但遗憾的是它不起作用。我实现了这种方法,并且 StateHasChanged 方法被触发,然后你登录但没有任何变化。您确定 StateHasChanged 应该重新渲染布局吗?
  • 它应该可以工作。我已经测试过了。请尝试以下操作:在您的登录方法中,只需设置 AppState.LoggedIn = true;并查看 NavMenu 组件是否刷新。不要在代码中执行登录。
  • 好吧,你是对的。我检查了它是否真的在重新渲染,并且确实如此。它只是出于某种原因呈现 中的代码块。但在手动刷新后,它会在 中呈现代码。无论如何,你回答了这个问题,所以谢谢你,但如果你有任何想法为什么会发生这种情况,我会加倍感激。
  • "出于某种原因,它只是渲染了 中的代码块。"这意味着 1. 我的代码完成了它创建的工作。 2.您的登录逻辑有问题。在这种情况下,我建议您创建一个新答案,并发布所有相关代码。
  • 延迟可能与此有关:public async void HandleValidSubmit() 使用 Task 而不是 void 并报告我是否正确。
【解决方案2】:

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

【讨论】:

  • 注意。 NavMenu.razor 使用@implements IDisposable。
猜你喜欢
  • 2020-03-12
  • 2021-12-25
  • 2020-11-18
  • 2021-02-04
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 2013-04-17
  • 1970-01-01
相关资源
最近更新 更多