I needed to bind an <input type=number> in a Blazor application and get the value as soon as the user changes it. This works fine for integer values, but decimal values are more problematic.
First, I started with a basic solution. I used an input element and set the oninput event to bind the value as soon as the user changes it.
Razor
<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 reasonably well, but there are edge cases that break it. For example, you cannot enter a value such as "0.01". As soon as you type "0.0", the value is parsed (0f) and assigned to model.Value. Blazor then re-renders the component and sets the input value to 0f.ToString(), which is "0". As a result, "0.0" is immediately reset to "0". You can see this behavior in the following video:
⇒ This solution doesn't work for my needs
For my second attempt, I looked at the InputNumber<T> component:
Razor
<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; }
}
}
The InputNumber<T> component handles any decimal value without issue. However, there is no way to customize the binding event. It only uses the onchange event.
⇒ This solution doesn't work for my needs
#Third attempt: Custom component
Since the default binding behavior does not meet my needs, I created a new component that inherits from InputNumber and implements custom binding logic:
Razor
@* 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:
Razor
<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!