On the normal Blazor Input controls update occurs when you exit the control.
To wire them up for the oninput event, you need to extend the existing controls. I've added the UpdateOnInput parameter to control which event the update is wired to.
Here's MyInputText:
public class MyInputText : InputText
{
[Parameter] public bool UpdateOnInput { get; set; } = false;
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddMultipleAttributes(1, AdditionalAttributes);
if (!string.IsNullOrWhiteSpace(this.CssClass))
builder.AddAttribute(2, "class", CssClass);
builder.AddAttribute(3, "value", BindConverter.FormatValue(CurrentValue));
if (this.UpdateOnInput)
builder.AddAttribute(4, "oninput", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
else
builder.AddAttribute(5, "onchange", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.AddElementReferenceCapture(6, __inputReference => Element = __inputReference);
builder.CloseElement();
}
}
The original is here: https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web/src/Forms/InputText.cs
And MyInputNumber:
public class MyInputNumber<TValue> : InputNumber<TValue>
{
[Parameter] public bool UpdateOnInput { get; set; } = false;
private static readonly string _stepAttributeValue = GetStepAttributeValue();
private static string GetStepAttributeValue()
{
// Unwrap Nullable<T>, because InputBase already deals with the Nullable aspect
// of it for us. We will only get asked to parse the T for nonempty inputs.
var targetType = Nullable.GetUnderlyingType(typeof(TValue)) ?? typeof(TValue);
if (targetType == typeof(int) ||
targetType == typeof(long) ||
targetType == typeof(short) ||
targetType == typeof(float) ||
targetType == typeof(double) ||
targetType == typeof(decimal))
{
return "any";
}
else
{
throw new InvalidOperationException($"The type '{targetType}' is not a supported numeric type.");
}
}
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddAttribute(1, "step", _stepAttributeValue);
builder.AddMultipleAttributes(2, AdditionalAttributes);
builder.AddAttribute(3, "type", "number");
if (!string.IsNullOrWhiteSpace(this.CssClass))
builder.AddAttribute(4, "class", CssClass);
builder.AddAttribute(5, "value", BindConverter.FormatValue(CurrentValueAsString));
if (this.UpdateOnInput)
builder.AddAttribute(6, "oninput", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
else
builder.AddAttribute(7, "onchange", EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.AddElementReferenceCapture(8, __inputReference => Element = __inputReference);
builder.CloseElement();
}
}
Original here - https://github.com/dotnet/aspnetcore/blob/main/src/Components/Web/src/Forms/InputNumber.cs
Here's my test form:
@page "/"
@using System.ComponentModel.DataAnnotations;
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
<EditForm EditContext=this.editContext>
<DataAnnotationsValidator />
<div class="row my-2">
<div class="col">
<div class="form-label">Value:</div>
<MyInputNumber UpdateOnInput=true class="form-control" @bind-Value=this.model.Value />
<ValidationMessage For="() => this.model.Value" />
</div>
</div>
<div class="row my-2">
<div class="col">
<div class="form-label">First Name:</div>
<InputText class="form-control" @bind-Value=this.model.FirstName />
<ValidationMessage For="() => this.model.FirstName" />
</div>
</div>
<div class="row my-2">
<div class="col">
<div class="form-label">Last Name:</div>
<MyInputText UpdateOnInput=true class="form-control" @bind-Value=this.model.LastName />
<ValidationMessage For="() => this.model.LastName" />
</div>
</div>
<div class="row my-2">
<div class="col">
<div class="alert alert-primary mt-3">
First Name: @this.model.FirstName - Last Name: @this.model.LastName - Value : @this.model.Value
</div>
</div>
</div>
<div class="row my-2">
<div class="col text-end">
<button type="submit" class="btn btn-success">Submit</button>
</div>
</div>
</EditForm>
@code {
private MyModel model = new MyModel { FirstName = "Shaun", LastName="Curtis" };
private EditContext? editContext;
protected override void OnInitialized()
{
editContext = new EditContext(model);
base.OnInitialized();
}
public class MyModel
{
[StringLength(10, ErrorMessage = "Too long (10 character limit).")]
public string FirstName { get; set; } = string.Empty;
[StringLength(10, ErrorMessage = "Too long (10 character limit).")]
public string LastName { get; set; } = string.Empty;
[Range(1, 10, ErrorMessage = "Wrong! (1-10).")]
public int Value { get; set; }
}
}
Based on these you should be able to find the code for the other controls and extend them yourself. Let me know if you run into difficulties and I'll look at the specific control.
Here's an example with the cursor in the Value control (just added 0):
