In web development, form handling and input validation can be a daunting and laborious task. Most of the time, there is no out-of-the-box solution that helps us, developers, with this challenge.
Usually, the developer community and professional library authors create many different solutions, and we need to choose what’s suitable for us.
Of course, we also have the opportunity to use third-party libraries for Blazor Form handling and input validation. Still, the good news is that Blazor supports basic form handling and input validation out-of-the-box.
In this article, we will build an UserForm
component that accepts different input types, performs input validation, and handles the form submit.
You can follow along using the default Blazor application template within Visual Studio. All the code shown in this article is written in a single UserForm.razor
component file.
Basic Form Handling
Blazor provides an EditForm
component that wraps the HTML form tag and adds convenient functionality to handle user input.
The Model
property allows us to bind an instance of a model class to the form. We can create an instance of the class in the @code
block of the form component and bind the instance to the EditForm component using the Model property.
<EditForm Model="User"> </EditForm> @code { public UserModel User { get; set; } public class UserModel { public string Username { get; set; } public DateTime DateOfBirth { get; set; } public int LuckyNumber { get; set; } public UserType Type { get; set; } } public enum UserType { User, SuperUser, Admin } }
Handling the Form Submit
We have two different options for how to handle the submit of the form.
We can use the OnSubmit
property and provide a method that gets executed whenever the user clicks the submit button. Using this approach, we need to handle validation ourselves. This approach gives us the most freedom, but we also have to write more code.
The other approach is to use the OnValidSubmit
and OnInvalidSubmit
properties. Blazor performs input validation, and depending on the validation result, either the method bound to the OnValidSubmit
or the OnInvalidSubmit
property will be called.
<EditForm Model="User" OnValidSubmit="HandleValidSubmit"> </EditForm> @code { public UserModel User { get; set; } // UserModel definition omitted public void HandleValidSubmit() { // Save User object to backend } }
We want to use the default input validation and build a simple solution. That’s why we bind a HandleValidSubmit
method to the OnValidSubmit
property of the EditForm
component.
Input Wrappers Provide Value Binding
Now that we have the EditForm component in place, we need to add input fields to the form. Blazor not only offers a wrapper component for the form but also for the different input types.
Text Input
The InputText
wrapper component handles input fields of type text.
<InputText @bind-Value="User.Username" />
We can bind the Value property using the @bind-Value
syntax to a property of the UserModel
class referenced in the Model
property of the EditForm
component.
It is a two-way binding, meaning that the user input gets stored in the property, and if we initialize the property with a value, it will be displayed as the value of the form field on the screen.
Date Input
The InputDate
wrapper component handles input fields of type date.
<InputNumber @bind-Value="User.LuckyNumber" />
Value binding works the same as explained for the UserInput
wrapper.
InputSelect
The InputSelect
wrapper component handles select fields. In contrast to the very simple wrappers discussed before, the select input also needs options.
Let’s take a look at a convenient way to define a select input based on an enum type.
<InputSelect @bind-Value="User.Type"> @{ foreach (var value in Enum.GetValues(typeof(UserType))) { <option value="@value">@value</option> } } </InputSelect>
The InputSelect
wrapper component also offers a bindable Value
property. Further, we use a foreach
loop to create the option tags based on the UserType
enum type.
Additional built-in components
There are additional built-in form components available that we can use to create form fields.
We can also implement custom form components inheriting from the InputBase class.
Submit the Form Using an HTML5 Button
So far, we have used wrapper components for the form element and the input fields. For the submit button, we can use standard HTML5.
<button type="submit">Submit</button>
The Completed UserForm
Component
In the constructor of the UserForm
component, we create an instance of the UserModel
class.
We can also assign default values to the properties of the Model
class. Values set on the object and bound to a form field will be shown on the screen when the user navigates to the component.
The completed UserForm
component looks like this:
<EditForm Model="User" OnValidSubmit="HandleValidSubmit"> <InputText @bind-Value="User.Username" /> <InputDate @bind-Value="User.DateOfBirth" /> <InputNumber @bind-Value="User.LuckyNumber" /> <InputSelect @bind-Value="User.Type"> @{ foreach (var value in Enum.GetValues(typeof(UserType))) { <option value="@value">@value</option> } } </InputSelect> <button type="submit">Submit</button> </EditForm> @code { public UserModel User { get; set; } public UserForm() { User = new UserModel { DateOfBirth = DateTime.Today }; } public void HandleValidSubmit() { // Save User object to backend } public class UserModel { public string Username { get; set; } public DateTime DateOfBirth { get; set; } public int LuckyNumber { get; set; } public UserType Type { get; set; } } public enum UserType { User, SuperUser, Admin } }
Input Validation Rules Using Data Annotations
Now that we implemented the UserForm
component, we want to add simple input validation. It helps to make sure that the user provides the information required to fulfill our application’s needs.
Blazor supports validation based on data annotations from the System.ComponentModel.DataAnnotations
namespace.
Let’s add a few validation rules using data annotations for the UserModel
class.
public class UserModel { [Required] [MaxLength(15)] public string Username { get; set; } [Required] public DateTime DateOfBirth { get; set; } [Required] public int LuckyNumber { get; set; } [Required] public UserType Type { get; set; } }
Using the data annotation attributes, we mark all the properties as required and restrict the username’s max length to 15 characters.
There are more predefined validation attributes that you can use, for example, to validate email addresses, credit card numbers, or phone numbers.
You can also implement custom validation rules implementing an attribute based on the ValidationAttribute
base class.
Using the Validation Rules in the UserForm
Component
It’s not enough to define the validation rules on the class we bind to the Model
property of the EditForm
component.
We also need to specify that the EditForm
component uses the data annotations as validation rules. Luckily, Blazor offers the DataAnnotationsValidator
component to make things simple.
<EditForm Model="User" OnValidSubmit="HandleValidSubmit"> <DataAnnotationsValidator /> <!-- code omitted --> </EditForm>
We enable input validation based on data annotations by placing the DataAnnotationsValidator
as a child component of the EditForm
component.
Displaying Validation Errors
As long as the user correctly fills in the form, we don’t have to worry, but what if the user makes a mistake and the form is in an invalid state?
If the form is in an invalid state, we need to make sure that we let the user know about the validation errors and how he can fix the mistakes.
Blazor offers the ValidationSummary
component to display all the validation error messages on the form.
<EditForm Model="User" OnValidSubmit="HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <!-- code omitted --> </EditForm>
Like the DataAnnotationsValidator
component discussed before, we can add the ValidationSummary
component as a child component of the EditForm
component.
If we input invalid data into the form, we can now see the validation errors above the form fields.
Bonus: Making the Form Look Great
I purposely cut all the code example down as much as possible to demonstrate how the predefined Blazor components work.
To make it all look great, let’s add a few Bootstrap classes. Bootstrap is a user interface framework that is already included in the default Blazor application template.
The completed UserForm
component:
@using System.ComponentModel.DataAnnotations; <h3 style="margin-bottom: 20px;">User</h3> <EditForm Model="User" OnValidSubmit="HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="form-group"> <label for="dateInput">Username</label> <InputText class="form-control" @bind-Value="User.Username" /> </div> <div class="form-group"> <label for="dateInput">Date of Birth</label> <InputDate class="form-control" @bind-Value="User.DateOfBirth" /> </div> <div class="form-group"> <label for="dateInput">Lucky Number</label> <InputNumber class="form-control" @bind-Value="User.LuckyNumber" /> </div> <div class="form-group"> <label for="categoryInput">Type</label> <InputSelect class="form-control" @bind-Value="User.Type"> @{ foreach (var value in Enum.GetValues(typeof(UserType))) { <option value="@value">@value</option> } } </InputSelect> </div> <div> <button type="submit" class="btn btn-primary">Submit</button> </div> </EditForm> @code { public UserModel User { get; set; } public UserForm() { User = new UserModel { DateOfBirth = DateTime.Today }; } public void HandleValidSubmit() { // Save User object to backend } public class UserModel { [Required] [MaxLength(15)] public string Username { get; set; } [Required] public DateTime DateOfBirth { get; set; } [Required] public int LuckyNumber { get; set; } [Required] public UserType Type { get; set; } } public enum UserType { User, SuperUser, Admin } }
Summary
Blazor offers many predefined components to handle forms. We can use the EditForm
component to bind an instance of a model class to the form and to bind the method to handle the form submit.
Various wrapper components help us defining input fields and binding the value to a property of the EditForm
‘s Model.
We can define input validation rules using data annotations on the model class. We can even implement custom validation rules and apply them with the same strategy. The DataAnnotationsValidator
component enables data annotations as validation rules.
The ValidationSummary
component displays the validation errors on the form.
Bootstrap helps us formatting the form and make it visually appealing without writing additional CSS.