Blazor 服务器 - EditForm 提交 - 单击两个按钮即可聚焦和提交?

问题描述

所以我对某些提交逻辑在 Blazor Server 中的工作方式感到有些困惑(可能与 Blazor WebAssembly 的情况相同)。

我有一个带有字段和提交按钮(提交类型)的 EditForm:

    <EditForm EditContext="@_editContext" OnValidSubmit="@HandleValidSubmit">
        <DataAnnotationsValidator />
        <ValidationSummary />

        <label for="firstName" class="form-label">First Name</label>
        <InputText class="form-control" @bind-Value="_formModel.FirstName" />
        <button type="submit" class="btn btn-primary">New Person</button>

    </EditForm>


    @code {
    
    private void HandleValidSubmit()
    {
       System.Diagnostics.Debug.WriteLine("SUBMIT!");
    }
    
    private class FormModel
    {
        [required(AllowEmptyStrings = false,ErrorMessage = "Provide a first name")]
        public string FirstName{ get; set; }

    }
    
}

在浏览器中,我执行以下操作:

  1. 提供将根据我的 FormModelDataAnnotationsValidator 评估为无效的输入,单击按钮即可告知我。这是意料之中的。
  2. 我更正了名字文本输入的值,并且它仍然在浏览器中获得焦点,我点击 Submit
  3. 所有发生的事情是 DataValidation 似乎已经运行(很酷,这总是好的),但是单击 Submit 按钮似乎从未触发我运行 OnValidSubmit 的 C# 代码。 (如您在我的示例中所见,我使用 Debug.WriteLine

有没有办法在使用 OnSubmit/OnInvalidSubmit/OnValidSubmit 和 type="submit" 的按钮保持 EditForm 的同时避免这种情况?

(我读到在 Blazor 和 State 更新中使用 submit 类型的按钮与 button 类型的按钮有一点不同……不知道这是否是这里的一个因素)

用户的角度来看,他们为一次操作按了两次按钮。这当然会让人感觉有点奇怪或令人困惑。

从我的测试来看,这似乎是涉及 DataAnnotationsValidator 的情况,这部分是有意义的,直到需要在客户端上按下两个按钮才能实际点击我的 C# 代码。 (即我知道当焦点改变时会发生数据验证)

我觉得我部分误解了一些东西,部分缺少一些额外的代码或表单中的设置以避免这种情况发生。

有没有办法在保留 DataAnnotationsValidator 的同时做我想做的事情?

在此先感谢您的帮助!

解决方法

我认为发生的事情是当您“单击”按钮(显示验证消息)时,首先发生的是输入控件失去焦点。这会导致验证事件 - 数据条目通过验证,ValidationSummary 更新并消失。渲染事件将按钮分流到页面上,因此当浏览器应用单击事件时,它位于屏幕的空白处,而不是按钮上。在下面的测试代码中,我在 ValidationSummary 上方添加了一个提交按钮,它可以工作,因为它不会移动。它发生得如此之快,你会错过它!

对此的一种解决方法是更改​​输入的行为以验证数据输入而不是失去焦点。

这是一个自定义 InputText 控件:

@namespace MyNameSpace
@inherits InputText

<input type="text" value="@this.CurrentValue" @oninput="OnInput" @ref="Element" />

@code{

    public ElementReference? Element { get; set; }

    private void OnInput(ChangeEventArgs e)
    => this.CurrentValueAsString = e.Value.ToString();
}

它和原来的唯一区别在于响应事件现在设置为“oninput”而不是“onchange”。

测试页面如下所示。两个版本的控件都会显示出来,因此您可以看到不同的行为。

@page "/Test2"
<EditForm EditContext="@_editContext" OnValidSubmit="@HandleValidSubmit" OnInvalidSubmit="HandleInValidSubmit">

    <DataAnnotationsValidator />

    <div class=" m-2 p-2">
        <button type="submit" class="btn btn-primary">New Person</button>
    </div>

    <ValidationSummary />

    <label class="form-label">First Name (Only Updates on Exit)</label>
    <InputText class="form-control" @bind-Value="this._formModel.FirstName"></InputText>

    <label class="form-label">First Name (Updates on Typing)</label>
    <InputTextOnInput class="form-control" @bind-Value="this._formModel.FirstName"></InputTextOnInput>

    <button type="submit" class="btn btn-primary">New Person</button>

</EditForm>

<div class="m-3 p-3">@_submitType</div>

@code {
    private EditContext? _editContext;
    private FormModel _formModel;
    private string _submitType;

    private void HandleValidSubmit()
    {
        _submitType = "VALID SUBMIT!";
        System.Diagnostics.Debug.WriteLine("VALID SUBMIT!");
    }

    private void HandleInValidSubmit()
    {
        _submitType = "INVALID SUBMIT!";
        System.Diagnostics.Debug.WriteLine("INVALID SUBMIT!");
    }

    private class FormModel
    {
        [Required(AllowEmptyStrings = false,ErrorMessage = "Provide a first name")]
        public string FirstName { get; set; }

    }

    protected override Task OnInitializedAsync()
    {
        _formModel = new FormModel();
        _editContext = new EditContext(_formModel);
        return base.OnInitializedAsync();
    }

}

Here's the link to the source code for the input controls