在Angular 4中有条件地应用点击事件

66

是否可以在模板中定义一种条件,根据该条件附加点击处理程序?

例如,我能做到的最接近的是在点击方法的入口处评估一个条件。

<a class='user' (click)=" isOverflown? menu.toggle($event): '' "></a>

如果标志isOverflown为假,是否有一种方法可以完全避免绑定到单击事件?

另外,我不想在元素上使用ng-if并复制模板。即:创建一个具有click绑定的元素和创建另一个不带click绑定的元素,然后使用ng-if显示/隐藏它们。


1
没有办法启用/禁用绑定。您可以使用 @ViewChild('.user') aUser:ElementRef; 然后 this.aUser.nativeElement.addEventListener(...);removeEventListener() - Günter Zöchbauer
当您想要避免绑定时,您到底想要达到什么目的?为什么绑定对您是有问题的? - Pac0
我有一个组件,其中点击行为仅在用户为 @Input 之一提供值时才需要。因此,我只知道这两种方法,但我不想使用 ng-if,因为模板太大会重复。所以,我想知道是否有一种声明性的方式,如果条件不满足,则根本不注册处理程序。这解释了我试图做什么吗? - Himanshu Arora
1
只需在方法开头加上 if (!isOverflown) {return},就可以达到相同的效果。 - John Montgomery
3
@Pac0并未讨论具体情况,而是就一般情况而言,事件监听器应该越少越好。您希望尽可能晚地附加它们,尽可能早地取消它们,并且在不需要它们时不使用它们。这不仅关乎监听器方法中处理的最终效果,也关乎监听器方法本身是否被附加。因此,他正在寻找一种不需要它的方式。这是一个好的做法。 - dee zg
显示剩余4条评论
6个回答

119

你可以就像这样做

<a class='user' (click)="isOverflown && menu.toggle($event)"></a>

3
这是一个很好的回答和解决方案。 - Chris Knight
简单而精彩的答案 - fingers10
这样做不还是会向元素添加事件监听器吗? - Wilt
谢谢,对我很有帮助!@Wilt 不,如果初始条件是假的,它不会调用 menu.toggle() - Zaki Mohammed
4
您错了,当您添加了这样的条件时,监听器仍然会被添加,但是当第一个条件为false时,它将永远不会被执行。因此它不会调用该方法,但事件处理程序仍将被绑定。 - Wilt
1
由于@Wilt所说的原因,类似这样的代码不会按预期工作:this.onToggle.observers.length > 0。因为即使它永远不会调用您的方法,监听器仍然会被创建。 - Daniel

18
我建议您编写一个处理程序来执行条件动作,这是我个人认为最简单的方法:
在组件模板中:
<a class='user' (click)="myClickHandler($event)"></a>

在组件代码.ts文件中:

myClickHandler(event): void {
  if (this.isOverflown) {
    this.menu.toggle(event);
  }
}

评论后编辑:如果您真的想避免绑定(我不明白为什么,但无论如何),您可以使用*ngIf创建一个条件组件:


<a class='user' *ngIf="isOverflown" (click)="menu.toggle($event)"></a>
<a class='user' *ngIf="!isOverflown"></a>

是的,这和我在模板中所做的一样好。但我想知道是否可以避免绑定到该方法。 - Himanshu Arora
在这种情况下,您可以使用 *ngIf。我会编辑我的答案。 - Pac0
哈哈,我刚刚编辑了问题,我不想在模板中重复一个大元素。我放的例子很简单,实际上,我的模板非常复杂。 - Himanshu Arora
嗯,我之前不知道这个。现在我还不知道任何答案。我可以问一下为什么你不想要绑定吗?这似乎是 Angular 的最佳实践。这听起来像是一个 XY 问题。 - Pac0
所以,看起来这是唯一两种干净的方式。 - Himanshu Arora
我有一个组件,其中点击行为仅在用户为 @Input 之一提供值时才需要。因此,我只知道这两种方法,但我不想使用 ng-if,因为模板太大会重复。所以,我想知道是否有一种声明性的方式,如果条件不满足,则根本不注册处理程序。 - Himanshu Arora

18

无法启用/禁用绑定。

可以通过命令式的方式实现。

@ViewChild('.user') aUser:ElementRef; 

clickHandler(event) {
  console.log(event);
}
_clickHandler = this.clickHandler.bind(this);

ngAfterViewInit() {
  this.aUser.nativeElement.addEventListener('click', this._clickHandler); 
}  

取消订阅请使用

this.aUser.nativeElement.removeEventListener('click', this._clickHandler); 

另请参见动态添加事件监听器


你的代码中removeEventListener无法工作,因为bind方法会创建新的函数。 - yurzui
Renderer.listen 返回一个函数,我们可以调用它来取消订阅。 - yurzui
好的,听起来不错。你认为我的更新答案可行吗? - Günter Zöchbauer

12

我遇到了类似的问题,以下方法对我有用:

<p (click)="item.isClickable ? item.onClick() : return;">
 Hello Mom!
</p>

你仍然在 p 标签上添加了点击事件。 - Himanshu Arora

7
只需使用三元操作符绑定 null,并添加 disabled 类即可解决我的问题。
<a (click)="item.quantity>1 ? decreaseQuantity():null;" [ngClass]="{'disabled': item.quantity<=1}">

Bootstrap的disabled类已经可以防止点击事件,不需要使用Angular来阻止点击。尽管这是最不流行的答案,但只要项目中存在Bootstrap,这种方法就是解决问题最简单的方式。 - Halil

4

2
似乎没有办法使用Renderer.listen来移除事件处理程序,也许可以使用invokeElementMethod(this.elementRef.nativeElement, 'addEventListener', ['click', this.clickHandler.bind(this)]) - Günter Zöchbauer
Yurziu在我的回答下面进行解释。 - Günter Zöchbauer
是的,是的,您只需调用由listen返回的函数。有一个附加项,现在有一个Renderer2。 - dee zg
我已经看到 Renderer2 几个月了,但从未看到任何解释为什么它要在 Renderer 之外添加。 - Günter Zöchbauer
1
如果我们要数一下文档/代码库中缺乏解释的所有内容,那将会是一个漫长的、漫长的夜晚 ;) - dee zg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接