I'm trying to create a register account page in Blazor Web App. I need the form submission callback to run on the client as it needs to set the authentication cookie, but I can't manage to achieve this.
This is my current component code:
@using ClassLibrary.Models.Auth
@using ClassLibrary.Models.Database
@using ClassLibrary.Localization.Shared.Forms
@using IDFW.Models.HttpClients
@rendermode InteractiveWebAssembly
@page "/auth/register"
@inject IStringLocalizer<Register> localizer
@inject ApiHttpClient api
<HeadContent>
<link rel="stylesheet" href="/css/auth/login.css" />
<link rel="stylesheet" href="/css/components/loader.css" />
</HeadContent>
<div class="content-container">
<EditForm FormName="LoginForm" EditContext="editContext" class="data-form" OnValidSubmit="HandleOnValidSubmit">
<DataAnnotationsValidator />
<h1>@localizer["PageTitle"]</h1>
<fieldset disabled="@isProcessing">
<label>
<p>@FormLocalization.FirstName<span class="danger">*</span></p>
<InputText DisplayName="@FormLocalization.FirstName" @bind-Value="RegisterModel.FirstName" />
<ValidationMessage For="() => RegisterModel.FirstName" class="danger" />
</label>
<label>
<p>@FormLocalization.LastName<span class="danger">*</span></p>
<InputText DisplayName="@FormLocalization.LastName" @bind-Value="RegisterModel.LastName" />
<ValidationMessage For="() => RegisterModel.LastName" class="danger" />
</label>
<label>
<p>Email<span class="danger">*</span></p>
<InputText DisplayName="Email" @bind-Value="RegisterModel.Email" type="email" />
<ValidationMessage For="() => RegisterModel.Email" class="danger" />
</label>
<label>
<p>Password<span class="danger">*</span></p>
<div class="form-password">
<InputText DisplayName="Password" @bind-Value="RegisterModel.Password" type="password" autocomplete="new-password" />
<span class="fluent-icon icon-ic_fluent_eye_16_filled password-toggle"></span>
</div>
<ValidationMessage For="() => RegisterModel.Password" class="danger" />
</label>
<label>
<p>@AuthFormLocalization.ConfirmPassword<span class="danger">*</span></p>
<div class="form-password">
<InputText DisplayName="@AuthFormLocalization.ConfirmPassword" @bind-Value="RegisterModel.ConfirmPassword" type="password" autocomplete="new-password" />
<span class="fluent-icon icon-ic_fluent_eye_16_filled password-toggle"></span>
</div>
<ValidationMessage For="() => RegisterModel.ConfirmPassword" class="danger" />
</label>
</fieldset>
<fieldset class="form-checkbox-area" disabled="@isProcessing">
<label>
<InputCheckbox @bind-Value="RegisterModel.KeepLogin" />
@AuthFormLocalization.KeepLogin
</label>
<label>
<InputCheckbox @bind-Value="RegisterModel.AgreeLegal" />
@((MarkupString)FormLocalization.LegalNote)
<ValidationMessage For="() => RegisterModel.AgreeLegal" class="danger" />
</label>
</fieldset>
<button type="submit" class="cta-btn primary-cta-btn" disabled="@isProcessing">
@if (!isProcessing)
{
@localizer["Register"]
}
else
{
<span class="loader"></span>
}
</button>
<p class="form-hint">@((MarkupString)localizer["FormHint"].Value)</p>
</EditForm>
</div>
<script src="/js/form.js"></script>
@code {
private RegisterModel RegisterModel { get; set; } = null!;
private EditContext editContext = null!;
private ValidationMessageStore validationMessageStore = null!;
private bool isProcessing = false;
[Inject]
private NavigationManager navigation { get; set; } = null!;
protected override void OnInitialized()
{
RegisterModel ??= new();
editContext = new(RegisterModel);
validationMessageStore = new(editContext);
editContext.OnFieldChanged += (_, args) =>
{
validationMessageStore.Clear(args.FieldIdentifier);
editContext.NotifyValidationStateChanged();
};
}
private async Task HandleOnValidSubmit()
{
isProcessing = true;
validationMessageStore.Clear();
// Try register account
HttpResponseMessage res = await api.HttpClient.PostAsJsonAsync("auth/register", RegisterModel);
if (res.IsSuccessStatusCode)
{
navigation.NavigateTo("/");
}
else if (res.StatusCode == System.Net.HttpStatusCode.BadRequest)
{
HttpValidationProblemDetails problemDetails = (await res.Content.ReadFromJsonAsync<HttpValidationProblemDetails>())!;
if (problemDetails.Errors != null)
{
foreach (KeyValuePair<string, string[]> message in problemDetails.Errors)
{
FieldIdentifier fieldIdentifier = new(RegisterModel, message.Key);
validationMessageStore.Add(fieldIdentifier, message.Value);
}
editContext.NotifyValidationStateChanged();
}
}
isProcessing = false;
}
}
I want everything in the page to be rendered on the server as well as realtime data validation, except HandleOnValidSubmit() to run on the client. I tried using the InteractiveWebAssembly rendermode, but apparently it's not the right approach or I'm doing something wrong.
auth/registerappears in the network tab which confirms the code is running on the server.HttpClient.BaseAddress.