3

Can we add a custom validation message to an EditForm in Blazor? My form is like below and on submission of form i have to perform some business logic checks to see the provided value for a parameter is OK or not and if its not OK i have to show a custom dynamic validation message

 <EditForm Model="@starship" OnValidSubmit="@HandleValidSubmit">
        <DataAnnotationsValidator />
        <ValidationSummary /> 
        <p>
            <label>
                Identifier:
                <InputText @bind-Value="starship.Identifier" />
            </label>
        </p>
        <p>
            <label>
                Description (optional):
                <InputTextArea @bind-Value="starship.Description" />
            </label>
        </p>    
        <p>
            <label>
                Maximum Accommodation:
                <InputNumber @bind-Value="starship.MaximumAccommodation" />
            </label>
        </p>
        <p>
            <label>
                Engineering Approval:
                <InputCheckbox @bind-Value="starship.IsValidatedDesign" />
            </label>
        </p>
        <p>
            <label>
                Production Date:
                <InputDate @bind-Value="starship.ProductionDate" />
            </label>
        </p>
    
        <button type="submit">Submit</button>
    
       
    </EditForm>
    
    @code {
        private Starship starship = new() { ProductionDate = DateTime.UtcNow };
    
        private void HandleValidSubmit()
        {
            Logger.LogInformation("HandleValidSubmit called");
            //I have to check for some custom validation spefic to this form and end result is 
            //i might have to show a validation message against the property    ProductionDate
            //      Please choose a date before 01/01/2021 or something similar
            
            Does Blazor does have anything like 
            ModelState.AddModelError("Key","Error message"); 
            
            
        }
    }

Do we have something similar to ModelState.AddModelError in Blazor server side

3
  • 1
    DataAnnotationsValidator is fairly basic. You probably need to write your own validator or use one of the custom ones you can find on the Internet. They are not too difficult once you understand the concepts I've written one myself - shauncurtis.github.io/articles/…. Commented Oct 30, 2021 at 21:47
  • This does not solve the problem, because usually your server rejected the submit and you need to display it. Commented Feb 22, 2022 at 16:34
  • You might be able to use a custom validation attribute as shown here Commented Jul 15, 2022 at 13:30

3 Answers 3

4

You can add current EditContext as parameter to HandleValidSubmit(). Create a new ValidationMessageStore where you can put your custom validation error. Then tell the EditContext that ValidationState has changed.

This is now for OnValidSubmit so your form cannot be submitted before the possible data annotation validations are OK. That means this is performed after those are all good (if any). If needed to check other things at the same time, you can check other answers how more complex things can be done.

@code {
    private Starship starship = new() { ProductionDate = DateTime.UtcNow };

    private void HandleValidSubmit(EditContext editContext)
    {
        Logger.LogInformation("HandleValidSubmit called");
        // Check that date is before 01/01/2021
        DateTime compareDate = new DateTime(2021, 1, 1);            
        if (starship.ProductionDate >= compareDate)
        {
            ValidationMessageStore messageStore = new ValidationMessageStore(editContext);
            messageStore.Add(() => starship.ProductionDate, "Please choose a date before 01/01/2021");
            editContext.NotifyValidationStateChanged();
            // finally return, because we don't want to submit this form yet until given date is fixed
            return;
        }
        // Save changes etc. and navigate to wanted page...
    }
}

Validation error will then show in the list. If you want, you can add that validation error to appear also under the right input area by adding ValidationMessage for starship.ProductionDate:

<ValidationMessage For="() => starship.ProductionDate" class="text-danger" />

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

1 Comment

I really like this answer. It gives us the ability to use the built-in validators for simple things while adding our own custom validators for more complicated scenarios. Most of the examples out there are either/or not both.
3

You can add a custom validation handler as shown in the example in this MS doc. First, don't pass your model to the EditForm but an EditContext which gives you access to some life cycle methods. Here is the relevant code:

OnParametersSetAsync:

// Create EditContext
editContext = new EditContext(assignment);

// Create additional message store for the custom validation messages
ValidationMessageStore validationMessageStore = new(editContext);

// Add additional validation handler
editContext.OnValidationRequested += OnValidationRequestedAsync;

Here is the code for the custom validation handler:

private async void OnValidationRequestedAsync(object sender, ValidationRequestedEventArgs e)
{
    // clear previous error messages
    validationMessageStore.Clear();

    // check DB if Title already exists
    bool exists = await myService.IsPresent(myModel.Title);

    // While waiting for this async process, OnValidSubmit gets called
        
    if (exists)
    {
        // yes, so add a validation message
        validationMessageStore.Add(() => myModel.Title, "The Title is already used.");

        // inform ValidationSummary that a new error message has been added
        editContext.NotifyValidationStateChanged();
    }
}

As descibed in question #72827137, it seems necessary to perform the check also in OnValidSubmit:

// check DB if Title already exists
bool exists = await myService.IsPresent(myModel.Title);

if (exists)
{
    // do nothing if invalid (error message is displayed by OnValidationRequestedAsync)
    return;
}

// ...

The example in the above link also un-hooks the event handler in Dispose, not sure if this is really necessary.

Comments

0

I have 2 IFormFile that are optional in edit but are required in add. so I let them optinal in ViewModel but check them in Add() method. Also for submit I used OnSubmit instead of OnValidSubmit of EditForm. Then use validationMessageStore.Add() for adding model error. I have .net 8 blazor SSR.

<EditForm Model="@vm" OnSubmit="Submit" FormName="CommodityAddEdit"
          method="post" enctype="multipart/form-data" Enhance>

public class AddCommodityVm
{
    [Required, Length(2, 50, ErrorMessage = Constants.LengthMsg)]
    public string Title { get; set; } = "";

    [Display(Name = "Industry")]
    [Required(ErrorMessage = Constants.RequiredMsg)]
    public string? IndustryId { get; set; }

    [Required]
    public string Description { get; set; } = "";

    public IFormFile? PdfFile { get; set; }

    public IFormFile? ImageFile { get; set; }

    public bool IsMain { get; set; }
}

public async Task Submit(EditContext editContext)
{
    if (Id == null)
        await Add(editContext);
    else
        await Update(editContext);
}

public async Task Add(EditContext editContext)
{
    var validationMessageStore = new ValidationMessageStore(editContext);
    var pdfFileIdentifier = new FieldIdentifier(vm, "PdfFile");
    var imageFileIdentifier = new FieldIdentifier(vm, "ImageFile");

    if (editContext.Validate() == false)
    {
        if (vm.PdfFile is null)
            validationMessageStore.Add(pdfFileIdentifier, "The Pdf File field is required.");
        if (vm.ImageFile is null)
            validationMessageStore.Add(imageFileIdentifier, "The Image File field is required.");
        return;
    }
    if (vm.PdfFile is null)
    {
        validationMessageStore.Add(pdfFileIdentifier, "The Pdf File field is required.");
        if (vm.ImageFile is null)
            validationMessageStore.Add(imageFileIdentifier, "The Image File field is required.");
        return;
    }
    if (vm.ImageFile is null)
    {
        validationMessageStore.Add(imageFileIdentifier, "The Image File field is required.");
        if (vm.PdfFile is null)
            validationMessageStore.Add(pdfFileIdentifier, "The Pdf File field is required.");
        return;
    }

    var commodityWithSameTitle = await appDbContext.Commodities.AsNoTracking()
     .FirstOrDefaultAsync(i => i.Title.ToLower() == vm.Title.ToLower());
    if (commodityWithSameTitle is not null)
    {
        ShowAlert = true;
        AlertText = "Commodity with this title is registered before.";
        return;
    }
    if ((vm.ImageFile!.Length / 1024) > 800) // KB
    {
        ShowAlert_ImageSize = true;
        AlertText_ImageSize = "Size of image must be less than 800 KB.";
        return;
    }
    var newCommodity = vm.Adapt<Commodity>();
    newCommodity.PdfName = vm.PdfFile!.FileName;
    newCommodity.ImageName = vm.ImageFile!.FileName;
    newCommodity.AddUserId = HttpContextAccessor.GetUserId();
    appDbContext.Commodities.Add(newCommodity);
    if (newCommodity.IsMain)
    {
        var currentCommodities = await appDbContext.Commodities.ToListAsync();
        foreach (var commodity in currentCommodities)
        {
            commodity.IsMain = false;
        }
    }
    var result = await appDbContext.SaveChangesAsync();
    if (result > 0)
    {
        await WriteFileAsync(newCommodity.Id, vm.PdfFile);
        await WriteFileAsync(newCommodity.Id, vm.ImageFile);
        NavigationManager.NavigateTo("Commodities");
    }
    else
    {
        ShowAlert_Submit = true;
        AlertText_Submit = Constants.Code500Msg;
    }
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.