Update (Solution)
Thanks to Mister Magoo's answer I've got it working. The solution is done with events and is also shown in the official sample project FlightFinder.
Make sure you use a singleton:
Example: 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;
}
Initial Question
I'm currently trying and learning blazor. I have a NavMenu Component which has multiple links, one of them being: <a href="login">Login</a>, which should change to <a onclick="@Logout">Logout</a> as soon as the user logs in. The login happens in another component (Login Component) using my own service LoginService.
LoginService has a Token property for the Bearer Token and a property public bool IsLoggedIn => !string.IsNullOrEmpty(Token);. I tried to use a simple binding with an if-else-statement in the razor view. That didn't work, my next try was using StateHasChanged(); in my Login component, as soon as someone logs in. Didn't work either (probably because I want to update NavMenu and not 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;
}
}
}
Unfortunately, NavMenu stays on <a href="login">Login</a>. I was thinking about sending a message to NavMenu from the Login Component. How do I get NavMenu to update its view?