【问题标题】:How to make an EditForm Input that binds using oninput rather than onchange?如何制作使用 oninput 而不是 onchange 绑定的 EditForm 输入?
【发布时间】:2019-12-05 15:34:42
【问题描述】:

假设我想使用EditForm,但我希望每次用户键入控件时触发值绑定,而不是仅仅在模糊时触发。

假设,为了一个例子,我想要一个 InputNumber<int> 来做这个?我试过使用不同的方法,比如bind-Value:event="oninput",但没有成功。通过复制 InputNumber 的 AspNetCore 源代码并覆盖/重写一些内容,我终于能够或多或少地获得我想要的东西。

这是我的InputNumber<int>,它实现了我的期望:

    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”已从基本 InputNumberBuildRenderTree 方法中的“onchange”更改。我想知道如何:

  1. 查看BuildRenderTree 的输出,以便我知道如何使用 Razor 和/或
  2. 只是大致了解哪种 Razor 语法相当于将来执行此操作。

我从 AspNetCore 代码中的 cmets 收集到,这绝对不是做这类事情的首选方法,Razor 是首选方法。我已经通过订阅 EditContextOnFieldChangedEvent 测试了这在 .NET Core 3 Preview 7 ASP.NET Core Hosted 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”事件。当前示例有效,只是在寻找更好的方法。

【问题讨论】:

  • 当您说“当前示例有效”时,您的意思是在您输入输入时触发了“EditContext_OnFieldChanged”事件吗?我认为无法触发“EditContext_OnFieldChanged”事件,因为没有代码触发 InputBase 中定义的 ValueChanged 委托
  • 我不确定它发生在哪里或为什么发生,但它正在发生。是的,Console.WriteLine 被击中,我可靠地获得了更改的字段的名称。我不知道,也许它在我正在扩展的派生类 InputNumber、InputText 等中。

标签: c# .net asp.net-core blazor


【解决方案1】:

试试这个:

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

更多...

不推荐您的方法。您应该使用 Razor 子类化您的组件。

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

包装InputText的解决方案:

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

Index.razor

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

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

 @inherits InputBase<string>
 <input type="number" bind="@CurrentValue" id="@Id" class="@CssClass" />

希望这会有所帮助...

【讨论】:

  • 我不禁注意到您的每个不同的解决方案都只是解决方案的一半。在某些时候,我想从 InputBase 派生,因为我的假设是在 EditForm 中使用是必需的(以及开箱即用的 EditContext 之类的东西),但我也想使用 OnInput。您是否有经过测试的解决方案,在输入输入时会触发 EditContext.OnFieldChanged 事件?另请注意,我使用的是 EditContext 而不是 Model 参数。
  • 你试过第一个解决方案了吗?
  • 我做了,但它不像我在 OP 中指定的那样工作。
  • 除非你指的是我根本不制作组件的 解决方案。这行不通,因为我出于其他原因需要编辑上下文。编辑:做了测试,也没有按预期工作
  • 更新了 OP 以阐明我在寻找什么。
【解决方案2】:

好的,在查看源代码特别是查看属性 CurrentValueAsString 和 CurrentValue 之后,我发现了这一点。这是我提出的用于制作文本输入的解决方案,该解决方案可以在输入上正确触发字段更改事件:

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

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

【讨论】:

  • 你有没有找到任何可配置/更简单的方法?
【解决方案3】:

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

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

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

史蒂夫·桑德森

【讨论】:

  • 太棒了!我不知道!我想知道继承现有组件是否会使用您编写的 Razor 标记覆盖其 BuildRenderTree 方法
  • 抱歉,我不知道这条评论...是的,“继承现有组件会使用您编写的 Razor 标记覆盖其 BuildRenderTree 方法”。事实上,这是推荐的方式:使用现有逻辑,同时提供不同的视图。
  • 执行此操作后,我收到了System.ArgumentException: Object of type 'Microsoft.AspNetCore.Components.Web.KeyboardEventArgs' cannot be converted to type 'Microsoft.AspNetCore.Components.ChangeEventArgs'.
  • 如果我的输入与其他代码放在一起,是否应该将@inherits 和输入抽象为一个单独的组件?还是@inherits 只适用于下一行?
  • 也不清楚从InputText继承有什么好处,而我可以使用具有相同效果的纯html输入。
【解决方案4】:

从组件继承并对其进行更改以使其响应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 上还有更多内容。

【讨论】:

    【解决方案5】:

    我发现如果他/她避免使用@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 &lt;input&gt; 元素,因此您不再依赖ValueChanged 回调来更新您的值。

    【讨论】:

      猜你喜欢
      • 2020-05-30
      • 2020-07-07
      • 1970-01-01
      • 2021-01-18
      • 1970-01-01
      • 2023-02-09
      • 2011-02-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多