Blazor - 绑定到服务的 属性,由另一个组件更改

Blazor - Bind to a property of a service, changed by another component

更新(解决方案)

感谢 Magoo 先生的回答,我已经开始工作了。该解决方案是通过事件完成的,并且在官方示例项目FlightFinder中也有展示。

确保使用 singleton:

示例:Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<LoginService, ILoginService>();
}

LoginService.cs:

public event Action OnChange;
public async Task<bool> LoginFromLocalStorageAsync()
{
    var response = await _http.PostJsonAsync<TokenResult>("/api/auth", model);
    Token = response.Token;
    ExpireDate = response.ExpireDate;
    _http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Token);
    OnChange?.Invoke(); // Here we invoke the event
}

NavMenu.cshtml:

protected override void OnInit()
{
    LoginService.OnChange += StateHasChanged;
}

初始问题

我目前正在尝试和学习 blazor。我有一个 NavMenu 组件,它有多个链接,其中之一是:<a href="login">Login</a>,用户登录后应该立即更改为 <a onclick="@Logout">Logout</a>。登录发生在另一个组件中(Login 组件) 使用我自己的服务 LoginService.

LoginService 有一个 Token 属性 作为 Bearer Token 和一个 属性 public bool IsLoggedIn => !string.IsNullOrEmpty(Token);。我尝试在 razor 视图中使用带有 if-else 语句的简单绑定。那没有用,我的下一次尝试是在我的 Login 组件中使用 StateHasChanged();,一旦有人登录。也没有用(可能是因为我想更新 NavMenu 和不是 Login...)

NavMenu.cshtml:

@inject ILoginService LoginService 
@if(LoginService.IsLoggedIn) {
    <a href="logout">Logout</a>
}
else {
    <a href="login">Login</a>
}

Login.cshtml:

<form onsubmit="@Submit">
    <input type="email" placeholder="Email Address" bind="@LoginViewModel.Email" />
    <input type="password" placeholder="Password" bind="@LoginViewModel.Password" />
    <button type="submit">Login</button>
</form>

@functions
{
    public LoginViewModel LoginViewModel { get; } = new LoginViewModel();
    public async Task Submit()
    {
        await LoginService.LoginAsync(LoginViewModel);
    }
}

LoginService.cs

public class LoginService : ILoginService
{
    private readonly HttpClient _http;
    public LoginService(HttpClient http) => _http = http;
    public string Token { get; private set; }
    public bool IsLoggedIn => !string.IsNullOrEmpty(Token);
    public async Task<bool> LoginAsync(LoginViewModel model)
    {
        try
        {
            var response = await _http.PostJsonAsync<TokenResult>("/api/auth", model);
            Token = response.Token;
            return true;
        }
        catch (Exception)
        {
            return false;
        }
    }
}

不幸的是,NavMenu 停留在 <a href="login">Login</a>。我正在考虑从 Login 组件向 NavMenu 发送消息。如何让 NavMenu 更新其视图?

您可以向 LoginService 添加一个事件,每当您的 Token 发生变化时都会引发该事件。

然后您的菜单组件可以订阅该事件(您已经注入了 LoginService)并调用 StateHasChanged()。

这将刷新视图并更新客户端。

您需要将 Spinner 组件放置在 MainLayout 中以使其对所有页面可用,这可以通过使用双向绑定 Visible API 轻松实现 属性.我们也准备了样品供大家参考,

代码片段: MainLayout.razor:

@using Syncfusion.Blazor.Spinner 
<CascadingValue Value="@this"> 
    <div class="page"> 
        <div class="sidebar"> 
            <NavMenu /> 
        </div> 
 
        <div class="main"> 
            <div class="top-row px-4"> 
                <a href=http://blazor.net target="_blank" class="ml-md-auto">About</a> 
            </div> 
 
            <div class="content px-4"> 
                @Body 
            </div> 
 
        </div> 
        <SfSpinner @bind-Visible="@SpinnerVisible" CssClass="e-spin-overlay"> 
        </SfSpinner> 
    </div> 
</CascadingValue> 
@code{ 
    public bool SpinnerVisible { get; set; } = false; 
 
    public async Task ClickHandler() 
    { 
        this.SpinnerVisible = true; 
        StateHasChanged(); 
        await Task.Delay(2000); 
        this.SpinnerVisible = false; 
        StateHasChanged(); 
    } 
} 
 

Index.razor

<div> 
    <SfButton @onclick="@ClickHandler">Show Spinner</SfButton> 
</div> 
 
@code{ 
    [CascadingParameter] 
    public MainLayout mainLayoutObj { get; set; } 
    private async Task ClickHandler() 
    { 
        await mainLayoutObj.ClickHandler(); 
    } 
} 

此 ClickHandler 方法可以在页面的任何位置调用,具体取决于使用情况,

注意:在 MainLayout 中使用 CascadingValue 可以在主体的任何位置访问主布局。

样本:https://www.syncfusion.com/downloads/support/directtrac/342190/ze/Web_spinner848284331

请检查上面的代码片段和示例,如果它满足您的要求,请告诉我们。

此致, 维尼萨