In our Blazor WebAssembly series, we’ve already talked about Forms and Form Validation. There we learned a lot about creating and validating forms but there is still more to it. In this article, we are going to extend that topic by introducing a custom validation in Blazor WebAssembly. We are going to learn how to add custom validation attributes and use them for our model objects. Also, we are going to see how to customize our error messages and how to customize the style of the input elements on the form.

To download the source code, you can visit our Custom Validation in Blazor WebAssembly repository.

Let’s get going.

Project Preparation with a Basic Validation

The first thing we are going to do is to create a new Blazor WebAssembly project named CustomValidation:

Support Code Maze on Patreon to get rid of ads and get the best discounts on our products!
Become a patron at Patreon!

Blazor WebAssembly project creation

As soon as we create it, we are going to create a new Class Library project, name it Shared and reference it from the main project:

Project Structure for the CustomValidation project

Now, let’s install the required library for the validation in the Shared project:

PM> Install-Package System.ComponentModel.Annotations

And create a new Product model in the same project:

public class Product
{
    [Required]
    public string Name { get; set; }
    [Required]
    public string Supplier { get; set; }
}

After this, we can go back to the main project and just modify the Index.razor file:

@page "/"

<EditForm Model="_product" OnValidSubmit="Submit" style="width:600px;">
    <DataAnnotationsValidator />
    <div class="form-group row">
        <label for="name" class="col-md-2 col-form-label">Name:</label>
        <div class="col-md-10">
            <InputText id="name" class="form-control" @bind-Value="_product.Name" />
            <ValidationMessage For="@(() => _product.Name)" />
        </div>
    </div>

    <div class="form-group row">
        <label for="supplier" class="col-md-2 col-form-label">Supplier:</label>
        <div class="col-md-10">
            <InputText id="supplier" class="form-control" @bind-Value="_product.Supplier" />
            <ValidationMessage For="@(() => _product.Supplier)" />
        </div>
    </div>

    <div class="row">
        <div class="col-md-12 text-right">
            <button type="submit" class="btn btn-success">Submit</button>
        </div>
    </div>
</EditForm>

Everything about this code is explained in our Forms and Form Validation in Blazor WebAssembly article, so we won’t be explaining anything here.

Let’s just create a new Index.razor.cs file in the Pages folder and modify it:

public partial class Index
{
    private Product _product = new Product();

    public void Submit() => 
        Console.WriteLine($"{_product.Name}, {_product.Supplier}");
}

That’s it. We can start the application and as soon as the Index page is up, we can press the Submit button:

Basic validation works in Blazor WebAssembly

And there we go. We have our validation in place. Now that we have it, we can customize it.

Adding Custom Validation in Blazor WebAssembly with Custom Validation Attributes

There are a lot of validation attributes provided with the Annotations library, but sometimes a new rule emerges that is not supported. For these kinds of rules, we have to create a custom attribute and apply it to our model class.

That said, let’s see how to do exactly that.

For example, let’s say we want to restrict the user to enter only the Code-Maze value for the Supplier field.

To do that, first, we have to create a new SupplierValidationAttribute class in the Shared project:

public class SupplierValidationAttribute : ValidationAttribute
{
    public string ValidSupplierValue { get; set; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var lowerValue = value.ToString().ToLower();
        if (lowerValue.Equals(ValidSupplierValue.ToLower()))
            return null;

        return new ValidationResult(ErrorMessage, new[] { validationContext.MemberName });
    }
}

As we can see, this class inherits from the ValidationAttribute abstract class, which is the base class for the validation attributes. Then, we have a single ValidSupplierValue property, which we can set from the model class when calling this attribute. After the property creation, we have to override the IsValid method that accepts two parameters. The value parameter will hold the value the user enters in the input field. The validationContext parameter describes the context in which we perform the validation.

The logic inside the method is pretty simple. We first convert the object value to a string and make it lowercase. Then we do a comparison between that value and our restricted property value. If they are the same, we just return null. Otherwise, we return a new ValidationResult with the provided ErrorMessage and a member name.

After this, we can return to the Product class and use this attribute:

public class Product
{
    [Required]
    public string Name { get; set; }
    [Required]
    [SupplierValidation(ErrorMessage = "Wrong value entered", ValidSupplierValue = "Code-Maze")]
    public string Supplier { get; set; }
}

So, we use the SupplierValdiation attribute and provide values for the ErrorMessage and ValidSupplierValue properties.

Testing

To test this, we can start the application and click the Submit button. As soon as we do that, we are going to see the same result as before. So the Required rule is applied for both input fields.

Now, let’s try entering the wrong value in the Supplier field:

Custom validation in Blazor WebAssembly with custom validation attribute

We can see our custom validation attribute kicks in immediately.

Also if we enter a correct value and press the submit button, we are going to see the result in the Console window:

Custom validation in Blazor WebAssembly valid input

Excellent.

Let’s move on.

Custom Validation Error Messages

Sometimes we want to add different styling to error messages by using a class from bootstrap or any other CSS library. Right now we can’t do that.

If we inspect the code of the ValidationMessage component (for the full source code, visit this link), we can find the BuildRenderTree method where the class that styles the message is hardcoded:

protected override void BuildRenderTree(RenderTreeBuilder builder)
{
    foreach (var message in CurrentEditContext.GetValidationMessages(_fieldIdentifier))
    {
        builder.OpenElement(0, "div");
        builder.AddMultipleAttributes(1, AdditionalAttributes);
        builder.AddAttribute(2, "class", "validation-message");
        builder.AddContent(3, message);
        builder.CloseElement();
    }
}

This means that any class we try to send to the ValdiationMessage component will be overridden by the validation-message class. We can find this class in the wwwroot/css/app.css file:

.validation-message {
    color: red;
}

Of course, we can always modify this class to add different styles. But that’s all we can do. As we said, we can’t provide a different class to the ValidationMessage component.

Well, we can’t with the default component design, but we are going to show you how to override it.

If we inspect the implemented ValidationMessage class, we can see the logic behind it. Of course, this class has a lot of logic that we don’t require here, but we are going to use some of its parts.

So, let’s start by creating a new MessageValidatorBase class in the main project and modifying it:

public class MessageValidatorBase<TValue> : ComponentBase, IDisposable
{
    private FieldIdentifier _fieldIdentifier;
    private EventHandler<ValidationStateChangedEventArgs> _stateChangedHandler
        => (sender, args) => StateHasChanged();

    [CascadingParameter]
    private EditContext EditContext { get; set; }
    [Parameter]
    public Expression<Func<TValue>> For { get; set; }
    [Parameter]
    public string Class { get; set; }

    protected IEnumerable<string> ValidationMessages =>
        EditContext.GetValidationMessages(_fieldIdentifier);

    protected override void OnInitialized()
    {
        _fieldIdentifier = FieldIdentifier.Create(For);
        EditContext.OnValidationStateChanged += _stateChangedHandler;
    }

    public void Dispose()
    {
        EditContext.OnValidationStateChanged -= _stateChangedHandler;
    }
}

For this to work, we require some using directives:

using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;

Explaining the Logic Behind this Class.

Our class inherits from the ComponentBase class and IDisposable interface as the ValidationMessage class does. Then we have a private _fieldIdentifier field to identify the field that we can edit. Also, we have a single event that does only one thing – calls StateHasChanged.

We have a couple of properties – EditContext, For, Class, and ValidationMessages. The EditContext property is a cascading parameter that provides the main support for the validation logic. The For property is a parameter of type Expression<Func<TValue>> that we are going to use to specify a field for which we want to display a validation message. We use the Class property that will accept the name of the class(es) we want to use for the message styling. Lastly, we store all the validation messages from EditContext inside the ValidationMessages property.

As soon as our component initializes, we initialize the _fieldIdentifier field by using the FieldIdentifier.Create method that accepts a single For parameter.  And also, attach our method to the OnValdiationStateChanged event. This means every time the validation state changes we are going to call the StateHasChanged method to re-render the page.

Finally, in the Dispose method, we have to clean after ourselves.

Excellent.

Now, we can use this base class in our custom validation component.

Creating a Custom Validation Component

Let’s start by creating a new Components folder in the main project and the CustomValidationMessage component inside it:

@typeparam TValue
@inherits MessageValidatorBase<TValue>

@foreach (var message in ValidationMessages)
{
    <div class="@Class">
        @message
    </div>
}

All we do here is inherit from the MessageValidatorBase class and iterate through each message from the ValidationMessages collection. For each message we find, we display it and use a provided class name(s) to style it.

And that’s all.

Let’s just modify the Index.razor file, to apply this component instead of the ValidationMessage component:

<div class="form-group row">
    <label for="supplier" class="col-md-2 col-form-label">Supplier:</label>
    <div class="col-md-10">
        <InputText id="supplier" class="form-control" @bind-Value="_product.Supplier" />
        <CustomValidationMessage For="@(() => _product.Supplier)"
            Class="alert alert-danger mt-1 py-1 text-center" />
    </div>
</div>

And we need a using statement inside the _Imports.razor file:

@using CustomValidation.Components

Now, we can test our form:

Custom validation message in Blazor WebAssembly

And there we go.

We can see our custom styled message.

Modifying Validation Styles for Input Elements

The last thing we are left to do is to customize the styles for the input fields. We are going to show you how to do that for both valid and invalid fields.

So, let’s continue on.

To support this, we can’t use the Model property in the EditForm component. We have to modify this by introducing the EditContext:

<EditForm EditContext="_editContext" OnValidSubmit="Submit" style="width:600px;">

Now, we have to modify the Index.razor.cs file, to support the validation with EditContext:

public partial class Index : IDisposable
{
    private Product _product = new Product();
    private EditContext _editContext;

    protected override void OnInitialized()
    {
        _editContext = new EditContext(_product);
        _editContext.OnFieldChanged += HandleFieldChanged;
    }

    private void HandleFieldChanged(object sender, FieldChangedEventArgs e)
    {
        _editContext.Validate();
        StateHasChanged();
    }

    public void Submit() =>
        Console.WriteLine($"{_product.Name}, {_product.Supplier}");

    public void Dispose()
    {
        _editContext.OnFieldChanged -= HandleFieldChanged;
    }
}

We create a private _editContext field and as soon as the component initializes, we initialize it with a model parameter. Also, we attach the HandleFieldChanged method to the OnFieldChanged event that raises every time the field value changes. Furthermore, we have the HandleFieldChanged method where we call the Validate method to validate EditContext, and call the StateHasChanged method to re-render the component.

Finally, we need the Dispose method to remove the HandleFieldChanged method from the event.

At this point, our validation should work as before.

Adding FieldCssClassProvider

To support the styling of our HTML elements, we need to create a new class in the main project:

public class ValidationFieldClassProvider : FieldCssClassProvider
{
    private readonly string _validClass;
    private readonly string _errorClass;

    public ValidationFieldClassProvider(string errorClass, string errorClass)
    {
        _validClass = validClass;
        _errorClass = errorClass;
    }

    public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier)
    {
        var hasMessages = !editContext.GetValidationMessages(fieldIdentifier).Any();

        return hasMessages ? _validClass : _errorClass;
    }
}

We inherit from the FieldCssClassProvider class that supplies information about the class names, we want to apply to our fields, and the validation state of EditContext. Then we have two fields for valid and error class names. Also, we have to override the GetFieldCssClass method where we check for any validation messages inside EditContext and conditionally return the class name we want to apply to the field.

Good, with this in place, we have to slightly modify the Index.razor.cs class:

protected override void OnInitialized()
{
    _editContext = new EditContext(_product);
    _editContext.SetFieldCssClassProvider(new ValidationFieldClassProvider("validField", "invalidField"));
    _editContext.OnFieldChanged += HandleFieldChanged;
}

And finally, let’s add the validField and invalidField classes in the app.css file:

.invalidField {
    outline: 2px dotted red;
}

.validField {
    outline: 1px dashed blue;
}

That’s it.

Let’s start the app:

Valid customized fields with FiealdCSSClassProvider

And we can see a different style for the valid fields.

Of course, if we click the Submit button:

 Invalid customized fields with FiealdCSSClassProvider

We can see a different style.

Awesome.

Everything works like a charm.

Conclusion

In this article, we have learned:

  • How to apply a custom validation in Blazor WebAssembly with custom validation attributes
  • How to modify the styles of the validation messages
  • And how to modify the styles of the fields on our form

Until the next article.

Best regards.

Liked it? Take a second to support Code Maze on Patreon and get the ad free reading experience!
Become a patron at Patreon!