Validating an input on keypress instead of on change in Blazor
Blazor comes with everything needed to create forms and validate them. You can create a form and validate fields using data annotations.
<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, the validation occurs when the input loses the focus:
The <InputText>
component uses the onchange
event to bind the value, and so, to trigger the validation. This event is fired when the user commits the element's value. For a text input this means when the element loses focus. When you use the @bind
directive, you can set the event to use. However, the InputText
component doesn't expose another event. Let's try with a raw <input>
element, so we can use the oninput
event to bind the value:
<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, the validation doesn't work anymore 😦
When using the InputText
component, the validation works because this component uses the current EditContext
created by the EditForm
and updates the value of the field. When using the input
element, it updates the value of model.Text
but it doesn't take into account the EditContext
, so the validation rules are not evaluated.
The solution is to create a new component. You can create a new file named Shared/InputTextOnInput.razor
with the following content:
@* 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:
<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 finally works as expected:
#Additional resources
Do you have a question or a suggestion about this post? Contact me!