Blazor includes built-in support for creating forms and validating fields using data annotations.
Razor
<EditForm Model="model">
<DataAnnotationsValidator />
<InputText @bind-Value="model.Text" />
<ValidationMessage For="() => model.Text" />
<div>
Current value: @model.Text
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</EditForm>
@code{
Model model = new Model();
class Model
{
[StringLength(maximumLength: 10, MinimumLength = 3)]
public string Text { get; set; }
}
}
While this works well, validation only occurs when the input loses focus:
The <InputText> component uses the onchange event to bind the value and trigger validation. This event fires when the user commits the element's value, which for a text input means when the element loses focus. While the @bind directive lets you specify which event to use, the InputText component does not expose an alternative event. Let's try with a raw <input> element so we can use the oninput event instead:
Razor
<EditForm Model="model">
<DataAnnotationsValidator />
<input type="text" @bind-value="model.Text" @bind-value:event="oninput" />
<ValidationMessage For="() => model.Text" />
<div>
Current value: @model.Text
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</EditForm>
This time, the value is bound after every keystroke, but validation no longer works 😦
When using the InputText component, validation works because the component integrates with the EditContext created by EditForm and notifies it when the field value changes. When using a plain <input> element, the model value updates correctly but the EditContext is never notified, so the validation rules are never evaluated.
The solution is to create a custom component. Create a new file named Shared/InputTextOnInput.razor with the following content:
Razor
@* Inherits from the original InputText component *@
@inherits InputText
@* Bind the oninput event *@
<input @attributes="AdditionalAttributes"
class="@CssClass"
value="@CurrentValue"
@oninput="EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
Now you can use the component in the form:
Razor
<EditForm Model="model">
<DataAnnotationsValidator />
<InputTextOnInput @bind-Value="model.Text" />
<ValidationMessage For="() => model.Text" />
<div>
Current value: @model.Text
</div>
<button class="btn btn-primary" type="submit">Submit</button>
</EditForm>
It now works as expected:
#Additional resources
Do you have a question or a suggestion about this post? Contact me!