如何使输入组件验证输入并调用指定的回调?

How to make an input component validate input and call a specified callback?

我想要这样的东西:

<input id=filterString type="number" class="form-control" value="@FilterModel.FilterString" @oninput="@FilterChanged" />

InputNumber, to make use of validation.

到目前为止,我遇到的最简单和最接近的解决方案(也许?)可能是创建 InputBase 的子类的方法,将指定的回调作为参数传递,并同时调用 InputBase 的 ValueChanged 被调用。类似于 this post.

我无法让它工作(我不记得具体的错误,但如果这个方法被推荐,以后可以找回它们),在反复排除故障后,它开始感觉笨拙。我已经搜索了文档,但仍然不明白验证系统和绑定是如何组合在一起的。所以我有点迷路了,如果能帮我整理一下,我将不胜感激。

总结一下:

  1. 我想知道实现 InputNumber(或等效)的最简单方法,我还可以指定一个 OnInput(或等效)回调,以便在值更改时执行一些工作.
  2. 如果有人可以简要地 clarify/describe 更改输入时发生的事件序列(即从用户与输入交互开始的“管道”看起来像什么),那也很好.特别是,when/where InputBase 是否进行验证? (我想我真的很困惑 InputBase(和 children)和验证是如何工作的,所以我在扩展它们时遇到了麻烦)。

谢谢,非常感谢大家的帮助。

您可以创建自己的组件并接收 EditContext 类型的级联参数 - 然后您可以使用该参数调用验证,并获取您的字段的任何验证消息。

现在您已经完全控制了输入,您可以连接到它的 @oninput 方法并完成您的工作(引发其他事件,做更多的逻辑,调用编辑的 .Validate() 方法上下文)。

您可能需要添加更多参数以允许值表达式,可能是类型参数,可能是字段名称的标识符,诸如此类,以使其更具可重用性和通用性,但核心是获得编辑上下文并控制您自己的渲染和事件。

编辑:添加示例 - 请注意,这只是一种实现,既不通用,也不完美

main component with the form

@using System.ComponentModel.DataAnnotations

<EditForm Model="@TheModel">
    <DataAnnotationsValidator></DataAnnotationsValidator>
    @TheModel.MyProperty
    <br />
    <MyNumberInput @bind-Value="@TheModel.MyProperty"
                   MySpecialEvent="@MySpecialEventHandler"
                   FieldId="@( new FieldIdentifier(TheModel, nameof(MyFormModel.MyProperty)) )" />

    <ValidationMessage For="@( () => TheModel.MyProperty )"></ValidationMessage>
</EditForm>

@code{
    MyFormModel TheModel { get; set; } = new MyFormModel();

    async Task MySpecialEventHandler(DateTime time)
    {
        Console.WriteLine($"special event fired at: {time.Millisecond}");
    }

    public class MyFormModel
    {
        [Required]
        [Range(3, 5)]
        public int? MyProperty { get; set; }
    }
}

child component - MyNumberInput

@implements IDisposable

<input type="number" @oninput="@OnInputHandler" value="@Value" />

@code {
    [CascadingParameter]
    public EditContext TheEditContext { get; set; }
    [Parameter]
    public FieldIdentifier FieldId { get; set; }
    [Parameter]
    public int? Value { get; set; }
    [Parameter]
    public EventCallback<int?> ValueChanged { get; set; }
    [Parameter]
    public EventCallback<DateTime> MySpecialEvent { get; set; }

    async Task OnInputHandler(ChangeEventArgs e)
    {
        try
        {
            int val = int.Parse(e.Value.ToString());
            Value = val;
        }
        catch
        {
            Value = null;
        }

        //twoway binding
        await ValueChanged.InvokeAsync(Value);
        //update validation - AFTER the value is saved in the form
        TheEditContext.NotifyFieldChanged(FieldId);
        //your own event as needed
        await MySpecialEvent.InvokeAsync(DateTime.Now);
    }

    //some general update that may not be needed but might help if other fields update this one and you need to rerender
    void ValidationStateChanged(object sender, ValidationStateChangedEventArgs e)
    {
        StateHasChanged();
    }

    protected override void OnInitialized()
    {
        if (TheEditContext != null)
            TheEditContext.OnValidationStateChanged += ValidationStateChanged;
    }

    public void Dispose()
    {
        if (TheEditContext != null)
            TheEditContext.OnValidationStateChanged -= ValidationStateChanged;
    }
}


事件的顺序是这样的

  1. 用户键入一个字符
  2. 输入组件在写入时做出反应(通常可以是 oninput 或 onchange DOM 事件 - 顺便说一句,标准组件使用 onchange)
  3. 编辑上下文收到字段更改通知并调用验证 - 它作为级联参数出现