Binding decimal numbers using the oninput event in Blazor

  • Gérald Barré

I needed to bind an <input type=number> in a Blazor application. I want to get the value as soon as the user changes the value. There is no problem for integer values. However, this is not as simple for decimal values.

#First attempt: <input type=number>

First, I started with a basic solution. I use an input element and set the oninput event to bind the value as soon as the user change the value.

<input type="number" @bind-value="model.Value" @bind-value:event="oninput" />
<p>Value: @model.ValueSingle</p>

@code {
    Model model = new Model();

    public class Model
    {
        public float Value { get; set; }
    }
}

This code works pretty well. However, there are a few things that don't work correctly. For example, you cannot enter a value such as "0.01". As soon as you enter "0.0", the value is parsed (0f) and set to model.Value. Then Blazor re-renders the component, so it set the input value to 0f.ToString() which is "0". Thus, when you enter "0.0" the text is reset to "0". You can see the result in the following video:

⇒ This solution doesn't work for my needs

#Second attempt: InputNumber

For my second attempt, I looked at the InputNumber<T> component:

<EditForm Model="model">
    <InputNumber @bind-Value="model.Value" />
    <p>Value: @model.Value</p>
</EditForm>

@code {
    Model model = new Model();

    public class Model
    {
        public float Value { get; set; }
    }
}

Using the InputNumber<T> component, there is no problem to enter any decimal value. However, there is no way to customize the event used to bind the value. So, it only uses onchange event and cannot be customized.

⇒ This solution doesn't work for my needs

#Third attempt: Custom component

As the default binding behavior doesn't work the way I want, I decided to create a new component that inherits from the InputNumber component and implement a custom binding logic:

@* file: InputNumberOnInput.razor *@
@typeparam T
@inherits InputNumber<T>

<input @attributes="AdditionalAttributes"
       type="number"
       class="@CssClass"
       value="@stringValue"
       @oninput="OnInput"
       @onblur="OnBlur" />

@code {
    private string stringValue;
    private T lastParsedValue;

    protected override void OnParametersSet()
    {
        // Only overwrite the "stringValue" when the Value is different
        if (!Equals(CurrentValue, lastParsedValue))
        {
            lastParsedValue = CurrentValue;
            stringValue = CurrentValueAsString;
        }
    }

    private void OnInput(ChangeEventArgs e)
    {
        // Update the value
        CurrentValueAsString = stringValue = (string)e.Value;
        lastParsedValue = CurrentValue;
    }

    private void OnBlur(FocusEventArgs e)
    {
        // Overwrite the stringValue property with the parsed value.
        // This call Value.ToString(), so the value in the input is well formatted.
        // note: Ensure the string value is valid before updating the content
        if (!EditContext.GetValidationMessages(FieldIdentifier).Any())
        {
            stringValue = CurrentValueAsString;
        }
    }
}

You can use this component like the InputNumber component:

<EditForm Model="model">
    <InputNumberOnInput @bind-Value="model.Value" />
    <p>Value: @model.Value</p>
</EditForm>

@code {
    Model model = new Model();

    public class Model
    {
        public float Value { get; set; }
    }
}

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee