Blazor: Validation does not trigger on class instances in the model

119 views Asked by At

I have a blazor EditForm tied to a model.

The model for example is StudentModel which has other class instantiated like lets say Phone.

public class Student
{
  [Required]
  public string Name {get; set;}
  
  public Phone Phone {get; set;}
}

public class Phone
{
  [Required]
  public string Number {get; set;}
}

When validation runs on the form, the Name in the model does get validated, but the Number property on the Phone class does not. Why? How do I get properties validated that are in other classes but are instantiated in the model?

2

There are 2 answers

0
MrC aka Shaun Curtis On

You have a complex object. In this case it's relatively easy to flatten out.

In the code below I've created an edit context from the object and moved the validation to that object. Create an instance of StudentEditContext in the edit form and then submit StudentEditContext.AsStudent back into the data pipeline to persist it.

public class Student
{
    public string? Name { get; set; }
    public Phone Phone { get; set; } = new();
}

public class Phone
{
    public string? Number { get; set; }
}

public class StudentEditContext
{
    [Required] public string? Name { get; set; }

    [Required] public string? Number { get; set; }

    public StudentEditContext(Student student)
    {
        this.Name = student.Name;
        this.Number = student.Phone.Number;
    }

    public Student AsStudent => new()
    {
        Name = this.Name,
        Phone = new() { Number = this.Number },
    };
}

That being said, I'm guessing you've simplified your requirement and your object isn't so simple to deal with.

While it's relatively easy to get such a data object from the data domain [EF makes it simple], dealing with it safely and properly in the core business domain is far more complex. An explanation of how to construct and use aggregate objects is far too complex a subject for an SO answer.

4
Kurt Hamilton On

You have not shown how you have set up your form, but I imagine it is something like this:

<EditForm Model="@Model">
  <DataAnnotationsValidator />

  @*Working*@
  <InputText @bind-Value="@Model.Name" /> 

  @*Not Working*@
  <InputText @bind-Value="@Model.Phone.Number" />
</EditForm>

@code {
  private Student Model {get; set;} = new();
}

If you want to validated nested complex properties, instead of using

<DataAnnotationsValidator />

you can use

<ObjectGraphDataAnnotationsValidator />

This validator can be used by installing the prerelease version of the Microsoft.AspNetCore.Components.DataAnnotations.Validation NuGet package.

From the docs.

Blazor provides support for validating form input using data annotations with the built-in DataAnnotationsValidator. However, the DataAnnotationsValidator only validates top-level properties of the model bound to the form that aren't collection- or complex-type properties.

To validate the bound model's entire object graph, including collection- and complex-type properties, use the ObjectGraphDataAnnotationsValidator provided by the experimental Microsoft.AspNetCore.Components.DataAnnotations.Validation package

Edit

Thanks to MrC aka Shaun Curtis for making it clear in the comments that the package I have referenced was last updated in 2020 and should probably not be used for new projects that you want to maintain over future releases of .NET.

One thing to bear in mind is that if the approach you want to take doesn't seem to be well-supported, it might be worth re-thinking your approach.