Suppose I want to use an EditForm
, but I want the value binding to trigger every time the user types into the control instead of just on blur.
Suppose, for the sake of an example, that I want an InputNumber<int>
that does this? I've tried using different means that are floating around such as bind-Value:event="oninput"
with no success. I was finally able to get more or less what I wanted by copying the AspNetCore source code for InputNumber
and overriding/rewriting a few things.
Here is my InputNumber<int>
which accomplishes what I'm hoping for:
public class MyInputNumber: InputNumber<int>
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "input");
builder.AddAttribute(1, "step", "any");
builder.AddMultipleAttributes(2, AdditionalAttributes);
builder.AddAttribute(3, "type", "number");
builder.AddAttribute(4, "class", CssClass);
builder.AddAttribute(5, "value", FormatValue(CurrentValueAsString));
builder.AddAttribute(6, "oninput", EventCallback.Factory.CreateBinder<string>(this, __value => CurrentValueAsString = __value, CurrentValueAsString));
builder.CloseElement();
}
// Copied from AspNetCore - src/Components/Components/src/BindConverter.cs
private static string FormatValue(string value, CultureInfo culture = null) => FormatStringValueCore(value, culture);
// Copied from AspNetCore - src/Components/Components/src/BindConverter.cs
private static string FormatStringValueCore(string value, CultureInfo culture)
{
return value;
}
}
note the "oninput
" in the sequence 6 item was changed from "onchange
" in the base InputNumber
's BuildRenderTree
method. I'd like to know how to:
BuildRenderTree
, so that I can know how to do this with Razor and/orI've gathered from comments in the AspNetCore code that this is definitely not the preferred way of doing this sort of thing, with Razor being the preferred approach. I've tested that this works in .NET Core 3 Preview 7 ASP.NET Core Hosted Blazor by subscribing to the EditContext
's OnFieldChangedEvent
, and can see that with this approach I get the different behavior that I'm looking for. Hopefully there is a better way.
Including some more information about the problem
@using BlazorAugust2019.Client.Components;
@inherits BlazorFormsCode
@page "/blazorforms"
<EditForm EditContext="EditContext">
<div class="form-group row">
<label for="date" class="col-sm-2 col-form-label text-sm-right">Date: </label>
<div class="col-sm-4">
<KlaInputDate Id="date" Class="form-control" @bind-Value="Model.Date"></KlaInputDate>
</div>
</div>
<div class="form-group row">
<label for="summary" class="col-sm-2 col-form-label text-sm-right">Summary: </label>
<div class="col-sm-4">
<KlaInputText Id="summary" Class="form-control" @bind-Value="Model.Summary"></KlaInputText>
</div>
</div>
<div class="form-group row">
<label for="temperaturec" class="col-sm-2 col-form-label text-sm-right">Temperature °C</label>
<div class="col-sm-4">
<KlaInputNumber Id="temperaturec" Class="form-control" @bind-Value="Model.TemperatureC"></KlaInputNumber>
</div>
</div>
<div class="form-group row">
<label for="temperaturef" class="col-sm-2 col-form-label text-sm-right">Temperature °F</label>
<div class="col-sm-4">
<input type="text" id="temperaturef" class="form-control" value="@Model.TemperatureF" readonly />
</div>
</div>
</EditForm>
using BlazorAugust2019.Shared;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using System;
namespace BlazorAugust2019.Client.Pages.BlazorForms
{
public class BlazorFormsCode : ComponentBase
{
public EditContext EditContext;
public WeatherForecast Model;
public BlazorFormsCode()
{
Model = new WeatherForecast()
{
Date = DateTime.Now,
Summary = "Test",
TemperatureC = 21
};
EditContext = new EditContext(Model);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
Console.WriteLine($"EditContext_OnFieldChanged - {e.FieldIdentifier.FieldName}");
}
}
}
What I'm looking for is the "EditContext_OnFieldChanged
" event to fire when I type into the inputs. The current example works, just looking for a better way.
Bind a property or field on other Document Object Model (DOM) events by including an @bind:event="{EVENT}" attribute with a DOM event for the {EVENT} placeholder. The following example binds the InputValue property to the <input> element's value when the element's oninput event ( input ) is triggered.
In Blazor, there is no significant difference between using these two attributes. The @bind attribute is a shorthand of the @bind-value attribute and its delegate will trigger the ValueChanged event of the component.
The input element value is updated by the @bind event in Blazor. To get the current input value, use the oninput native event of the input element and get the current input value.
The EditForm component is used to build a form. The Blazor framework also provides built-in form input components such as InputText, InputSelect, InputDate, InputTextArea, InputCheckbox, and so on. We use the Model attribute to define a top-level model object for the form.
For anyone wondering, you can subclass InputText
to change how it renders. For example, to make it use the oninput
event, create MyInputText.razor containing:
@inherits Microsoft.AspNetCore.Components.Forms.InputText
<input @attributes="@AdditionalAttributes" class="@CssClass" @bind="@CurrentValueAsString" @bind:event="oninput" />
Now when you use <MyInputText @bind-Value="@someval" />
it will behave just like InputText
except it updates on each keystroke.
SteveSanderson
Inheriting from a component and changing it so that it responds to the input
event is now covered in the official documentation for .NET Core 3.1:
InputText based on the input event
Use the
InputText
component to create a custom component that uses theinput
event instead of thechange
event.Create a component with the following markup, and use the component just as
InputText
is used:razor:
@inherits InputText <input @attributes="AdditionalAttributes" class="@CssClass" value="@CurrentValue" @oninput="EventCallback.Factory.CreateBinder<string>( this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
The documentation also gives an example of how to inherit a generic component:
@using System.Globalization @typeparam TValue @inherits InputBase<TValue>
So if you then combine the two of those together you can create a component that changes the behaviour of the InputNumber
component as follows:
@typeparam TNumber @inherits InputNumber<TNumber> <input type="number" step="any" @attributes="AdditionalAttributes" class="@CssClass" value="@CurrentValue" @oninput="EventCallback.Factory.CreateBinder<string>( this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />
Including the type="number"
part will give the same behaviour as the existing InputNumber
(only allowing numeric character entry, using the up and down arrows to increment/decrement etc.)
If you put the code above in a file called say InputNumberUpdateOnInput.razor in the Shared folder of the Blazor project that component can then be used in the same way you'd use a normal InputNumber
, for example:
<InputNumberUpdateOnInput class="form-control text-right" @bind-Value="@invoiceLine.Qty" />
If you want to control the number of decimal places the component will allow you'd need to make the step
value a parameter that you pass into the component. There's more on step
in this answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With