代码之家  ›  专栏  ›  技术社区  ›  Damien Flury

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

  •  0
  • Damien Flury  · 技术社区  · 5 年前

    更新(解决方案)

    多亏了马古先生的回答,我才有办法。解决方案是通过事件完成的,并且也显示在正式的示例项目中。 FlightFinder .

    确保使用 singleton :

    示例:startup.cs

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

    登录服务.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 .

    服务函数 有一个 Token 不记名代币的财产和财产 public bool IsLoggedIn => !string.IsNullOrEmpty(Token); . 我试图用一个简单的绑定 if-else -Razor视图中的语句。那不行,我下一次尝试是用 StateHasChanged(); 在我的 登录 组件,只要有人登录。也不起作用(可能是因为我想更新 导航菜单 而不是 登录 ……)

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

    登录服务

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

    不幸的是, 导航菜单 停留在 <a href=“login”>登录</a> . 我想给 导航菜单 登录 组件。我如何得到 导航菜单 更新视图?

    2 回复  |  直到 5 年前
        1
  •  0
  •   Mister Magoo    5 年前

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

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

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

        2
  •  0
  •   enet    5 年前

    您不应该使用表单元素,也不应该提交表单。 幸运的是,据我所知,内部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>
    

    注意,按钮的类型属性应该设置为“button”。现在,只要您点击按钮,就会调用loginasync方法,从中您将登录数据发布到服务器。

    尝试以下操作:

    将这些代码段添加到您的登录服务中:

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

    //您还必须在客户机上设置如下启动类:

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

    //一般来说,这是您在客户机上要做的事情。//在服务器上,你必须有一个方法,比如在帐户控制器中,它的功能是生成JWT令牌,你必须配置JWT中间件,用必要的属性来注释你的控制器,例如:

    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
    

    希望这有帮助…