Validating an input on keypress instead of on change in Blazor

  • Gérald Barré

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!

Follow me:
Enjoy this blog?Buy Me A Coffee