I'm working on a Blazor application with fluent validation.
I'm working on a manage profile page where they can change their first name, last name, and email.
Here is my Razor:
<EditForm Model="Input" OnValidSubmit="@UpdateProfile">
<FluentValidator TValidator="InputModelValidator" />
<div class="form-row">
<div class="form-group col-md-6">
<h2>Manage Profile</h2>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="First Name" @bind-Value="Input.FirstName"></SfTextBox>
</div>
<div class="form-group col-md-4">
<SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="Last Name" @bind-Value="Input.LastName"></SfTextBox>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<SfTextBox FloatLabelType="FloatLabelType.Auto" Placeholder="Email Address" @bind-Value="Input.Email"></SfTextBox>
</div>
</div>
<div class="form-row btn-update">
<div class="form-group col-md-4">
<SfButton IsPrimary="true">Update</SfButton>
<SfToast ID="toast_customDupemail" @ref="@toastDuplicateEmail" Title="Invalid Email" Content="@toastDupEmailErrorMsg" CssClass="e-toast-danger" Timeout=6000>
<ToastPosition X="Center" Y="Top"></ToastPosition>
</SfToast>
</div>
</div>
</EditForm>
Here is my validator:
public class InputModelValidator : AbstractValidator<InputModel>
{
public InputModelValidator()
{
RuleFor(e => e.FirstName).NotEmpty().WithMessage("First name is required.");
RuleFor(e => e.LastName).NotEmpty().WithMessage("Last name is required.");
RuleFor(e => e.Email).NotEmpty().WithMessage("Email is required.");
RuleFor(e => e.Email).EmailAddress().WithMessage("Email is not valid.");
RuleFor(x => x.Email).Custom((email, context) => {
if (IsEmailValid(email) == false)
{
context.AddFailure("The email is not valid.");
}
});
}
private bool IsEmailValid(string email)
{
var userInfo = Task.Run(async () => await utilities.GetApplicationUser().ConfigureAwait(false)).Result;
if (string.Equals(userInfo.Email, email, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
return false;
}
}
I have the initial checks for empty, and valid email and such. Those work great!
I need to add a custom message to make sure the email is not already in use.
What is the proper way to talk to the database / asp.net identity UserManager
within the validator class?
I'm tried to inject my dependencies, but they are coming in null when I try that.
Thanks.
UPDATE:
Per response that this needs to happen in the handler, is something like this possible?
public partial class ManageProfile
{
public InputModel Input { get; set; } = new InputModel();
private EditContext _editContext;
protected override async Task OnInitializedAsync() // = On Page Load
{
var userInfo = await utilities.GetApplicationUser().ConfigureAwait(false);
Input = new InputModel
{
FirstName = userInfo.FirstName,
LastName = userInfo.LastName,
Email = userInfo.Email
};
await InvokeAsync(StateHasChanged).ConfigureAwait(false);
}
private async Task<EditContext> UpdateProfile()
{
_editContext = new EditContext(Input);
var messages = new ValidationMessageStore(_editContext);
messages.Clear();
if (IsEmailValid(Input.Email) == false)
{
messages.Add(() => Input.Email, "Name should start with a capital.");
_editContext.NotifyValidationStateChanged();
return _editContext;
}
return _editContext;
}
private void ValidateFields(EditContext editContext, ValidationMessageStore messages, FieldIdentifier field)
{
messages.Clear();
if (IsEmailValid(Input.Email) == false)
{
messages.Add(() => Input.Email, "Name should start with a capital.");
editContext.NotifyValidationStateChanged();
}
}
private bool IsEmailValid(string email)
{
var userInfo = Task.Run(async () => await utilities.GetApplicationUser().ConfigureAwait(false)).Result;
if (string.Equals(userInfo.Email, email, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
return false;
}
}
public class InputModel
{
[Required]
[MaxLength(250)]
[Display(Name = "First Name", Prompt = "Enter first name")]
public string FirstName { get; set; }
[Required]
[MaxLength(250)]
[Display(Name = "Last Name", Prompt = "Enter last name")]
public string LastName { get; set; }
/// <summary>
/// This API supports the ASP.NET Core Identity default UI infrastructure and is not intended to be used
/// directly from your code. This API may change or be removed in future releases.
/// </summary>
[Required]
[EmailAddress]
[Display(Name = "Email", Prompt = "Enter email")]
public string Email { get; set; }
}
public class InputModelValidator : AbstractValidator<InputModel>
{
public InputModelValidator()
{
RuleFor(e => e.FirstName).NotEmpty().WithMessage("First name is required.");
RuleFor(e => e.LastName).NotEmpty().WithMessage("Last name is required.");
RuleFor(e => e.Email).NotEmpty().WithMessage("Email is required.");
RuleFor(e => e.Email).EmailAddress().WithMessage("Email is not valid.");
}
}
UPDATE 3:
public class InputModelValidator : AbstractValidator<InputModel>
{
public InputModelValidator(UserManager<ApplicationUser> user)
{
RuleFor(e => e.FirstName).NotEmpty().WithMessage("First name is required.");
RuleFor(e => e.LastName).NotEmpty().WithMessage("Last name is required.");
RuleFor(e => e.Email).NotEmpty().WithMessage("Email is required.");
RuleFor(e => e.Email).EmailAddress().WithMessage("Email is not valid.");
//RuleFor(e => e.Email).EmailAddress().WithMessage("Email is not valid.").Must(IsEmailexist).WithMessage("{PropertyName} Is Already Exist.");
}
private async Task<bool> IsEmailexist(string Email)
{
return false;
}
}
I tried injecting UserManager<>
, but I have this error:
Severity Code Description Project File Line Suppression State Error CS0310 'InputModelValidator' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'TValidator' in the generic type or method 'FluentValidator' C:...\Microsoft.NET.Sdk.Razor.SourceGenerators\Microsoft.NET.Sdk.Razor.SourceGenerators.RazorSourceGenerator\Areas_Identity_Pages_Account_ManageProfile_razor.g.cs 200 Active