Using forms in Blazor WebAssembly

Form is a good way to collect user information. Blazor WebAssembly has a built-in form with rich features. In this tutorial, you will discover:

  • Blazor WebAssembly form and HTML form.
  • Create a basic Blazor WebAssembly form.
  • Blazor WebAssembly form validation.
  • Disable a form control.
  • Display validation message.
  • Blazor WebAssembly form events.
  • Blazor WebAssembly form validation styles.
  • Common mistakes.
You can download the example code used in this topic on GitHub.

Blazor WebAssembly form and HTML form

Both Blazor WebAssembly form and HTML share the same purpose: collect user's information. However, they are different in terms of use and support in Blazor WebAssembly. The following image illustrates the differences between Blazor Form and HTML form:

blazor-form-vs-html-form.png

A sample Blazor WebAssembly form:

<EditForm Model="BlazorFormModel" OnSubmit="_ => BlazorFormSubmitted = true">
    <fieldset>
        <legend>Blazor form</legend>
        <InputText @bind-Value="BlazorFormModel.BlazorFormResult" />
        <input class="btn btn-primary" type="submit" value="Submit" />
    </fieldset>
</EditForm>

A sample HTML form:

<form @onsubmit="_ => HtmlFormSubmitted = true">
    <fieldset>
        <legend>HTML form</legend>
        <input type="text" @bind-value="HtmlFormResult" @bind-value:event="oninput" />
        <input class="btn btn-primary" type="submit" value="Submit" />
    </fieldset>
</form>
This tutorial will only focus on Blazor WebAssembly form.

Create a basic Blazor WebAssembly form

To create a basic Blazor WebAssembly form, you need to:

  1. Create a form model class. The form model is an object for Blazor WebAssembly form to store the information that is collected from the users. Each Blazor WebAssembly form has one and only one form model. This is an example of a form model class:
public class BasicBlazorFormModel
{
    public string ExampleString { get; set; } = "Blazor School";
}
  1. In your component, create a new property for the form model in the code section as follows:
@code {
    public BasicBlazorFormModel FormModel { get; set; } = new();
}
  1. Use the EditForm component in the UI section to display your form. You must also pass the FormModel to the component. For example:
<EditForm Model="FormModel" OnSubmit="SubmitForm">
    <InputText @bind-Value="FormModel.ExampleString" />
</EditForm>
  1. Create a method to handle form submission, pass the method and create a submit button in your form. For example:
<EditForm Model="FormModel" OnSubmit="SubmitForm">
    ...
    <button class="btn btn-primary">Submit</button>
</EditForm>

@code {
    ...
    public void SubmitForm()
    {
    }
}

Blazor WebAssembly form validation

Validation is a crucial task for programmers. In the world of website development, never trust a user input. Form validation is the process of checking if the information provided by a user is correct. In Blazor WebAssembly form, the validation will be executed in the browser and you will need another validation layer at the API level. There are 2 kinds of validation:

  • Validating against static data.
  • Validating against dynamic data.

Validating against static data

Blazor WebAssembly form uses DataAnnotation to validate against static data like the length, the pattern of a string, a number in range. To validating data against static data, you need to:

  1. Create a form model class. When you are defining the form model class, you can specify validate rules for each property of the class by using DataAnnotation. For example:
public class AgainstStaticDataFormModel
{
    [Required]
    public string ExampleString { get; set; } = "";
    [Range(2, int.MaxValue, ErrorMessage = "This is a custom message.")]
    public int ExampleInt { get; set; } = 1;
}

In the above example, the property ExampleString has the [Required] attribute which will specify the ExampleString must have a value. The property ExampleInt has the [Range] attribute which will specify the minimum and the maximum number for ExampleInt, in this case the ExampleInt must have value from 2 to the int.MaxValue; Any value that is not in the range will display the error message "This is a custom message".

  1. Get the form ready (declare and binding form model, add submit button). For example:
<EditForm Model="FormModel">
    <button class="btn btn-primary">Submit</button>
</EditForm>

@code {
    public AgainstStaticDataFormModel FormModel { get; set; } = new();
}
  1. Add DataAnnotationsValidator tag inside the EditForm. For example:
<EditForm Model="FormModel">
    <DataAnnotationsValidator />
    ...
</EditForm>
  1. (Optional) Show error messages with ValidationMessage or ValidationSummary.

This is the list of DataAnnotation attributes for validate data.

Attribute Name Description
Compare Compares two properties.
CreditCard Check if the data is a well-formed credit card.
EmailAddress Validates an email address.
EnumDataType Check if the provided data belongs to an enum.
FileExtensions Validates file name extensions.
MaxLength Specifies the maximum length of array or string data allowed in a property.
MinLength Specifies the minimum length of collection/string data allowed in a property.
Phone Specifies that a data field value is a well-formed phone number.
Range Specifies the numeric range constraints for the value of a data field.
RegularExpression Regular expression validation attribute.
Required Validation attribute to indicate that a property field or parameter is required.
StringLength Validation attribute to assert a string property, field or parameter does not exceed a maximum length.
Url Provides URL validation.

Validating against dynamic data

This kind of validation usually benefits complex business logic. Some examples are: check if username is unique, check if there is enough money in the bank account, etc... Blazor WebAssembly form uses EditContext and ValidationMessageStore, EditContext will help you to control the validation flow, ValidationMessageStore will allow you to add a validation message to the entire form or a specific form control. To validating data against dynamic data, you need to:

  1. Declare a property for each EditContext, form model, ValidationMessageStore in the code section. For example:
@code {
    public EditContext FormContext { get; set; } = null!;
    public AgainstDynamicDataFormModel FormModel { get; set; } = new();
    public ValidationMessageStore ValidationMessageStore { get; set; } = null!;
}
The null! will help you to ignore the dereference warning of Visual Studio 2022. However, it does not protect you against null reference. You need to pay attention whenever you use null!.
  1. Initialize the created properties in the OnInitialized phase. For example:
protected override void OnInitialized()
{
    FormContext = new(FormModel);
    ValidationMessageStore = new(FormContext);
}
  1. Define the validating flow: Remove old validating result => Custom validation code => Run static data validate. The method's signature must be object? sender, FieldChangedEventArgs e. For example:
public void OnFormContextFieldChanged(object? sender, FieldChangedEventArgs e)
{
    ValidationMessageStore.Clear();

    if (FormModel.AllowText is false && string.IsNullOrEmpty(FormModel.Text) is false)
    {
        ValidationMessageStore.Add(FormContext.Field(nameof(FormModel.Text)), "This message is for the field!");
        ValidationMessageStore.Add(FormContext.Field(string.Empty), "This message is for the entire form!");
    }

    FormContext.Validate();
}
To add a validation message, you need to use ValidationMessageStore.Add() and pass in the field. In case you want to add a validation message to the entire form, you need to specify the field as <YourEditContext>.Field(string.Empty).
  1. Subscribe to the event OnFieldChanged of EditContext and unsubscribe afterward. For example:
@implements IDisposable

...

@code {
    ...
    protected override void OnInitialized()
    {
        ...
        FormContext.OnFieldChanged += OnFormContextFieldChanged;
    }

    public void Dispose()
    {
        FormContext.OnFieldChanged -= OnFormContextFieldChanged;
    }
}

Disable a form control

Having parts of a disabled is a common requirement for any large website. Sometimes, a user must be prevented from interacting with a form based upon their roles on the website. There are 2 ways to disable a form control in Blazor WebAssembly form.

  1. Use HTML disabled attribute.
  2. Use Blazor disabled directive.

Using HTML attribute to disable a form control

By applying this method, your form control is permanently disabled. Assuming you have the following form control:

<InputText class="form-control" @bind-Value="FormModel.ExampleString" />

Then you need to add disabled="true" to the form control. For example:

<InputText class="form-control" @bind-Value="FormModel.ExampleString" disabled="true" />

Using Blazor directive to disable a form control

By applying this method, your form control can be switched from enabled to disabled. Assuming you have the following form control:

<InputText class="form-control" @bind-Value="FormModel.ExampleString" />

Then you need to declare a bool property in the component and bind it to the form control using the @bind-disabled directive. For example:

<InputText class="form-control" @bind-Value="FormModel.ExampleString" @bind-disabled="DisableFormControl2"/>

@code {
    public bool DisableFormControl2 { get; set; } = false;
    ...
}

Then whenever you change the boolean value, the form control will be enabled or disabled accordingly.


Display validation message

There are 2 places to display the validation message, you can either display the validation summary or the validation message for each form control.

validation-summary-vs-validation-form-control.png

The validation summary usually displays all the validation error in your form. You can use the component ValidationSummary to display the validation summary. For example:

<EditForm Model="FormModel">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <div>
        <label>
            Example string:
            <InputText @bind-Value="FormModel.ExampleString" />
        </label>
    </div>
    <div>
        <label>
            Example number (must be greater than 2):
            <InputNumber @bind-Value="FormModel.ExampleInt" />
        </label>
    </div>
    <button class="btn btn-primary">Submit</button>
</EditForm>

The inline validation message displays all the validation messages for one and only one form control. You can use the component ValidationMessage to display the validation message for a specific form control. For example:

<EditForm Model="FormModel">
    <ValidationSummary />
    <div>
        <label>
            Example string:
            <InputText @bind-Value="FormModel.ExampleString" />
        </label>
    </div>
    <ValidationMessage For="() => FormModel.ExampleString" />
    <div>
        <label>
            Example number (must be greater than 2):
            <InputNumber @bind-Value="FormModel.ExampleInt" />
        </label>
    </div>
    <ValidationMessage For="() => FormModel.ExampleInt" />
    <button class="btn btn-primary">Submit</button>
</EditForm>

When using the ValidationMessage component, you must specify the For parameter followed by a delegate that returns your form model property.


Blazor WebAssembly form events

A Blazor WebAssembly form has 3 events: OnSubmit, OnValidSubmit and OnInvalidSubmit.

  • OnSubmit: Fired when the users press submit button, Blazor WebAssembly form will leave you do the validation.
  • OnValidSubmit: Fired when the users press the submit button and the form is passed the validation against static data. You can validate your form again and change the validation result here.
  • OnInvalidSubmit: Fired when the users press the submit button and the form is failed the validation against static data. You can validate your form again and change the validation result here.

Handling the Blazor WebAssembly form events is similar. Here is an example of handling OnValidSubmit events.

<EditForm Model="FormModel" OnValidSubmit="ValidSubmit">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <label>
        Leave textbox empty to make validation error:
        <InputText @bind-Value="FormModel.ExampleString" />
    </label>
    <button>Submit</button>
</EditForm>

@code {
    ...
    public void ValidSubmit(EditContext formContext)
    {
    }
}

In the example, in the ValidSubmit method, you can remove the EditContext parameter in case you don't need it.


Blazor WebAssembly form validation styles

In the website development, there are 2 validation styles for form: inline validation and after submission validation.

  • Inline validation: The validation will occur whenever you make a change in your form. For example, the form requires the users to input email and phone number; As soon as the user leaves the email text box, the validation will begin and will show error right away if email is not valid.
  • After submission validation: The validation will occur after the user has submitted the form. For example, the form requires the users to input email and phone number; The user must input email and phone number, then as soon as the user presses the submit button, the form will show validation error (if any).

However, Blazor WebAssembly form does not support after submission validation by default. We will have a guide about this topic later in the Advanced Blazor WebAssembly tutorial series.


Common mistakes

When working with Blazor WebAssembly form, there are some common mistakes that you need to avoid. They are:

  • Modify form control value without notify field change.
  • Create function button as submit button.
  • Forget DataAnnotationsValidator tag.

Modify form control value without notify field change

For some reasons, you need to modify a form control value but you don't notify the field change. Assuming you have the following code:

<EditForm EditContext="FormContext">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <label>
        Leave textbox empty to make validation error:
        <InputText @bind-Value="FormModel.RequiredString" />
    </label>
    <button type="button" class="btn btn-outline-success" @onclick="Correct">Correct</button>
    <button type="button" class="btn btn-outline-danger" @onclick="Mistake">Mistake</button>
</EditForm>

This is not a correct way to update FormModel.RequiredString.

public void Mistake()
{
    FormModel.RequiredString = "";
}

To update the FormModel.RequiredString correctly, you need to notify field change afterward.

public void Correct()
{
    FormModel.RequiredString = "";
    FormContext.NotifyFieldChanged(FormContext.Field(nameof(FormModel.RequiredString)));
}

Whenever you modify a form control value, the validations should run again. When you update the form control value and not notifying field change, the validations are not run, giving you a possibility to accept wrong information. The following video demonstrate this mistake:

Create function button as submit button

A function button is a button to execute some logic but not to submit the form. There are 2 ways to create a button, by using <button> tag and <input type="button"> tag. These are correct submit buttons:

<button>Submit (button tag)</button>
<input type="submit" value="Submit (input tag)" />

These are correct function buttons:

<input type="button" @onclick="Method1" value="Call Method (input tag)" />
<button type="button" @onclick="Method1" value="">Call Method (button tag)</button>

These buttons are not correct buttons because it will call the method and submit the form at the same time:

<button @onclick="Method1">(Not Intended) Call Method & Submit (button tag)</button>
<input type="submit" @onclick="Method1" value="(Not Intended) Call Method & Submit (input tag)" />

The following video demonstrates this mistake:

Forget DataAnnotationsValidator tag

The DataAnnotationsValidator is an important component when you are using EditForm component. Without this tag, you cannot validate the form. The following video demonstrates this situation:

BLAZOR SCHOOL
Designed and built with care by our dedicated team, with contributions from a supportive community. We strive to provide the best learning experience for our users.
Docs licensed CC-BY-SA-4.0
Copyright © 2021-2025 Blazor School
An unhandled error has occurred. Reload 🗙