🟨 Slightly different to Blazor Server
Handling user input with forms is the cornerstone of many common applications. Applications use forms to enable users to log in, to update a profile, to enter sensitive information, and to perform many other data-entry tasks. Blazor makes it easy to create and manage them using C#. With Blazor's data binding capabilities, form validation, and other features, you can quickly create dynamic and interactive forms that help users input and submit data. In this tutorial, we'll explore the various aspects of building forms in Blazor and provide you with practical examples to help you get started:
You can download the example code used in this topic on GitHub.
EditForm
is a component that is used to create forms in Blazor applications, while HTML form is a standard way of creating forms in HTML. The EditForm
component in Blazor provides features such as form validation, disabling a form control, and data binding. It allows developers to easily create forms that are tightly integrated with their Blazor application.
Below is a sample code for a Blazor WebAssembly form:
<EditForm Model="BlazorFormModel" OnSubmit="_ => BlazorFormSubmitted = true"> <fieldset> <legend>Blazor form</legend> <InputText @bind-Value="BlazorFormModel.BlazorFormResult" /> <input type="submit" value="Submit" /> </fieldset> </EditForm>
And here's a sample code for an HTML form:
<form @onsubmit="_ => HtmlFormSubmitted = true"> <fieldset> <legend>HTML form</legend> <input type="text" @bind-value="HtmlFormResult" @bind-value:event="oninput" /> <input type="submit" value="Submit" /> </fieldset> </form>
This tutorial will only cover the Blazor WebAssembly form.
To create a basic Blazor WebAssembly form, follow these steps:
public class SimpleBlazorFormModel { public string InputText { get; set; } = "Blazor School"; }
@code { public SimpleBlazorFormModel FormModel { get; set; } = new(); }
EditForm
component to define the form in your UI. The EditForm
component is responsible for managing the form submission and binding the form fields to the model's properties:<EditForm Model="FormModel" OnSubmit="HandleFormSubmit"> <InputText @bind-Value="FormModel.InputText" /> <button>Submit</button> </EditForm>
HandleFormSubmit
method in your component to handle the form submission:@code { ... public void HandleFormSubmit() { // Do something with the form data } }
As a programmer, validation is a crucial task that you cannot ignore, especially when it comes to website development. It's essential to validate user input to ensure that the data you receive is correct and safe to use. In Blazor WebAssembly forms, validation is performed in the browser, but you also need to implement another layer of validation at the API level. There are two types of validation that you can use:
Static Data Validation: This type of validation is performed against predefined rules or constraints. For example, you might validate that an email address is in the correct format or that a password meets certain complexity requirements. Blazor provides several built-in validation attributes that you can use, such as Required
, StringLength
, and RegularExpression
.
Dynamic Data Validation: This type of validation is performed against dynamic data that changes at runtime. For example, you might validate that a username is unique or that a credit card number is valid by calling an external service. Blazor provides a way to implement custom validation logic using the ValidationMessage
and ValidationMessage<T>
components, which can display error messages based on your custom validation logic.
By using a combination of these two validation types, you can ensure that your Blazor forms collect correct and valid data from users, and prevent errors and security issues.
In Blazor form, you can use DataAnnotation
to validate data against static rules like length, pattern, and range. Here are the steps to validate data against static rules using DataAnnotation
:
DataAnnotation
attributes. 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 ExampleString
property has the [Required]
attribute which specifies that it must have a value, while the ExampleInt
property has the [Range]
attribute which specifies the minimum and maximum values it can have.
Model
property of the EditForm
component. Also, add a submit button. For example:<EditForm Model="FormModel"> <button>Submit</button> </EditForm> @code { public AgainstStaticDataFormModel FormModel { get; set; } = new(); }
DataAnnotationsValidator
component inside the EditForm
component. For example:<EditForm Model="FormModel"> <DataAnnotationsValidator /> ... </EditForm>
ValidationMessage
or ValidationSummary
components.Here's a list of the available DataAnnotation attributes that you can use for validating 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. |
EditContext
and ValidationMessageStore
are the two primary components used for data validation. EditContext
facilitates control over the validation flow, while ValidationMessageStore
enables the addition of validation messages to the entire form or specific form controls. To validate data against dynamic business logic, follow these steps:
EditContext
, form model, and ValidationMessageStore
in the code section:@code { public EditContext FormContext { get; set; } = null!; public AgainstDynamicDataFormModel FormModel { get; set; } = new(); public ValidationMessageStore ValidationMessageStore { get; set; } = null!; }
Note: Usingnull!
will help ignore the dereference warning in Visual Studio 2022, but it doesn't safeguard against null references. Always exercise caution when usingnull!
.
OnInitialized
phase:protected override void OnInitialized() { FormContext = new(FormModel); ValidationMessageStore = new(FormContext); }
object? sender, FieldChangedEventArgs e
: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(); }
The validation flow involves removing old validation results, executing custom validation code, and running static data validation. To add a validation message, use ValidationMessageStore.Add()
and pass in the field. To add a validation message to the entire form, specify the field as <YourEditContext>.Field(string.Empty)
.
OnFieldChanged
event of EditContext
, and unsubscribe when necessary:@implements IDisposable ... @code { ... protected override void OnInitialized() { ... FormContext.OnFieldChanged += OnFormContextFieldChanged; } public void Dispose() { FormContext.OnFieldChanged -= OnFormContextFieldChanged; } }
By following these steps, you can implement dynamic data validation in Blazor forms with ease, ensuring data accuracy and integrity in complex business logic scenarios.
Disabling form controls is a common requirement for many websites, especially when certain users need to be prevented from interacting with specific parts of a form based on their roles. There are two ways to disable form controls in Blazor.
disabled
attributeUsing this method will permanently disable the form control. To do this, simply add the disabled
attribute with a value of true
to the control. For example:
<InputText class="form-control" @bind-Value="FormModel.ExampleString" disabled="true" />
@bind-disabled
directiveUsing this method, the form control can be dynamically switched between enabled and disabled states. To do this, declare a boolean
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="DisableFormControl"/> @code { public bool DisableFormControl { get; set; } = false; ... }
Whenever you change the boolean
value, the form control will be enabled or disabled accordingly.
Validation messages are essential in ensuring that users provide the correct inputs in a form. In order to effectively communicate validation errors to users, there are two places to display validation messages: validation summaries and inline validation messages.
A validation summary displays all validation errors in a form, and can be implemented using the ValidationSummary
component. To use this component, simply add it within the EditForm
element:
<EditForm Model="FormModel"> <DataAnnotationsValidator /> <ValidationSummary /> ... </EditForm>
Inline validation messages, on the other hand, are displayed next to the form control that has an error. To display inline validation messages, use the ValidationMessage
component. To implement inline validation messages, add the ValidationMessage
component immediately after the corresponding form control. Here's an example:
<EditForm Model="FormModel"> <ValidationSummary /> <InputText @bind-Value="FormModel.ExampleString" /> <ValidationMessage For="() => FormModel.ExampleString" /> </EditForm>
When using the ValidationMessage
component, it's important to specify the For
parameter, which should be followed by a delegate that returns the corresponding form model property.
Blazor forms have three events that you can handle: OnSubmit
, OnValidSubmit
, and OnInvalidSubmit
. Understanding how to handle these events can help you validate user inputs and provide a better user experience.
Here's what each event does:
OnSubmit
: This event is fired when the user clicks the submit button. Blazor form will let you handle the validation. You can choose to implement your own validation logic or use static data to validate the form.
OnValidSubmit
: This event is fired when the user clicks the submit button and the form has passed validation against static data. You can use this event to perform additional validation or make changes based on the validated form data.
OnInvalidSubmit
: This event is fired when the user clicks the submit button and the form has failed validation against static data. You can use this event to perform additional validation or make changes based on the invalidated form data.
Handling these events is similar, you just need to bind the event with an event handler. Here's an example of handling the OnValidSubmit
event:
<EditForm Model="FormModel" OnValidSubmit="ValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> ... <button>Submit</button> </EditForm> @code { ... public void ValidSubmit(EditContext formContext) { } }
In this example, the ValidSubmit
method is called when the form has passed validation against static data. You can modify the method to suit your validation logic, and you can remove the EditContext
parameter if you don't need it.
When it comes to website development, there are two commonly used form validation styles: inline validation and after submission validation.
By default, Blazor forms do not support after submission validation.
When working with Blazor forms, there are some common mistakes that should be avoided. These include:
DataAnnotationsValidator
tag.If you need to modify a form control value for some reason, it's important to notify the field change to ensure that the validations run again. In the following code, the incorrect way of updating the FormModel.RequiredString
value is shown:
<EditForm EditContext="FormContext"> <DataAnnotationsValidator /> <ValidationSummary /> <InputText @bind-Value="FormModel.RequiredString" /> <button type="button" @onclick="Correct">Correct</button> <button type="button" @onclick="Mistake">Mistake</button> </EditForm> @code { public void Mistake() { FormModel.RequiredString = ""; } }
To correctly update the FormModel.RequiredString
value, you need to notify the field change as shown below:
public void Correct() { FormModel.RequiredString = ""; FormContext.NotifyFieldChanged(FormContext.Field(nameof(FormModel.RequiredString))); }
By not notifying the field change, you run the risk of accepting incorrect information since the validations are not run again. The following video demonstrates this mistake in action.
To ensure clarity in button functionality, it is important to differentiate between submit buttons and function buttons. Submit buttons are used to send form data to a server while function buttons are used to execute some logic without submitting the form.
To create a function button, you can use either the <button type="button">
tag or <input type="button">
tag. Correct submit buttons can be created using the <button>
tag or the <input type="submit">
tag.
Examples of correct function buttons include:
<input type="button" @onclick="Method1" value="Call Method" /> <button type="button" @onclick="Method1" value="">Call Method</button>
Examples of correct submit buttons include:
<button>Submit</button> <input type="submit" value="Submit" />
However, it is important to note that some buttons may unintentionally call a method and submit the form at the same time. This can be problematic and should be avoided. Examples of incorrect buttons that call the method and submit the form at the same time include:
<button @onclick="Method1">(Not Intended) Call Method & Submit (button tag)</button> <input type="submit" @onclick="Method1" value="(Not Intended) Call Method & Submit (input tag)" />
To avoid this mistake, it is important to carefully consider the type and functionality of each button when creating them. The following video demonstrates this common mistake and the importance of distinguishing between function buttons and submit buttons.
DataAnnotationsValidator
tagThe DataAnnotationsValidator
tag is a crucial component for validating forms when using the EditForm
component. Its absence can result in the failure of form validation. To illustrate this point, please refer to the following video.
In Blazor WebAssembly, form validation occurs on the client-side. Therefore, it's necessary to perform additional validation on the API side. Alternatively, you could delegate all validation processes to the API and import errors to the form, as demonstrated in the Dynamic Data Validation section.
In contrast, Blazor Server handles form validation on the server-side, which eliminates the need for additional API-side validation.