简体   繁体   English

如何从代码更新 Blazor static 布局

[英]How to update Blazor static layout from code

I have a main layout with a sidebar that differs for authorized and non-authorized users.我有一个带有侧边栏的主布局,对于授权和非授权用户来说是不同的。 The place which I want to update looks like that我要更新的地方是这样的

        <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>

After user passes authorization I want him to see his personal information so in my login method I do some stuff like用户通过授权后,我希望他看到他的个人信息,所以在我的登录方法中,我做了一些类似的事情

public async void HandleValidSubmit()
{
    ...
   ((CustomAuthenticationStateProvider)authenticationStateProvider).AuthenticateUser(authorizedUser);
   navigationManager.NavigateTo("/");
   //here I want to update the layout
    ...
    return;
}

and in my CustomAuthenticationStateProvider after setting the current user I do NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user)));并在设置当前用户后在我的 CustomAuthenticationStateProvider 中执行NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(user))); which i hoped would be enough to all components which are authorization-based to update.我希望这足以让所有基于授权的组件进行更新。 But it's not.但事实并非如此。 I tried StateHasChanged() method but understanably it doesn't work like that cause it just updates the component which it's triggered from.我尝试了 StateHasChanged() 方法,但可以理解的是它不能像那样工作,因为它只是更新触发它的组件。 But if u manually reload the page after logging in all will be ok.但是如果你在登录后手动重新加载页面就可以了。 Any ideas how can I update the layout from code?任何想法如何从代码更新布局?

I'm not sure about the layout of your MainLayout, so let us suppose for this answer's shake that the AuthorizeView component is embedded within the NavMenu component, itself embedded in the MainLayout component...我不确定您的 MainLayout 的布局,所以让我们假设 AuthorizeView 组件嵌入在 NavMenu 组件中,该组件本身嵌入在 MainLayout 组件中......

You want to refresh the content of the NavMenu component, which is embedded in the MainLayout component, from the login page, right?您想从登录页面刷新嵌入在 MainLayout 组件中的 NavMenu 组件的内容,对吗?

You can use various methods to achieve this.您可以使用各种方法来实现这一点。 The following solution is based on the App State Pattern.以下解决方案基于 App State 模式。

First off, we have to create a service class that can be accessed from both, the NavMenu component and the Login component.首先,我们必须创建一个可以从 NavMenu 组件和 Login 组件访问的服务 class。 Here's the 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();
}

This class defines an event delegate, named OnChange, which should encapsulate the method that will refresh the NavMenu.这个 class 定义了一个名为 OnChange 的事件委托,它应该封装刷新 NavMenu 的方法。 This delegate is invoked when the boolean property LoggedIn's value changes.当 boolean 属性 LoggedIn 的值更改时,将调用此委托。 The LoggedIn property's value may change in the Login page, when the user has been logged in, thus any subscriber to this delegate, in our case, the NavMenu, will be notified of this.当用户登录时,LoggedIn 属性的值可能会在登录页面中更改,因此此委托的任何订阅者(在我们的示例中为 NavMenu)都将收到此通知。

Login Page登录页面

  • @inject AppState AppState Note the above inject the AppState to the Login Page. @inject AppState AppState请注意上面将 AppState 注入到登录页面。 Put it at the top of the page放在页面顶部

  • AppState.LoggedIn = true; that code should be place at the end of the log in procedure.该代码应放在登录过程的末尾。 This will initiate the triggering of the OnChange delegate.这将启动 OnChange 委托的触发。

NavMenu component导航菜单组件

  • @inject AppState AppState
  • @implements IDisposable

* *

protected override void OnInitialized()
{
    AppState.OnChange += StateHasChanged;
}

public void Dispose()
{
    AppState.OnChange -= StateHasChanged;
}

Now, whenever you log in, the AppState service notifies the NavMenu component to re-render so that the content of the Authorized in the AuthorizeView is rendered.现在,无论何时登录,AppState 服务都会通知 NavMenu 组件重新渲染,以便渲染 AuthorizeView 中 Authorized 的内容。

Startup class启动 class

services.AddSingleton<AppState>();

You don't need DI, if the class that generates the event is static.如果生成事件的 class 是 static,则不需要 DI。 An example.一个例子。 Let's say you want to swap the language from a component, in NavMenu.假设您想在 NavMenu 中交换组件的语言。 Create a class AppStatus in 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:导航菜单.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;
}

Create component SetLanguage.razor:创建组件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();
    }
}

When clicked the button, then NavMenu update the state... I only illustrated the part that solves the question consulted单击按钮时,NavMenu 会更新 state... 我只说明了解决所咨询问题的部分

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM