Reputation: 438
I have an extension method to check if a specific field is valid or not.
public static bool IsValid(this EditContext editContext, string fieldName)
{
var fieldIdentifier = editContext.Field(fieldName);
editContext.NotifyFieldChanged(fieldIdentifier);
var result = !editContext.GetValidationMessages(fieldIdentifier).Any();
Console.WriteLine($"{fieldName} : {result}");
return result;
}
It's working fine for non-complex fields but not for complex fields. e.g
My model is
public class RegisterCompanyRequest
{
[Required(ErrorMessage = "Company Name is required")]
[MaxLength(200, ErrorMessage = "Maximum allowed length for company name is 200 characters")]
public string NameEn { get; set; }
[ValidateComplexType]
public List<CompanyLocation> CompanyLocations { get; set; }
[ValidateComplexType]
public CompanyAdminInfo CompanyAdminInfo { get; set; } = new CompanyAdminInfo();
}
public class CompanyAdminInfo
{
[Required(ErrorMessage = "Full name is required")]
[MaxLength(200, ErrorMessage = "Maximum allowed length for full name is 200 characters")]
public string FullName { get; set; }
}
public class CompanyLocation
{
public int Id { get; set; }
[Required]
[MaxLength(200, ErrorMessage = "Maximum allowed length for location name is 200 characters")]
public string Name { get; set; }
}
and if I pass that method NameEn
it returns false if validation fails and true otherwise, but if i pass it CompanyLocations[0].Name
or CompanyAdminInfo.FullName
it always return true.
How can I achieve the same functionality for nested objects in my Model?
To Reproduce: Copy the below code to new blazor WASM project. also install the following package to validate complex type (Install-Package Microsoft.AspNetCore.Components.DataAnnotations.Validation -Version 3.2.0-rc1.20223.4)
@page "/"
@using System.ComponentModel.DataAnnotations
<p>"Name" : @company.Name <span>@nameValid</span> </p>
<p>"Admin Name" : @company.Admin.Name <span>@adminNameValid</span> </p>
<EditForm EditContext="editContext">
<ObjectGraphDataAnnotationsValidator />
<input type="text" @bind-value="@company.Name"/>
<input type="text" @bind-value="@company.Admin.Name"/>
<button @onclick="Validate" type="submit">Validate</button>
</EditForm>
@code
{
private bool nameValid, adminNameValid;
private Company company = new Company();
EditContext editContext { get; set; }
protected override async Task OnInitializedAsync()
{
editContext = new(company);
}
private async Task Validate()
{
var isValid = IsValid(editContext, "Name");
nameValid = isValid;
isValid = IsValid(editContext, "Admin.Name");
adminNameValid = isValid;
}
public bool IsValid(EditContext editContext, string fieldName)
{
var fieldIdentifier = editContext.Field(fieldName);
editContext.NotifyFieldChanged(fieldIdentifier);
return !editContext.GetValidationMessages(fieldIdentifier).Any();
}
public class Company
{
[Required]
public string Name { get; set; }
[ValidateComplexType] /*(Install-Package Microsoft.AspNetCore.Components.DataAnnotations.Validation -Version 3.2.0-rc1.20223.4)*/
public Admin Admin { get; set; } = new Admin();
}
public class Admin
{
[Required]
public string Name { get; set; }
}
}
Upvotes: 3
Views: 4105
Reputation: 390
In current versions of .NET you will find that <ObjectGraphDataAnnotationsValidator />
is no longer available so now the way to get this to work is to use FluentValidation
.
It took some time to get it working but it does and validates all child objects correctly and only submits if all models are valid: EditForm Validation With List of Model Rather Than Single Model
Upvotes: 0
Reputation: 438
Created an extension method.
public static class EditContextHelper
{
public static bool IsValid(this EditContext editContext, Expression<Func<Object>> field)
{
var fieldIdentifier = FieldIdentifier.Create(field);
editContext.NotifyFieldChanged(fieldIdentifier);
return !editContext.GetValidationMessages(fieldIdentifier).Any();
}
}
using it like,
var field = model.CompanyLocations[0].Name
var result = EditContext.IsValid(field);
in my case i am using array to loop through specif fields like this.
var isValid = false;
var fieldsToValidate = new Expression<Func<Object>>[]
{
() => model.NameEn, () => (model.Address), () => model.Lattitude,
() => model.Longitude , () => (model.Tel1), () => (model.Tel2), () => (model.Mobile), () => (model.Whatsapp),
() => (model.Email), () => (model.Url), () => (model.TaxNumber)
};
foreach (var field in fieldsToValidate)
{
var result = EditContext.IsValid(field);
if (!result)
{
isValid = false;
}
//doing something if any of the fields is wrong.
}
Upvotes: 0
Reputation: 51665
I guess, do you have two antipatterns in your code.
First one is to call, by hand, editContext.NotifyFieldChanged(fieldIdentifier);
. Let Blazor deal with notifications.
The second one is with Submit, in my opinion, you should to avoid calling functions on submit
button. Instead of that, use OnValidSubmit
or OnInvalidSubmit
at EditForm
component level, or bind a function to editContext.OnValidationStateChanged
like in my running sample bellow.
@page "/"
@using System.ComponentModel.DataAnnotations
@implements IDisposable
<EditForm EditContext="editContext" OnValidSubmit="ValidSubmit">
<ObjectGraphDataAnnotationsValidator />
<InputText type="text" @bind-Value="@company.Name" />
<ValidationMessage For="@(() => company.Name)" />
<InputText type="text" @bind-Value="@company.Admin.Name" />
<ValidationMessage For="@(() => company.Admin.Name)" />
<button type="submit">Validate</button>
</EditForm>
<p>"Name" : @company.Name <span>@nameValid</span> </p>
<p>"Admin Name" : @company.Admin.Name <span>@adminNameValid</span> </p>
@code
{
private bool? nameValid, adminNameValid;
private Company company = new Company();
EditContext editContext { get; set; } = default!;
protected override void OnInitialized()
{
editContext = new(company);
editContext.OnValidationStateChanged += HandleValidationStateChanged;
}
private void HandleValidationStateChanged(object? o, ValidationStateChangedEventArgs args)
{
var name_field = FieldIdentifier.Create( () => company.Name );
nameValid = IsValid(editContext, name_field);
var admin_name_field = FieldIdentifier.Create( () => company.Admin.Name );
adminNameValid = IsValid(editContext, admin_name_field);
}
private void ValidSubmit()
{
nameValid = true;
adminNameValid = true;
}
public bool IsValid(EditContext editContext, FieldIdentifier fieldIdentifier)
=>
!editContext.GetValidationMessages(fieldIdentifier).Any();
public class Company
{
[Required]
[StringLength(3, ErrorMessage = "Identifier too long (3 character limit).")]
public string Name { get; set; } = default!;
[ValidateComplexType] /*(Install-Package Microsoft.AspNetCore.Components.DataAnnotations.Validation -Version 3.2.0-rc1.20223.4)*/
public Admin Admin { get; set; } = new Admin();
}
public class Admin
{
[Required]
[StringLength(3, ErrorMessage = "Identifier too long (3 character limit).")]
public string Name { get; set; } = default!;
}
public void Dispose()
{
if (editContext is not null)
{
editContext.OnValidationStateChanged -= HandleValidationStateChanged;
}
}
}
Upvotes: 1