Blazor - привязка к свойству службы, измененной другим компонентом - PullRequest
0 голосов
/ 06 января 2019

Обновление (Решение)

Благодаря ответу мистера Магу, у меня все получилось. Решение сделано с событиями и также показано в официальном примере проекта 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>, как только пользователь войдет в систему. Вход в систему происходит в другом компоненте (Login Компонент), используя мой собственный сервис LoginService.

LoginService имеет свойство Token для токена на предъявителя и свойство public bool IsLoggedIn => !string.IsNullOrEmpty(Token);. Я попытался использовать простую привязку с if-else -составлением в бритве Это не сработало, моей следующей попыткой было использование StateHasChanged(); в моем Login компоненте, как только кто-то вошел в систему. Также не сработало (вероятно, потому что я хочу обновить 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>. Я думал об отправке сообщения на NavMenu из Login Компонента. Как мне получить NavMenu чтобы обновить его представление?

Ответы [ 2 ]

0 голосов
/ 06 января 2019

Вы не должны использовать элемент формы и не должны отправлять форму. К счастью, насколько мне известно, внутренний код Blazor должен остановить отправку с помощью protectDefault (); В любом случае, 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>

Обратите внимание, что атрибут типа кнопки должен быть установлен на «кнопку». Теперь, когда вы нажимаете кнопку, вызывается метод 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>
}

// Вы также должны установить класс запуска на клиенте следующим образом:

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

Надеюсь, это поможет ...

0 голосов
/ 06 января 2019

Вы можете добавить событие к LoginService, которое вы будете вызывать при каждом изменении вашего токена.

Тогда ваш компонент меню может подписаться на это событие (у вас уже есть служба LoginService) и вызвать StateHasChanged ().

Это обновит представление и обновит клиента.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...