如何制作使用 oninput 而不是 onchange 绑定的 EditForm 输入?
Posted
技术标签:
【中文标题】如何制作使用 oninput 而不是 onchange 绑定的 EditForm 输入?【英文标题】:How to make an EditForm Input that binds using oninput rather than onchange? 【发布时间】: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
”已从基本 InputNumber
的 BuildRenderTree
方法中的“onchange
”更改。我想知道如何:
-
查看
BuildRenderTree
的输出,以便我知道如何使用 Razor 和/或
只是大致了解哪种 Razor 语法相当于将来执行此操作。
我从 AspNetCore 代码中的 cmets 收集到,这绝对不是做这类事情的首选方法,Razor 是首选方法。我已经通过订阅 EditContext
的 OnFieldChangedEvent
测试了这在 .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 °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");
我正在寻找的是在我输入输入时触发的“EditContext_OnFieldChanged
”事件。当前示例有效,只是在寻找更好的方法。
【问题讨论】:
当您说“当前示例有效”时,您的意思是在您输入输入时触发了“EditContext_OnFieldChanged”事件吗?我认为无法触发“EditContext_OnFieldChanged”事件,因为没有代码触发 InputBase试试这个:
<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" />
现在,当您使用 <MyInputText @bind-Value="@someval" />
时,它的行为就像 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 <input>
元素,因此您不再依赖ValueChanged
回调来更新您的值。
【讨论】:
以上是关于如何制作使用 oninput 而不是 onchange 绑定的 EditForm 输入?的主要内容,如果未能解决你的问题,请参考以下文章
html 范围滑块 - oninput 在 IE 11 中不起作用