'Blazor Complex Validation between two nested Objects
Let's say we have simple Object that contains two of another type
public class Parent
{
[ValidateComplexType]
public Child Child1 { get; set; }
[ValidateComplexType]
public Child Child2 { get; set; }
}
public class Child : IValidatableObject
{
public String Name { get; set; } = String.Empty
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
return new ValidationResult("Error", new[] { nameof(Name) })
}
}
I managed to do nested validation by using ObjectGraphDataAnnotationsValidator as suggested at
https://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-5.0#nested-models-collection-types-and-complex-types
Now let's say that I don't want Child2 to have the same Name as Child 1, so I need to compare their Name properties and display an error on the Child2 input field.
If I do this by adding IValidatableObject to the Parent and in the Validate method return new ValidationResult("Error", new[] { nameof(Child2.Name) }) this doesn't actually set the field as invalid.
I thought about adding a Func<Child, Boolean> to each child and then set it when I Instantiate the Parent object, that looks like child => child == Child2 && Child2.Name == Child1.Name and it works but it is very confusing in my opinion.
How to do this properly?
Solution 1:[1]
In my humble opinion, you need to use custom validation here to check if Child2 has the same Name as Child1. I did a test in a blazor server application. My model has 2 properties which are Name1 and Name2.
public class ExampleModel
{
[Required]
public UserTest userName1 { get; set; }
[Required]
public UserTest userName2 { get; set; }
}
public class UserTest {
[StringLength(10, ErrorMessage = "Name is too long.")]
public string userName { get; set; }
}
@page "/form-example-1"
@using BlazorAppServer.Model
<h3>FormExample1</h3>
<EditForm Model="@exampleModel" OnValidSubmit="@HandleValidSubmit">
<CustomValidation @ref="customValidation" />
<DataAnnotationsValidator />
<ValidationSummary />
<InputText id="name" @bind-Value="exampleModel.userName1.userName" />
<InputText id="name" @bind-Value="exampleModel.userName2.userName" />
<button type="submit">Submit</button>
</EditForm>
@code {
private ExampleModel exampleModel = new() { userName1 = new UserTest { userName="asdfgh"}, userName2 = new UserTest { userName="hgfdsa"} };
private CustomValidation customValidation;
private void HandleValidSubmit()
{
customValidation.ClearErrors();
var a = exampleModel.userName1.userName;
var b = exampleModel.userName2.userName;
var errors = new Dictionary<string, List<string>>();
if (a == b)
{
errors.Add(nameof(exampleModel.userName2.userName), new() { "name2 can't be the same as name1" });
}
if (errors.Any())
{
customValidation.DisplayErrors(errors);
}
}
}
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.Collections.Generic;
namespace BlazorAppServer
{
public class CustomValidation : ComponentBase
{
private ValidationMessageStore messageStore;
[CascadingParameter]
private EditContext CurrentEditContext { get; set; }
protected override void OnInitialized()
{
if (CurrentEditContext == null)
{
throw new InvalidOperationException(
$"{nameof(CustomValidation)} requires a cascading " +
$"parameter of type {nameof(EditContext)}. " +
$"For example, you can use {nameof(CustomValidation)} " +
$"inside an {nameof(EditForm)}.");
}
messageStore = new(CurrentEditContext);
CurrentEditContext.OnValidationRequested += (s, e) =>
messageStore.Clear();
CurrentEditContext.OnFieldChanged += (s, e) =>
messageStore.Clear(e.FieldIdentifier);
}
public void DisplayErrors(Dictionary<string, List<string>> errors)
{
foreach (var err in errors)
{
messageStore.Add(CurrentEditContext.Field(err.Key), err.Value);
}
CurrentEditContext.NotifyValidationStateChanged();
}
public void ClearErrors()
{
messageStore.Clear();
CurrentEditContext.NotifyValidationStateChanged();
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 |

