Binding decimal numbers using the oninput event in Blazor
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!