如何制作使用oninput而非onchange绑定的EditForm输入

时间:2019-07-27 17:20:34

标签: asp.net-core blazor

假设我要使用EditForm,但我希望每次用户在控件中键入值时触发值绑定,而不是仅在模糊时触发。举例来说,假设我想要一个做到这一点的InputNumber?我尝试使用不同的方式浮动,例如bind-Value:event =“ oninput”,但没有成功。通过复制InputNumber的AspNetCore源代码并覆盖/重写一些东西,我终于能够或多或少地获得所需的东西。这是我的InputNumber,可以实现我所希望的:

    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;
        }
    }

请注意,序列6项中的“ oninput”已从基本InputNumber的BuildRenderTree方法中的“ onchange”更改为。我想知道如何a)看到BuildRenderTree的输出,以便我可以知道如何使用Razor和/或b)大致上知道哪种剃刀语法等效于在未来。我从AspNetCore代码中的注释中收集到,这绝对不是执行此操作的首选方法,而Razor是首选方法。我已经通过订阅EditContext的OnFieldChangedEvent来测试了在.NET Core 3 Preview 7 Asp.net Core托管Blazor中的工作原理,并且可以看到通过这种方法,我得到了所寻找的不同行为。希望有更好的方法。

更新:包括有关该问题的更多信息

@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}");
        }
    }
}

我要寻找的是当我在输入中键入内容时触发的“ EditContext_OnFieldChanged”事件。当前示例有效,只是在寻找更好的方法。

5 个答案:

答案 0 :(得分:5)

对于任何想知道的人,您都可以将InputText子类化以更改其呈现方式。例如,要使其使用oninput事件,请创建包含以下内容的MyInputText.razor:

@inherits Microsoft.AspNetCore.Components.Forms.InputText
<input @attributes="@AdditionalAttributes" class="@CssClass" @bind="@CurrentValueAsString" @bind:event="oninput" />

现在,当您使用<MyInputText @bind-Value="@someval" />时,它的行为就像InputText一样,只是它会在每次击键时更新。

SteveSanderson

答案 1 :(得分:3)

从组件继承并对其进行更改,以使其响应input事件已在official documentation for .NET Core 3.1中涵盖:

基于输入事件的InputText

使用InputText组件创建一个使用input事件而不是change事件的自定义组件。

使用以下标记创建一个组件,并像使用InputText一样使用该组件:

剃刀:

@inherits InputText

<input
    @attributes="AdditionalAttributes" 
    class="@CssClass" 
    value="@CurrentValue" 
    @oninput="EventCallback.Factory.CreateBinder<string>(
        this, __value => CurrentValueAsString = __value, CurrentValueAsString)" />

该文档还提供了an example of how to inherit a generic component

@using System.Globalization
@typeparam TValue
@inherits InputBase<TValue>

因此,如果您随后将两者结合在一起,则可以创建一个更改InputNumber组件行为的组件,如下所示:

@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)" />

包含type="number"部分将具有与现有InputNumber相同的行为(仅允许输入数字字符,使用向上和向下箭头递增/递减等)

如果将上面的代码放在Blazor项目的Shared文件夹中名为InputNumberUpdateOnInput.razor的文件中,则可以使用与使用常规InputNumber相同的方式来使用组件,例如:

<InputNumberUpdateOnInput class="form-control text-right" @bind-Value="@invoiceLine.Qty" />

如果要控制小数位数,则组件将允许您将step值设为传递给组件的参数。 step in this answer上有更多内容。

答案 2 :(得分:1)

尝试一下:

v.12.4.0

更多...

不建议您使用此方法。您应该使用Razor对组件进行子类化。

下面是一个应该工作的示例。它可以重定向您如何获得解决方案。

包装InputText的解决方案:

NewInputText.razor

 <input type="text" id="example1" @bind-value="@value" @bind-value:event="oninput" />

Index.razor

<div class="form-group">
    <label class="col-form-label">@Label</label>
    <InputText Class="form-control" Value="@Value" ValueChanged="@ValueChanged" ValueExpression="@ValueExpression"></InputText>
</div>

@functions{
    [Parameter] string Label { get; set; }
    [Parameter] string Value { get; set; }
    [Parameter] EventCallback<string> ValueChanged { get; set; }
    [Parameter] Expression<Func<string>> ValueExpression { get; set; }
}

您也可以像这样直接从InputBase继承:

<span>Name of the category: @category.Name</span>
<EditForm Model="@category">
    <NewInputText @bind-Value="@category.Name"/>
</EditForm>

希望这对您有帮助...

答案 3 :(得分:1)

好吧,我在仔细研究了源代码之后,特别是在查看了属性CurrentValueAsString和CurrentValue之后,已经弄清楚了。这是我提出的一种解决方案,用于使文本输入正确触发oninput上的字段更改事件:

    public class InputTextCode : InputBase<string>
    {
        protected override bool TryParseValueFromString(string value, out string result, out string validationErrorMessage)
        {
            result = value;
            validationErrorMessage = null;
            return true;
        }
    }
@inherits InputTextCode;

<input type="text" id="@Id" class="@Class" @bind-value="CurrentValueAsString" @bind-value:event="oninput" />

如果这是开箱即用的输入组件上易于配置的选项,那将非常好,但是至少有一种方法可以使我的皮肤不会爬行。

答案 4 :(得分:0)

我发现如果他/她避免使用@bind 而是像这样手动绑定,他/她可以做到这一点:

<InputText Value=@MyValue @oninput=@HandleInputChange ValueExpression=@(() => MyValue) />

@{
  // This will change for every character which is entered/removed from the input
  Public string MyValue { get; set; }

  void HandleInputChange(ChangeEventArgs args)
  {
    MyValue = args.Value.ToString();
  }

}

我仍然不明白为什么会这样。我认为这是因为 AdditionalAttributes 参数。它将 oninput 属性传递给更新您的值的 html <input> 元素,因此您不再依赖 ValueChanged 回调来更新您的值。