首页 > 解决方案 > Blazor:如何通过单击按钮提交表单

问题描述

<EditForm autocomplete="off" class="contex card-body" Model="Input" OnValidSubmit="OnSaveChangesAsync">
  <!-- all fields and validations -->
  <EditButton>Save changes</EditButton> 
</EditForm>

我有这样的表格。它执行许多验证。然后保存更改。几乎一切正常。

问题是第一次单击按钮什么都不做(似乎使按钮处于活动状态),第二次单击实际上提交了表单。当然它可能专注于表单元素,但这不应该阻止按钮正常工作。

如何使按钮在第一次单击时起作用,而不是双击/第二次单击?

编辑:编辑按钮源:

EditButton.razor:

@namespace Woof.Blazor.Components
<button type="submit" class="@CssClass" @attributes="AdditionalAttributes">@ChildContent</button>

EditButton.razor.cs:

using System;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.AspNetCore.Components;

namespace Woof.Blazor.Components {

    public partial class EditButton {

        [Parameter] public RenderFragment ChildContent { get; set; }

        /// <summary>
        /// Gets or sets a collection of additional attributes that will be applied to the created element.
        /// </summary>
        [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }

        string CssClass {
            get {
                const string defaultClass = "btn btn-primary baseline";
                if (AdditionalAttributes != null &&
                    AdditionalAttributes.TryGetValue("class", out var @class) &&
                    !string.IsNullOrEmpty(Convert.ToString(@class, CultureInfo.InvariantCulture))) {
                    return $"{defaultClass} {@class} ";
                } else return defaultClass;
            }
        }

    }

}

让我重新表述这个问题以使其更清楚:我怀疑第一次单击按钮只会使按钮获得焦点(将焦点从输入元素中取出),第二次单击被注册为“提交”操作。我想跳过聚焦部分并第一次点击通话OnValidSubmit EventCallback。当触摸按钮时,它在平板电脑上以这种方式工作也很重要。一触即可保存更改。意外点击按钮的后果几乎没有。如果没有对表单进行任何更改,我的代码将跳过更新。如果进行了无效更改 - 验证将不允许OnValidSubmit被触发。当进行了有效的更改,但并未全部输入时 - 用户仍然可以重新编辑已保存的项目。另一方面——如果用户忘记保存更改——这可能会导致表单中的错误被保留而不是被纠正,而用户甚至不知道发生了什么。用户可以关闭浏览器,平板设备可以锁屏。有几次我个人在使用银行应用程序时犯了类似的错误:我以为我已经汇款并等待交货,结果发现我还没有确认转账,商店还在等待我的付款。我认为这是一个主要的烦恼,当它发生在我身上时我不喜欢它,所以我不喜欢它发生在我的应用程序的用户身上。

标签: blazorblazor-server-side

解决方案


此答案的目的是反驳问题作者本人接受的答案的有效性。

这是OP发布的问题中的代码...

编辑按钮.razor

@namespace Woof.Blazor.Components
<button type="submit" class="@CssClass" @attributes="AdditionalAttributes">@ChildContent</button>

编辑按钮.razor.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Globalization;
using Microsoft.AspNetCore.Components;

namespace Woof.Blazor.Components
{

    public partial class EditButton
    {

        [Parameter] public RenderFragment ChildContent { get; set; }

        /// <summary>
        /// Gets or sets a collection of additional attributes that will be applied to the created element.
        /// </summary>
        [Parameter(CaptureUnmatchedValues = true)] public IReadOnlyDictionary<string, object> AdditionalAttributes { get; set; }

        string CssClass
        {
            get
            {
                const string defaultClass = "btn btn-primary baseline";
                if (AdditionalAttributes != null &&
                    AdditionalAttributes.TryGetValue("class", out var @class) &&
                    !string.IsNullOrEmpty(Convert.ToString(@class, CultureInfo.InvariantCulture)))
                {
                    return $"{defaultClass} {@class} ";
                }
                else return defaultClass;
            }
        }

    }
}

以下是我用于测试目的的代码。将其复制到索引页面并运行应用程序...请注意,我正在使用 Blazor Server 应用程序。

@page "/"

    @using System
    @using System.Collections.Generic
    @using System.Linq
    @using System.Threading.Tasks
    @using System.ComponentModel.DataAnnotations
    @using Woof.Blazor.Components


    <EditForm Model="@Model" OnValidSubmit="@HandleValidSubmit" 
                                OnInvalidSubmit="@HandleInvalidSubmit">
   
        <DataAnnotationsValidator />
        <ValidationSummary />

        <div class="form-group">
            <label for="name">Name: </label>
            <InputText autocomplete="off" Id="name" Class="form-control" 
                                 @bind-Value="@Model.Name"></InputText>
            <ValidationMessage For="@(() => Model.Name)" />
        </div>
        <div class="form-group">
            <label for="body">Text: </label>
            <InputTextArea autocomplete="off" Id="body" Class="form-control" 
                                  @bind-Value="@Model.Text"></InputTextArea>
            <ValidationMessage For="@(() => Model.Text)" />
        </div>
        <EditButton>Save changes</EditButton>

    </EditForm>

    @code
    {
        private Comment Model = new Comment();

        protected void HandleValidSubmit()
        {
             Console.WriteLine("Handle valid submit");
        }

        protected void HandleInvalidSubmit()
        {
            Console.WriteLine("Handle Invalid Submit");
        }

        public class Comment
        {
            [Required]
            [MaxLength(10)]
            public string Name { get; set; }

            [Required]
            public string Text { get; set; }

        }
    }

测试 1

  1. 在“名称”字段中键入名称,然后按 Tab 键转到“文本”字段。
  2. 输入一些文本,但不要按 TAB 键:将输入焦点留在文本字段中。
  3. 用鼠标指针单击“保存更改”按钮,然后转到输出窗口
  4. 可以看到,点击按钮已经提交了表单,并打印出文字:“Handle valid submit”

这表明您的断言

然后问题:如果另一个编辑组件有焦点,按钮必须先获得焦点,然后才能点击

这不仅是错误的,而且清楚地揭示了您如何“选择”忽略 Web 和其他地方的 UI 元素的行为方式。

测试 2

  1. 在名称字段中输入名称,然后用鼠标指针单击“保存更改”按钮。DataAnnotations 验证立即响应消息:“需要文本字段。”

  2. 转到输出窗口,看到消息:“处理无效提交”

  3. 返回网页,在文本字段中输入一些文本,然后用鼠标指针单击“保存更改”按钮。

  4. 红色短信消失。

  5. 现在转到输出窗口,看到没有发出任何消息,这尤其意味着 click 事件从未被触发过。这导致您形成“浏览器本身不将点击视为点击”的错误断言。但是,唉,从来没有点击过“保存更改”按钮。您的解释毫无意义,并且没有任何价值来解释这里发生的事情......

    当您将鼠标指针指向“保存更改”按钮并尝试单击它时,文本字段失去焦点,其结果是 DataAnnotations 代码删除了红色消息,并随后重新呈现所涉及的组件。这是以这样的速度完成的,以至于您无法产生在重新渲染之前执行的点击。此代码以光速执行,但即使您足够快(光速,右)在失去焦点到开始重新渲染之间的间隔中插入单击,代码也会以给定的顺序执行. 无论如何,事实仍然是组件被重新渲染,并且您没有成功引发或发出单击事件,因为“保存更改”按钮没有焦点。正如我在测试 1 中所展示的,不需要焦点。

测试 3

  1. 在名称字段中输入名称,然后用鼠标指针单击“保存更改”按钮。DataAnnotations 验证立即响应消息:“需要文本字段。”
  2. 转到输出窗口,看到消息:“处理无效提交”
  3. 返回网页,在文本字段中输入一些文本,然后用鼠标指针将焦点移到名称字段。

如您所见,红色消息已被删除,也就是说,组件的重新渲染已经发生。4. 现在,当焦点位于名称字段时,用鼠标指针单击“保存更改”按钮。5. 转到输出窗口,看到消息:“处理有效提交”

测试 4

  1. 添加以下 onmouseover="document.getElementById('name').focus();" 到 EditButton.razor 文件中的按钮元素。这代替了接受的答案中使用的 onmouseover="this.focus()" 作为解决方案。

  2. 在名称字段中输入名称,然后用鼠标指针单击“保存更改”按钮。DataAnnotations 验证立即响应消息:“需要文本字段。”

  3. 转到输出窗口,看到消息:“处理无效提交”

  4. 返回网页,并在文本字段中输入一些文本。现在,当您向“保存更改”按钮的方向移动鼠标时,将触发 mouseover 事件,并将焦点设置到 Name 字段。

  5. 现在,当焦点位于名称字段时,用鼠标指针单击“保存更改”按钮。

  6. 转到输出窗口,看到消息:“处理有效提交”

  7. 请注意,通过将 onmouseover="this.focus()" 与 onmouseover="document.getElementById('name').focus();" 交换来引发行为 是相同的 显然,您不必为了发出单击而设置“保存更改”按钮的焦点。只有在组件被代码相关的 DataAnnotations 刷新后才能单击。

总结:无论您在回答中所说的还是评论都是毫无根据的、错误的和误导性的。我唯一同意的断言是这是一个简单的问题:“你至少犯了两个错误。第一:你误认为这是一个简单的问题”

是的,这是一个简单的问题,鉴于您提供的详细信息,我提供了一个答案,这是正确的,甚至不能被当前答案无效。

请删除您的答案,因为它具有误导性和错误性。它当然不应该被接受。


推荐阅读