Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to make an EditForm Input that binds using oninput rather than onchange?

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:

  1. see the output of BuildRenderTree, so that I can know how to do this with Razor and/or
  2. just kind of know in general what sort of Razor syntax would be equivalent to doing this in the future.

I'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.

Update

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 &deg;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 &deg;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.

like image 252
Jakotheshadows Avatar asked Jul 27 '19 17:07

Jakotheshadows


People also ask

How do you bind a value in Blazor?

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.

What is the difference between bind and bind value in Blazor?

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.

How do you take input in Blazor?

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.

What is EditForm in Blazor?

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.


2 Answers

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

like image 92
enet Avatar answered Sep 18 '22 17:09

enet


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 the input event instead of the change 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.

like image 44
tomRedox Avatar answered Sep 17 '22 17:09

tomRedox