Skip to content

An Introduction to Forms and Validation with Blazor

Resilient Forms

Forms are a necessary evil in many applications, but that doesn’t mean they have to be difficult to utilize. Throughout this post, we will explore how to set up form validation with Blazor so you can easily create resilient forms and move on with your life.

Project Overview

To show off some of the form-based goodness that Blazor has to offer, we will work through creating an application to track simple contact information. You can find the code for this project in this repository on GitHub.

The starting project is a simple application with a form to create new contacts and list any added contacts.

Important files

We will be working primarily with the following files:

  • Shared/AddcontactForm.razor – This holds the UI and our mechanism for submitting and saving field entries.
  • Data/Contact.cs – This is the data model we will augment to have tighter validation controls.

Starting Off – Field updates

If you run the starting project, you’ll see that the form submission works out of the box because we have done some basic wiring needed to get things up and running.

If you are totally new to Blazor, we recommend checking out the Blazor for Beginners video series to get up to speed.

For now, the important pieces to know are @onsubmit, which connects the form’s submit event to a defined function, and @bind-value, which will automatically update the value of a property for us.

Making a Field Required

The form may be able to create new contacts, but a blank contact is not exactly helpful. Let’s make the name fields required so we can no longer submit a blank contact.

Updating the Model

First, we need to update our data model so that it knows that we expect at least the first name and last name fields. Luckily, we can use data annotations to accomplish this. Let’s modify the Contact.cs file to include the required data annotation for those fields. Now, you should have a model that looks like this:

public class Contact
{
    [Required]
    public string? FirstName { get; set; }

    [Required]
    public string? LastName { get; set; }
    public string? PhoneNumber { get; set; }
    public string? Email { get; set; }
}
Code language: C# (cs)

Enforcing the Validation

Even though we told our model to require the name fields, the normal HTML <form> element doesn’t know how to enforce the validation. To make the form automatically test the data validity, we need to use two Microsoft components called <EditForm> and <DataAnnotationsValidator />; this will let us say what model this form should use to validate against, as well as trigger the validation automatically. In our AddContactForm.razor file, let’s replace the standard <form> tag with the following components:

<EditForm Model="editingContact" class="mb-5" OnValidSubmit="submitForm">
<DataAnnotationsValidator /></EditForm>
Code language: HTML, XML (xml)

The Model property associates the instance of the model to validate. In this case, it is the instance of a Contact model stored in a variable below in the @code section of the same file. The OnValidSubmit does the same thing as the onsubmit binding. The new <DataAnnotationsValidator /> component tells the form to look at our data annotations for the field requirements. Now, trying to submit the form without providing the name fields will fail.

For more information, see Binding a form in the Microsoft documentation.

Displaying Field Errors

It’s great that we are no longer accepting empty contact records, but we should probably let the user know what is wrong so they can make corrections. Luckily, there is a pre-built component we can leverage to get the error message to show our customized text.

Let’s update the first name field HTML to the following:

<label for="first-name" class="form-label">First name</label>
<InputText @bind-Value="editingContact.FirstName" class="form-control" id="first-name"/>
<ValidationMessage For="@(() => editingContact.FirstName)" />
Code language: HTML, XML (xml)

The <ValidationMessage /> component allows us to define a For property and select the field for which we should show messages. If everything is valid, no message is displayed. However, if the field is invalid, it will show either the default data annotation message or any defined custom error message.

Before moving on, update all other fields with the <ValidationMessage /> component.

Displaying Custom Error Messages

The default data annotation message is helpful but does not always provide the most user-friendly message. Fortunately, if we specify a custom error message for our data annotation, it is automatically used. Update the Contact.cs model to have custom error messages like the one below and reload your application to see the custom error message:

[Required(ErrorMessage = "Please provide a first name.")]
public string FirstName { get; set; } = string.Empty;
Code language: C# (cs)

Optional Fields

The form is working, and the required string fields are validating correctly, but what about an optional field like email? Leaving the field empty is valid, but if someone enters a value in the field, we want to ensure it is in the typical email format. Annotations come to our rescue again! In our Contact.cs file, we can update the email property to be:

[EmailAddress]
public string? Email { get; set; }
Code language: C# (cs)

The [EmailAddress] validates the string is formatted correctly.

Please note that the [EmailAddress] attribute only validates that the string looks vaguely email-like but doesn’t catch everything. For more information, check out the Microsoft documentation for verifying email addresses.

Unfortunately, if someone were to populate the email address and then delete the value to be empty again, the validation will try to validate that an empty string is formatted like an address. To accommodate this use case, we can update the email field to have a shadow property to ensure it is set to null when a value is deleted.

private string? _Email;

[EmailAddress]
public string? Email
{
    get
    {
        return _Email;
    }
    set
    {
        _Email = string.IsNullOrWhiteSpace(value) ? null : value;
    }
}
Code language: JavaScript (javascript)

Wrapping Up

After those final adjustments to our Contact model, we have a fully validated form!

With minimal effort and code, we were able to connect the backend to our UI, allowing us to maintain one model as a source of truth. Now, if we make changes to the model, the form will automatically reflect the changes with no additional work.

Want More?

Check out how the IntelliTeam utilized Blazor to create games in our 24-hour coding competition.

Just getting started with Blazor? Here are some helpful resources:

Does Your Organization Need a Custom Solution?

Let’s chat about how we can help you achieve excellence on your next project!

Tags: