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

时间:2019-01-05 22:37:21

标签: c# razor data-binding state blazor

更新(解决方案)

感谢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;
}

初始问题

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

LoginService具有无记名令牌的LoginService属性和属性Token。我试图在剃刀视图中使用带有public bool IsLoggedIn => !string.IsNullOrEmpty(Token);语句的简单绑定。那没有用,我的下一次尝试是在有人登录后立即在if-else组件中使用StateHasChanged();。也没有用(可能是因为我想更新Login,然后不是NavMenu ...)

NavMenu.cshtml:

Login

Login.cshtml:

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

LoginService.cs

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

很遗憾,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>组件向NavMenu发送消息。如何获得Login来更新其视图?

2 个答案:

答案 0 :(得分:0)

您可以将一个事件添加到LoginService中,每当令牌更改时都会引发该事件。

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

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

答案 1 :(得分:0)

您不应使用表单元素,也不应提交表单。 幸运的是,据我所知,内部Blazor代码应使用preventDefault()停止提交。 无论如何,回发发生后可能会调用LoginAsync。只需考虑一下:一方面,您的代码启动了一个“回发”,另一方面,它向服务器发出了一个http请求。简而言之,您应该使用HttpClient发布表单数据。

表单数据:

<div>
    <input type="email" placeholder="Email Address" bind="@LoginViewModel.Email" />
    <input type="password" placeholder="Password" bind="@LoginViewModel.Password" />
    <button type="button">Login</button>
</div>

请注意,按钮的type属性应设置为“ button”。现在,每当您按下按钮时,都会调用LoginAsync方法,您将从中将登录数据发布到服务器。

尝试以下操作:

将这些代码段添加到您的LoginService中:

       [Inject]
        protected LocalStorage localStorage;
       // Note: LocalStorage is a library for storing data in Web Storage API. You may store your token in the LocalStorage, and retrieve it when you need to verify whether a user is authenticated. 

        // This method may be called from your NavMenu Component
        public bool IsAuthenticated()
        {
            var token = localStorage.GetItem<string>("token");

            return (token != null); 
        }




     public async Task<bool> LoginAsync(LoginViewModel model)
{
    try
    {
        var response = await _http.PostJsonAsync<TokenResult>("/api/auth", model);
        Token = response.Token;

         if (Token)
       {

          // Save the JWT token in the LocalStorage
          // https://github.com/BlazorExtensions/Storage
          await localStorage.SetItem<Object>("token", Token);


          // Returns true to indicate the user has been logged // in and the JWT token is saved on the user browser
         return true;

       }
    }
    catch (Exception)
    {
        return false;
    }
}

最后是NavMenu.cshtml:

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

//您还必须在客户端上设置Startup类,如下所示:

public void ConfigureServices(IServiceCollection services)
    {
        // Add Blazor.Extensions.Storage
       // Both SessionStorage and LocalStorage are registered
       // https://github.com/BlazorExtensions/Storage
       **services.AddStorage();**

      ...
    }

//一般来说,这就是您必须在客户端上执行的操作。 //在服务器上,您必须有一个方法,例如在Account控制器中,该方法的功能是生成JWT令牌,您必须配置JWT中间件,以为控制器添加必要的属性,例如实例:

[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]

希望这对您有帮助...