Author : MD TAREQ HASSAN | Updated : 2020/08/16
Form validation in blazor
Forms and validation are supported (out-of-the-box) in Blazor using data annotations (the same component model data annotations that are used in MVC & Razor pages). To use validation we have to have model with data annotations and edit form defined in Blazor view.
<EditForm>
- A form is defined using the
<EditForm>
component (there are built-in form components,EditForm
is one of them) Model
attribute of<EditForm>
- Property or field is assigned to the
Model
attribute of the<EditForm>
element - where Property or field is of a type
T
T
==Foo
(class Foo { // properties/fields }
)
- Property or field is assigned to the
- A form is defined using the
<DataAnnotationsValidator />
is used insideEditForm
to activate form validation- The DataAnnotationsValidator component attaches validation support using data annotations
- https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.forms.dataannotationsvalidator
<ValidationSummary />
is used insideEditForm
to show validation errors- The ValidationSummary component summarizes validation messages.
- https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.forms.validationsummary
<ValidationMessage For = >
: to show indivisual form field error messages- Quoted form:
<ValidationMessage For="() => Person.Name"/>
- Razor expression form:
<ValidationMessage For=@( () => Person.Name )/>
- Quoted form:
- When form is submitted, corresponding event is triggered (see form submission below)
- Links:
Notes:
- All of the input components, including EditForm, support arbitrary attributes. Any attribute that doesn’t match a component parameter is added to the rendered HTML element
- Any attribute is rendered in HTML element
- suppose we added
xxx
attribute in built-in componentInputText
:<InputText xxx="123" id="..." @bind-Value="@..." />
xxx
will not match any parameter ofInputText
- xxx will be rendered as:
<input xxx="123" id="..." class="valid">
- suppose we added
Built-in Form Components
Blazor has built-in form components
- A set of built-in input components are available to receive and validate user input
- Inputs are validated when they’re changed and when a form is submitted
- Example:
EditForm
: Parent component for form. Supports form validation out of the boxInputText
: An input component for editing String valuesInputTextArea
: A multiline input component for editing String values- etc.
- Available form components : https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.forms
Ways to validate
Method -1 : <EditForm Model="@XxxModel" OnValidSubmit="@OnValidSubmission" OnInValidSubmit="@OnInValidSubmission">
- For change in any input form field, a server call will be made and validation will be performed automatically
- chnage event of form input field: clicked outside of input field, pressed enter
ValidationSummary
andValidationMessage
will be shown accordingly (automatically)
OnSubmit
can not be used (useOnValidSubmit
andOnInValidSubmit
)- Less controll over form
Create.razor.cs
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
public class CreateBase: ComponentBase
{
protected CreateEmailModel CreateEmailModel { get; set; }
protected override void OnInitialized()
{
CreateEmailModel = new CreateEmailModel();
}
protected void OnValidSubmission(EditContext editContext)
{
// ... ... ...
}
protected void OnInValidSubmission(EditContext editContext)
{
// ... ... ...
}
}
Create.razor
@page "/emails/create"
@inherits CreateBase
// ... ... ...
<EditForm Model="@CreateEmailModel" OnValidSubmit="@OnValidSubmission" OnInValidSubmit="@OnInValidSubmission">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group row">
<label for="@nameof(CreateEmailModel.To)" class="col-1 text-right pr-1 my-auto">@nameof(CreateEmailModel.To):</label>
<InputText id="@nameof(CreateEmailModel.To)" @bind-Value="@CreateEmailModel.To" class="col-5" />
<ValidationMessage For="() => CreateEmailModel.To" class="col" />
</div>
// ... ... ...
</EditForm>
More: https://www.learmoreseekmore.com/2020/03/blazor-server-form-validation.html
Method - 2 : <EditForm EditContext="@_editContext" OnSubmit="@AddOrUpdate">
- More controll over form
- Need to create and track edit context manually (
_editContext = new EditContext(CreateEmailModel);
) - Need to validate form manually by calling
var isInvalidForm = !_editContext.Validate();
(if invalid then log error and return)
Create.razor.cs
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
public class CreateBase: ComponentBase
{
protected CreateEmailModel CreateEmailModel { get; set; }
protected EditContext _editContext;
// ... ... ...
private void InitEditContext()
{
CreateEmailModel = new CreateEmailModel();
_editContext = new EditContext(CreateEmailModel);
}
// Lifecycle callback
protected override void OnInitialized()
{
InitEditContext();
}
protected void OnSubmit()
{
var isInvalidForm = !_editContext.Validate();
if (isInvalidForm)
{
Debug.WriteLine($"Submitted form is not valid");
return;
}
// Perfor operation i.e. _service.CreateEmailEntry(CreateEmailModel)
// Reset edit context
InitEditContext();
}
// ... ... ...
}
Create.razor
@page "/emails/create"
@inherits CreateBase
// ... ... ...
<EditForm EditContext="@_editContext" OnSubmit="@OnSubmit">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group row">
<label for="@nameof(CreateEmailModel.To)" class="col-1 text-right pr-1 my-auto">@nameof(CreateEmailModel.To):</label>
<InputText id="@nameof(CreateEmailModel.To)" @bind-Value="@CreateEmailModel.To" class="col-5" />
<ValidationMessage For="() => CreateEmailModel.To" class="col" />
</div>
// ... ... ...
</EditForm>
Basic EditForm Example
@page "/emails/create"
@inherits CreateBase
<h1>Create</h1>
<hr />
<EditForm Model="@CreateEmailModel" OnValidSubmit="@OnValidSubmission">
<DataAnnotationsValidator />
<ValidationSummary />
<div class="form-group row">
<label for="@nameof(CreateEmailModel.To)" class="col-1 text-right pr-1 my-auto">@nameof(CreateEmailModel.To):</label>
<InputText id="@nameof(CreateEmailModel.To)" @bind-Value="@CreateEmailModel.To" class="col-5" />
<ValidationMessage For="() => CreateEmailModel.To" class="col" />
</div>
<div class="form-group row">
<label for="@nameof(CreateEmailModel.Cc)" class="col-1 text-right pr-1 my-auto">@nameof(CreateEmailModel.Cc):</label>
<InputText id="@nameof(CreateEmailModel.Cc)" @bind-Value="@CreateEmailModel.Cc" class="col-5" />
<ValidationMessage For="() => CreateEmailModel.Cc" class="col" />
</div>
<div class="form-group row">
<label for="@nameof(CreateEmailModel.Bcc)" class="col-1 text-right pr-1 my-auto">@nameof(CreateEmailModel.Bcc):</label>
<InputText id="@nameof(CreateEmailModel.Bcc)" @bind-Value="@CreateEmailModel.Bcc" class="col-5" />
<ValidationMessage For="() => CreateEmailModel.Bcc" class="col" />
</div>
<div class="row">
<div class="col-1">
</div>
<div class="col p-0 my-auto">
<button type="submit" class="btn btn-success">Create</button>
</div>
</div>
</EditForm>
Create.razor.cs
using Blazor.Server.Models.InputModels;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
public class CreateBase: ComponentBase
{
protected CreateEmailModel CreateEmailModel { get; set; }
protected override void OnInitialized()
{
CreateEmailModel = new CreateEmailModel();
}
protected void OnValidSubmission(EditContext editContext)
{
// ... ... ...
}
}
CreateEmailModel.cs
using System.ComponentModel.DataAnnotations;
public class CreateEmailModel
{
[Required]
[StringLength(50)]
public string To { get; set; }
public string Cc { get; set; }
public string Bcc { get; set; }
}
Form Submission
- There are three events on an EditForm related to form submission:
OnValidSubmit
OnInvalidSubmit
OnSubmit
- “
OnValidSubmit
+OnInvalidSubmit
” and “OnSubmit
” are mutually exclusive, that means use either [OnValidSubmit
and/orOnInvalidSubmit
] or [OnSubmit
] - From submission events pass
EditContext
as a parameterEditContext
can be used to check form input- Example:
bool formIsValid = editContext.Validate();
Using C# code in the same file for simplicity, use code behind approach as best practice
OnValidSubmit
+ OnInvalidSubmit
<EditForm Model="" OnValidSubmit="@OnValidSubmission" OnInvalidSubmit="@OnInvalidSubmission">
<!-- ... ... ... -->
</EditForm>
@code {
void OnValidSubmission(EditContext editContext)
{
// ... ... ...
}
void OnInvalidSubmission(EditContext editContext)
{
// ... ... ...
}
}
OnSubmit
<EditForm Model="" OnValidSubmit="@OnValidSubmission" OnInvalidSubmit="@OnInvalidSubmission">
<!-- ... ... ... -->
</EditForm>
@code {
void FormSubmitted(EditContext editContext)
{
bool formIsValid = editContext.Validate();
// ... ... ...
}
Validating a Form
Prerequisite: Scaffolding CRUD in blazor server