blazor - 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
被触发。当进行了有效的更改,但并未全部输入时 - 用户仍然可以重新编辑已保存的项目。另一方面——如果用户忘记保存更改——这可能会导致表单中的错误被保留而不是被纠正,而用户甚至不知道发生了什么。用户可以关闭浏览器,平板设备可以锁屏。有几次我个人在使用银行应用程序时犯了类似的错误:我以为我已经汇款并等待交货,结果发现我还没有确认转账,商店还在等待我的付款。我认为这是一个主要的烦恼,当它发生在我身上时我不喜欢它,所以我不喜欢它发生在我的应用程序的用户身上。
解决方案
此答案的目的是反驳问题作者本人接受的答案的有效性。
这是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
- 在“名称”字段中键入名称,然后按 Tab 键转到“文本”字段。
- 输入一些文本,但不要按 TAB 键:将输入焦点留在文本字段中。
- 用鼠标指针单击“保存更改”按钮,然后转到输出窗口
- 可以看到,点击按钮已经提交了表单,并打印出文字:“Handle valid submit”
这表明您的断言
然后问题:如果另一个编辑组件有焦点,按钮必须先获得焦点,然后才能点击
这不仅是错误的,而且清楚地揭示了您如何“选择”忽略 Web 和其他地方的 UI 元素的行为方式。
测试 2
在名称字段中输入名称,然后用鼠标指针单击“保存更改”按钮。DataAnnotations 验证立即响应消息:“需要文本字段。”
转到输出窗口,看到消息:“处理无效提交”
返回网页,在文本字段中输入一些文本,然后用鼠标指针单击“保存更改”按钮。
红色短信消失。
现在转到输出窗口,看到没有发出任何消息,这尤其意味着 click 事件从未被触发过。这导致您形成“浏览器本身不将点击视为点击”的错误断言。但是,唉,从来没有点击过“保存更改”按钮。您的解释毫无意义,并且没有任何价值来解释这里发生的事情......
当您将鼠标指针指向“保存更改”按钮并尝试单击它时,文本字段失去焦点,其结果是 DataAnnotations 代码删除了红色消息,并随后重新呈现所涉及的组件。这是以这样的速度完成的,以至于您无法产生在重新渲染之前执行的点击。此代码以光速执行,但即使您足够快(光速,右)在失去焦点到开始重新渲染之间的间隔中插入单击,代码也会以给定的顺序执行. 无论如何,事实仍然是组件被重新渲染,并且您没有成功引发或发出单击事件,因为“保存更改”按钮没有焦点。正如我在测试 1 中所展示的,不需要焦点。
测试 3
- 在名称字段中输入名称,然后用鼠标指针单击“保存更改”按钮。DataAnnotations 验证立即响应消息:“需要文本字段。”
- 转到输出窗口,看到消息:“处理无效提交”
- 返回网页,在文本字段中输入一些文本,然后用鼠标指针将焦点移到名称字段。
如您所见,红色消息已被删除,也就是说,组件的重新渲染已经发生。4. 现在,当焦点位于名称字段时,用鼠标指针单击“保存更改”按钮。5. 转到输出窗口,看到消息:“处理有效提交”
测试 4
添加以下 onmouseover="document.getElementById('name').focus();" 到 EditButton.razor 文件中的按钮元素。这代替了接受的答案中使用的 onmouseover="this.focus()" 作为解决方案。
在名称字段中输入名称,然后用鼠标指针单击“保存更改”按钮。DataAnnotations 验证立即响应消息:“需要文本字段。”
转到输出窗口,看到消息:“处理无效提交”
返回网页,并在文本字段中输入一些文本。现在,当您向“保存更改”按钮的方向移动鼠标时,将触发 mouseover 事件,并将焦点设置到 Name 字段。
现在,当焦点位于名称字段时,用鼠标指针单击“保存更改”按钮。
转到输出窗口,看到消息:“处理有效提交”
请注意,通过将 onmouseover="this.focus()" 与 onmouseover="document.getElementById('name').focus();" 交换来引发行为 是相同的 显然,您不必为了发出单击而设置“保存更改”按钮的焦点。只有在组件被代码相关的 DataAnnotations 刷新后才能单击。
总结:无论您在回答中所说的还是评论都是毫无根据的、错误的和误导性的。我唯一同意的断言是这是一个简单的问题:“你至少犯了两个错误。第一:你误认为这是一个简单的问题”
是的,这是一个简单的问题,鉴于您提供的详细信息,我提供了一个答案,这是正确的,甚至不能被当前答案无效。
请删除您的答案,因为它具有误导性和错误性。它当然不应该被接受。
推荐阅读
- reactjs - redux-saga 总是返回 'undefined'
- javascript - 如何使用 javascript 淡入淡出
- cakephp - cakephp 3 补丁存储额外的列数据
- r - 如何在 R 中编写循环以根据列名创建多个不同的数据子集?
- java - AnysoftKeyboard (Android) 的构建问题
- bash - LDAP 转储的 Bash 脚本数据解析
- python - 用 Scrapy 和 Selenium 抓取 CNN
- pine-script - 每次出现新栏时标签都会重复
- php - 无法在本地 Apache 服务器中打开 PHP webapp。获取错误 404
- python - Keras 创建新的损失函数