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

Blazor 如何使输入组件验证输入并调用指定的回调?,blazor,blazor-webassembly,Blazor,Blazor Webassembly,我想要一些像: <input id=filterString type="number" class="form-control" value="@FilterModel.FilterString" @oninput="@FilterChanged" /> 但是,要利用 到目前为止,我遇到的最简单和最接近的解决方案(也许?)可能是创建InputBase子类的方法,将指定的回调作为参数传递,并在调用I

我想要一些像:

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

但是,要利用

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

我无法让它工作(我无法具体回忆错误,但如果推荐使用这种方法,我可以在以后检索它们),并且在反复排除故障后,它开始感觉笨拙。我浏览了文档,但仍然不明白验证系统和绑定是如何结合在一起的。所以我有点迷路了,希望能有人帮我整理一下

总而言之:

  • 我想知道实现
    InputNumber
    (或等效)的最简单方法,其中我还可以指定
    OnInput
    (或等效)回调,以便在值更改时执行一些工作
  • 如果有人能简单地澄清/描述输入更改时发生的事件顺序(即,从用户与输入交互开始,“管道”看起来是什么样子),那也很好。特别是,InputBase何时/何地进行验证?(我想我真的很困惑InputBase(和子项)和验证是如何工作的,所以我很难扩展它们)

  • 谢谢,非常感谢您的帮助。

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

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

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

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

    与表单一起使用的主要组件

    @使用System.ComponentModel.DataAnnotations
    @model.MyProperty
    
    @代码{ MyFormModel模型{get;set;}=new MyFormModel(); 异步任务MySpecialEventHandler(日期时间) { WriteLine($“在:{time.millis秒}触发的特殊事件”); } 公共类MyFormModel { [必需] [射程(3,5)] 公共int?MyProperty{get;set;} } }
    子组件-MyNumberInput

    @实现IDisposable
    @代码{
    [CascadingParameter]
    公共EditContext TheEditContext{get;set;}
    [参数]
    公共字段标识符FieldId{get;set;}
    [参数]
    公共int?值{get;set;}
    [参数]
    public EventCallback ValueChanged{get;set;}
    [参数]
    public EventCallback MySpecialEvent{get;set;}
    InPurtHandler上的异步任务(ChangeEventArgs e)
    {
    尝试
    {
    int val=int.Parse(例如Value.ToString());
    值=val;
    }
    抓住
    {
    值=空;
    }
    //双向装订
    wait ValueChanged.InvokeAsync(值);
    //更新验证-值保存在表单中后
    TheEditContext.NotifyFieldChanged(FieldId);
    //根据需要选择您自己的活动
    等待MySpecialEvent.InvokeAsync(DateTime.Now);
    }
    //可能不需要一些常规更新,但如果其他字段更新此字段并且您需要重新提交,则可能会有所帮助
    无效ValidationStateChanged(对象发送方,ValidationStateChangedEventArgs e)
    {
    StateHasChanged();
    }
    受保护的覆盖无效OnInitialized()
    {
    如果(TheEditContext!=null)
    TheEditContext.OnValidationStateChanged+=ValidationStateChanged;
    }
    公共空间处置()
    {
    如果(TheEditContext!=null)
    TheEditContext.OnValidationStateChanged-=ValidationStateChanged;
    }
    }
    

    事件的顺序有点像

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

  • 哇,你真是个飞天高手。我也在这里。嗯,你能给我发个代码示例吗?我很难想象如何把它组合起来。感谢您澄清事件的顺序。我为您制作了一个,帖子是edited@rdmptnAFAICT此方法专门用于使
    EditForm
    (或只是一般的表单)更容易、更方便地实现、自定义和扩展等。。不幸的是,我认为这可能不是我想要的东西(使用表格),因为我的输入不打算在填写后立即提交,而是在每次更改后立即使用;我认为对于这一点,输入字段(而不是表单)才是工作的正确“工具”。我说得对吗?思维方式不是我在这里的正确工具?如果是这样的话,是否可以在没有
    EditForm
    的情况下执行此操作?您最初的帖子讨论了InputNumber和其他与表单相关的组件,因此我认为您需要它。如果你没有,可以放下所有这些,直接处理
    @oninput
    。只需更新视图模型-您仍然需要该值,否则每次击键都会丢失该值,用户将无法输入多个数字的数字。
    @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; }
        }
    }
    
    @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;
        }
    }