I am working with a Blazor web assembly application and facing an issue. I want to update the username and profile image on the top menu when a user logs in.
So, the approach I used is, when a user logs in, store username and profile image URL in the local storage. And fetch that from the header component.
Header.razor
<span>@_username</span>
<img src="@_profileImageUrl" alt="">
@code{
string? _username;
string? _profileImageUrl;
public async Task GetUserInfo()
{
_username = await LocalStorage.GetItemAsync<string>("username");
_profileImageUrl = await LocalStorage.GetItemAsync<string>("profileImageUrl");
StateHasChanged();
}
}
Login.razor
<Header @ref="_header"></Header>
@code{
private Header _header { get; set; }
private async Task HandleLogin()
{
var loginResponse = await AuthenticationService.Login(_model);
if (loginResponse.Success)
{
await _header.GetUserInfo();
var returnUrl = NavigationManager.QueryString("returnUrl") ?? "/myaccount";
NavigationManager.NavigateTo(returnUrl);
}
else
{
NotificationService.Notify(Notification.Error(loginResponse.Message));
}
}
}
App structure
-Controls
-Header.razor
-Pages
-Login.razor
Local storage is getting updated on login success. But the header values aren't updated.
I have tried to cascade the values from the main layout page; it works partially. When I hard refresh the page, both the values become null.
Edit-1:
public class AuthenticationService : IAuthenticationService
{
public async Task<Response<AuthenticationResponse>> Login(LoginViewModel loginViewModel)
{
var loginResponse = await _httpService.Post<AuthenticationResponse>("api/auth/login", loginViewModel);
if (loginResponse.Success)
{
await _localStorage.SetItemAsync("authToken", loginResponse.Data.AccessToken);
await _localStorage.SetItemAsync("refreshToken", loginResponse.Data.RefreshToken);
await _localStorage.SetItemAsync("username", loginResponse.Data.FirstName);
await _localStorage.SetItemAsync("profileImageUrl", loginResponse.Data.ProfileImageUrl);
((AuthStateProvider)_authStateProvider).NotifyUserAuthentication(loginResponse.Data.AccessToken);
}
return loginResponse;
}
}
Edit-2
MainLayout.razor
@inherits LayoutComponentBase
<RadzenNotification />
<div class="page">
<main>
<Header></Header>
<div>
@Body
</div>
<Footer></Footer>
</main>
</div>
The simplest way to implement this kind of feature in a header would be to put the header in a layout component.
Additionally, create a small service like so:
public class UserStatusService
{
public event EventHandler UserLoggedIn;
public void UserIsLoggedIn()
{
UserLoggedIn?.Invoke(this, null);
}
}
Register this in your services, and inject it into Login.razor.
Then, from login.razor, after successful login call:
_userStatusService.UserIsLoggedIn();
In header component:
@inject UserStatusService _userStatusService
@implements IDisposable
@code {
string? _username;
string? _profileImageUrl;
protected override OnInitialized()
{
_userStatusService.UserLoggedIn += HandleLoggedIn;
}
public void Dispose()
{
_userStatusService.UserLoggedIn -= HandleLoggedIn;
}
void HandleLoggedIn(object sender, EventArgs e)
{
_username = await LocalStorage.GetItemAsync<string>("username");
_profileImageUrl = await LocalStorage.GetItemAsync<string>("profileImageUrl");
StateHasChanged();
}
}