【问题标题】:Click handler is not triggered when target element has been moved移动目标元素时不触发点击处理程序
【发布时间】:2020-08-05 04:30:29
【问题描述】:

考虑以下演示https://stackblitz.com/edit/angular-pur1dt

我有带有同步验证器的反应式表单控制,当它失效时,该字段下方会显示错误消息。

当控件失去焦点时触发验证。在控件下方有一个具有单击处理程序的按钮。问题是当我单击按钮时,控件失去焦点,发生验证,显示错误消息并将按钮向下移动。据说这会阻止点击处理程序执行。有什么建议为什么会发生这种情况以及如何解决这个问题?

我已经用 cmets 更新了演示。注意:只有输入下方的按钮会重现该问题。第一次点击标题不会更新。

【问题讨论】:

  • 您提供的链接似乎按预期工作;两个按钮都按预期调用它们的点击事件。表单验证似乎也按预期工作。
  • 另一种方法是将 [disable] 属性与这样的表单绑定 <button [disabled]="form.invalid" (click)="onClick($event)">click OK</button> 如果表单无效,则按钮将被禁用
  • @joshvito,尝试重新加载每次点击。你会看到
  • @DanielC 这不是我的情况。该按钮与表单提交无关。它只是使控制失去焦点的其他一些组件。
  • 重新加载只会将应用程序重建到其初始状态。我想我没有完全关注你的问题。对不起。我同意@Daniel C,如果您希望在表单无效绑定到按钮的 disabled 属性时禁用按钮

标签: angular angular-reactive-forms


【解决方案1】:

该问题已在 GitHub 上的 issue #7113 中进行了讨论。这是由于在输入字段失去焦点时执行验证,在单击按钮有机会完成并触发click事件之前。

一种可能的解决方法是在单击按钮时使用标志来隐藏消息,如this stackblitz 所示。在下面的代码中,isClicking 标志在mousedown 事件开始时设置,click 事件完成时重置。

<p #errorMsg [hidden]="(errorMsg.hidden && isClicking) || form.controls.name.valid || form.controls.name.untouched ">
    Invalid :)
</p>
<button (mousedown)="onMouseDown($event)" (click)="onClick2($event)">click NOT ok</button>
export class AppComponent {

  isClicking = false;
  ...

  onMouseDown(event) {
    this.isClicking = true;
    setTimeout(() => {
      // The click action began but was not completed after two seconds
      this.isClicking = false;
    }, 2000);
  }

  onClick2(event) {
    console.log(event);
    this.name = "NOT";
    this.isClicking = false;
  }
}

您可以通过将setTimeout 替换为在mousedown 事件处理程序中捕获鼠标的过程来改进该解决方案,并在捕获丢失时重置isClicking。它会考虑到用户在没有完成点击的情况下离开按钮的情况。

【讨论】:

  • 当消息已显示时,我对点击的答案进行了更正。
  • 你可能还可以补充一点,这个问题不是Angular特有的,但也可以用纯JS轻松重现jsfiddle.net/f872ocbb
  • @AngularInDepth.com - Angular 的特殊之处在于验证是在 blur 事件上触发的,并且使用验证是非常标准的(远远超过处理 blur 事件)。这使得问题很可能在该框架中发生。我想知道:Angular 中是否存在无法在纯 Javascript 中重现的问题?
  • 是的,特定于框架的错误,例如 expressionchangedafteritwaschecked
【解决方案2】:

这个问题似乎与触发顺序的 DOM 事件有关

根据 MDN:

当指针设备按钮(通常是 鼠标的主按钮)在单个元素上被按下和释放。

https://developer.mozilla.org/en-US/docs/Web/Events/click

在给定的示例中,元素在您blur 输入的那一刻移动——因为验证会立即发生,显示错误并重新定位按钮。

因此,当鼠标在按钮上方时,鼠标向下,但是当向上时——按钮被重新定位。所以click 不会在按钮上触发

有几种解决方法:

  • mousedown 延迟错误显示
  • 隐藏错误直到mousedownmouseup 都发生,如果mousedown 发生在按钮上

这是一个使用 mousedown 事件处理的示例 https://jsfiddle.net/gjbgqqpo/

希望这会有所帮助:)

【讨论】:

  • 这正是我正在寻找的解释。太好了!
【解决方案3】:

好的,我想我跟着。您不希望在单击下方按钮时发生验证。

触发验证的原因是表单输入上的autofocus。 Angular “默认情况下,每当值更改时,控件的值和有效性都会更新。” See FormControl。控件正在将 name.untouchedfalse 更改为 true

如果您向模板添加一些额外的调试绑定,您可以看到这种情况发生。 例如

<hello name="{{ name }}"></hello>
<form [formGroup]="form">
    <input formControlName="name" autofocus>
    <button (click)="onClick($event)">click OK</button>
</form>
<pre>{{form.controls.name.valid}}</pre>
<pre>{{form.controls.name.untouched}}</pre>
<p [hidden]="form.controls.name.valid || form.controls.name.untouched ">
    Invalid :)
</p>
<button (click)="onClick2($event)">click NOT ok</button>

如果您想在用户单击下方按钮时隐藏错误消息,您应该删除autofocus 或在调用onClick2() 时重置表单的验证。或者,您可以在表单出现错误时显示错误消息。 Angular 说 A control is untouched if the user has not yet triggered a blur event on it. 点击下方按钮会导致模糊事件。

为什么不将错误消息上的模板绑定更改为

<p [hidden]="!form.controls.name.valid || form.controls.name.untouched">
    Invalid :)
</p>

【讨论】:

  • 不,应该触发验证,但也应该执行点击处理程序。它不是
  • 检查onClick2是否第一次(重新加载后)没有执行
  • 对不起,我意识到上一个答案只在初始化加载后才有效:/,所以我编辑了上面的
【解决方案4】:

一个简单的解决方案是将(mousedown)="false" 添加到您的关闭按钮。这种方式不会发生模糊事件,因为您的按钮上的焦点事件将被您的 mousedown 处理程序取消。反过来,这不会将控件标记为已触摸,并且不会显示您的错误。

<button type="button" (click)="dismiss()" (mousedown)="false">Cancel</button>

现在对于您的提交按钮,您不想取消模糊事件,但您可能希望在鼠标按下时运行某种验证逻辑。这样想,如果按钮移动了,那就意味着表单中有错误。当表格出现错误时,您想做什么?就我而言,我真正想做的就是将所有控件标记为已触摸,以便显示所有验证错误。

<button type="submit" (mousedown)="form.markAllAsTouched()">Create</button>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-12
    • 2019-02-18
    • 1970-01-01
    • 2023-02-23
    • 1970-01-01
    • 2011-07-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多