Search code examples
c#asp.net-coreblazor-webassemblydata-annotations

Blazor Validation Loop


I have a simple model called FooModel that has a single string property called Text.

I am attempting to use DataAnnotationsValidator to validate a collection of Models that are dynamically added to the EditForm

The chained binding and the ValidationSummary and ValidationMessage works outside of a loop. See Example 1

But using a loop, Example 2, the form submitting always validates as true and no errors are displayed in the ValidationSummary nor ValidationMessage.

I guess I could have an EditForm per iteration and that may solve it, but I was trying to get it in a single EditForm.

If I add a CascadingParameter of EditForm to the ListComponent and subscribe to OnValidationRequested I see all the latest values for the Model in question, but it doesn't seem to bubble up to the Page.

Any help or suggestions are appreciated.

Model

public class FooModel
{
    [MinLength(2)]
    public string? Text { get; set; }
}

Page

// Example 1

<EditForm Model=@_fooModel OnValidSubmit=@OnValidSubmit OnInvalidSubmit=@OnInvalidSubmit>
   <DataAnnotationsValidator />
      <ValidationSummary />
      <InputComponent @bind-Value=@_fooModel.Text />
      <button type="submit">Submit</button>
</EditForm>

// Example 2

<EditForm Model=@_fooModels OnValidSubmit=@OnValidSubmit OnInvalidSubmit=@OnInvalidSubmit>
   <DataAnnotationsValidator />
      <ValidationSummary />
      <ListComponent @bind-Values=@_fooModels />
      <button type="submit">Submit</button>
</EditForm>

@code {

   private FooModel _fooModel = new() { Text = "z" };
   private List<FooModel> _fooModels = new() { 
      new() { Text = "a" },
      new() { Text = "b" },
      new() { Text = "c" } 
   };

}

List Component

@{
    foreach (var value in Values)
    {
        <InputComponent @[email protected] />
    }
}

@code {

   [Parameter]
   public required List<FooModel> Values { get; set; }

   [Parameter]
   public EventCallback<List<FooModel>> ValuesChanged { get; set; }

}

InputComponent

<input type="text" value=@value @onchange=@OnInput />
<ValidationMessage For=@ValueExpression />

@code {

    private string? value;
    
    [Parameter]
    public Expression<Func<string>> ValueExpression { get; set; } = default!;
   
    [Parameter]
    public string? Value { get; set; }

    [Parameter]
    public EventCallback<string> ValueChanged { get; set; }
    
    private async Task OnInput(ChangeEventArgs e)
    {
        value = e?.Value?.ToString();
        await ValueChanged.InvokeAsync(value);
    }
}

Solution

  • DataAnnotationsValidator doesn't support complex objects.

    Take a look at Fluent Validation. See - Fluent Validation.

    There are references to several Blazor implementations in the link. If you want to see a simple Blazor implementation there's the one I use here - https://github.com/ShaunCurtis/Blazr.FluentValidation.