6

I created an EditForm wrapping a table like so:

**Index.razor**


@using System.ComponentModel.DataAnnotations;

<EditForm @ref="Form" Model="vms" OnSubmit="Submit">
    <DataAnnotationsValidator></DataAnnotationsValidator>
    <table class="table">
        <thead>
            <tr>
                <th>Code</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var vm in vms)
            {
                <tr>
                    <td>
                        <InputText @bind-Value="vm.Code"></InputText>
                        <ValidationMessage For="@(() => vm.Code)"></ValidationMessage>
                    </td>
                </tr>
            }
        </tbody>
    </table>
    <input type="submit" class="btn btn-primary" value="Submit" />
</EditForm>

@code{
    List<MyClass> vms;
    EditForm Form;
    class MyClass
    {
        [Required(ErrorMessage ="Required")]
        public string Code { get; set; }
    }
    protected override void OnInitialized()
    {
        vms = new List<MyClass>()
    {
            new MyClass()
            {
                Code = "1111"
            },
            new MyClass()
            {
                Code = "2222"
            }
        };
    }
    private void Submit()
    {
        bool IsValid = Form.EditContext.Validate();
    }
}

The message error pops correctly as per the below image:

enter image description here

However, when I submit the form and then validate, it does not seem to pick up the invalid state.

enter image description here

It is still returning true after calling EditContext.Validate(), even though there are errors.

How do I get this to work? (How do I get false when at least one of the model item in the EditForm context is invalid so I can do my other validation stuff?)


[Updated 2021-01-16] Answers can also be found here. https://www.pragimtech.com/blog/blazor/validating-complex-models-in-blazor/

In short, the built-in DataAnnotationValidation does not work with arrays. To get it working, you must

  1. installMicrosoft.AspNetCore.Components.DataAnnotations.Validation
  2. Make the array an attribute and then decorate it with [ValidateComplexType]
  3. Use ObjectGraphDataAnnotationsValidator
2
  • 1
    According to this link: learn.microsoft.com/en-us/aspnet/core/blazor/…, you should create a new EditContext in the OnInitialized() routine for the form (editContext = new EditContext(vms). Maybe this would help you Commented Jan 14, 2021 at 13:50
  • I agree with @JasonD , then when you what to validate you could use : if (editContext .Validate()) {//SomeLogic} Commented Jan 14, 2021 at 15:17

1 Answer 1

6

First off, I'd suggest you do something like this

<EditForm EditContext="editContext" OnSubmit="Submit">

Instead of

<EditForm @ref="Form" Model="vms" OnSubmit="Submit">

Which requires you to define EditContext like this: EditContext editContext;

And instantiate the EditContext object in the OnInitialized method like this:

protected override void OnInitialized()
    {
        vms = new List<MyClass>() { new MyClass() { Code = "1111" },
                                    new MyClass() { Code = "2222" }
                                  };

        editContext = new EditContext(vms);
    }

Incidentally, why do you use OnSubmit instead of OnValidSubmit and OnInvalidSubmit ? Are you looking for challenges ?

EditContext.Validate() returns incorrect results

That is not true...

The issue is that you try to bind to an array of MyClass...But you should bind to a single object. Binding to an array is possible, but I can't extend on it as it merits a new question. Suffice it to say that the array of objects you can bind to must be itself a field (property) of an a single bounded object, as for instance, a Student object that contains a list of Languages he speaks.

In order to verify the above, change List<MyClass> vms;

into MyClass model = new MyClass();

and editContext = new EditContext(vms);

into editContext = new EditContext(model);

and instead of

 @foreach (var vm in vms)
  {
            <tr>
                <td>
                    <InputText @bind-Value="vm.Code"></InputText>
                    <ValidationMessage For="@(() => vm.Code)"> 
       </ValidationMessage>
                </td>
            </tr>
  }

code this:

<tr>
     <td>
        <InputText @bind-Value="model.Code"></InputText>
        <ValidationMessage For="@(() => model.Code)"></ValidationMessage>
     </td>
 </tr>

Now, run your code, and verify whether the defamation of the EditContext.Validate() was justified.

Update

The following code sample describes how to bind to a collection in an EditForm, and how to validate this collection as well as other fields in a model of which this collection is a field member.

Note: You should execute Install-Package Microsoft.AspNetCore.Components.DataAnnotations.Validation -Version 3.2.0-rc1.20223.4 in your Package ManagerConsole, to access objects necessary to make the sample work...

@page "/"
@using Microsoft.AspNetCore.Components.Forms
@using System.ComponentModel.DataAnnotations;

<EditForm EditContext="EditContext" OnSubmit="@OnSubmitHandler">
    @*<DataAnnotationsValidator />*@
    <ObjectGraphDataAnnotationsValidator/>
    <p>
        <label for="name">Enter name: </label>
        <InputText id="name" @bind-Value="customer.Name" /><br />
        <ValidationMessage For="@(() => customer.Name)" />
    </p>
    @foreach (var phone in customer.phones)
    {
<p>
    <label>Enter phone: </label>
    <InputText @bind-Value="phone.PhoneNumber" />
    <ValidationMessage For="@(() => phone.PhoneNumber)" />
</p>
    }

    <p>
        <button type="submit">submit</button>
    </p>

</EditForm>

<div>
    <p>Edit  customer</p>

    <p>@customer.Name</p>
    @foreach (var phone in customer.phones)
    {
        <p>@phone.PhoneNumber</p>

    }

</div>
<div>
     <p>Is model validated: @validated.ToString()</p>
 </div>
@code {
    EditContext EditContext;
    Customer customer;
    bool validated;

    protected override void OnInitialized()
    {
        customer = new Customer();
        EditContext = new EditContext(customer);

    }
    private void OnSubmitHandler()
    {            
        validated = EditContext.Validate();
    }

    public class Customer
    {
        [Required]
        public string Name { get; set; }  
        [ValidateComplexType]
        public List<Phone> phones { get; set; } = new List<Phone>() { new Phone { }, new Phone { }, new Phone { }};
         
    }

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

}
Sign up to request clarification or add additional context in comments.

3 Comments

Thanks for the response. However the struggle I am having is that I want to achieve the following two things at the same time: 1. Bind to a list 2. Make EditContext.Validate() work. I tried your suggest of creating a wrapper class and then include the list of MyClass into the wrapper as a property, but data annotation stops working after I do that. Perhaps I should word my questions better, like "How to get EditContext.Validate() to work while binding EditForm to an array".
See Update section in my answer. Would you mind marking it as the answer if it solved your problem, so others know it was useful.
"Incidentally, why do you use OnSubmit instead of OnValidSubmit and OnInvalidSubmit? Are you looking for challenges?" This is now my favorite comment of my career... I'm enjoying the answer too well.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.