Angular组件:禁用点击事件

13

我正在创建一个可重复使用的组件,就像这个:

<my-button [isDisabled]="isDisabled" (click)="click($event)"> submit </my-button>
我希望当isDisabled属性为true时禁用点击事件,我尝试了一些东西,但它不起作用。
包/组件/我的按钮.组件.html
<button  [disabled]="isDisabled" #myButton>
        <ng-content></ng-content>
</button>

包/组件/我的按钮.component.ts

@ViewChild('uxButton') uxButton: ElementRef;
@Input() isDisabled: boolean = false;

this.myButton.nativeElement.parentNode.removeEventListener('click' , (e) => {
       e.stopPropagation();
});
10个回答

16

以下的CSS也可以解决这个问题:

# This prevents host to get a direct click
:host {
  pointer-events: none;
}

# This prevents the span inside the button to "steal" the click from the button
:host /deep/ button span {
  pointer-events: none;
}

# Now only the button can get a click 
# Button is the only smart enough to stop propagation when needed
button {
  pointer-events: auto;
}

现在,您不需要像其他答案那样手动传递点击事件:您已经回到了旧的(点击)事件:D

<my-button [isDisabled]="isDisabled" (click)="click($event)"> submit </my-button>
在您的自定义组件中,您只需要传递已禁用的属性:
<button  [disabled]="isDisabled" #myButton>
        <ng-content></ng-content>
</button>

还可以考虑从另一个stackoverflow答案修改的stackblitz


2
请注意,CSS的pointer-events: none将禁用其他鼠标事件。因此,像工具提示这样的东西将不再起作用,因为没有鼠标事件被触发。 - Jacob Roberts

14

试试这样

<button  [disabled]="isDisabled" (click)="btnClick.emit($event)">
        <ng-content></ng-content>
</button>

@Input() isDisabled: boolean = false;
@Output() btnClick = new EventEmitter();

使用Output, 默认情况下,如果按钮被禁用,则按钮单击事件将不起作用。利用它

<my-button [isDisabled]="isDisabled" (btnClick)="click($event)"> submit </my-button>

我想在可重用组件内部完成它。 - Gelso77
我喜欢你的想法,但它不起作用,我在你的代码中看不到任何@output。 - Gelso77
抱歉,我犯了一个错误。已更新答案。btnClick是一个输出。 - Sheik Althaf

6
如果你想在不使用输出的情况下保留传统的点击事件,可以采用基于之前解决方案的综合方法。
my-button.component.html:
<button [disabled]="disabled">
  <ng-content></ng-content>
</button>

my-button.component.ts

export class MyButtonComponent {
  @HostBinding('style.pointer-events') get pEvents(): string {
    if (this.disabled) {
      return 'none';
    }
    return 'auto';
  }

  @Input()
  disabled: boolean = false;

 constructor() {}
}

父组件,您将在其中调用组件,例如app.component.html

<my-button [disabled]="isDisabled" (click)="onClickFnc()">
  <span>Save which not trigger click when button is disabled</span>
</my-button>

5
您可以查看(click)属性来进行验证:
<my-button [isDisabled]="isDisabled" (click)="!isDisabled && click($event)"> submit </my-button>

4
这不是一个好答案。如果你在100个不同的地方使用了你的按钮,那么就需要进行额外的100次检查。 - claudekennilol

3
我能够在scss中禁用禁用按钮的点击事件。这是我添加的内容:
:host {
button {
&:disabled:active {
      pointer-events: none;
}
}
}

1
这可以通过使用布尔变量来检查某个条件是否为真来实现,例如:
<my-button (click)="is_some_condition_true ? null : click($event)"> submit </my-button>

此条件可以是:

在组件中将变量(如editMode、addMode或isDisabled)的初始值设置为false,然后在实际禁用按钮时将其设置为true。

参考:how-to-disabled-click-event-or-any-event-if-condition-is-false-true-in-angular


0

我认为问题出在你的代码中,你有:

<my-button [isDisabled]="isDisabled" (click)="click($event)"> submit </my-button>

应该是这样的

<my-button [disabled]="isDisabled" (click)="click($event)"> submit </my-button>

1
isDisabled 是可重用组件的输入属性。 - Gelso77

0

我们可以使用ElementRef/HostListener根据需要添加/删除事件监听器,但是以下简单的修复方法可能更好。

click(event) {
if (this.isDisabled) {
return;
}
......
}

我必须在可重用组件内完成它,我使用了ElementRef但不起作用。 - Gelso77

0

你应该使用在文档中提到的[disabled]属性:

<button [disabled]="isDisabled" (click)="disableButton()">Disable button</button>

然后在您的代码中

export class AppComponent  {
  isDisabled = false;

  disableButton() {
    this.isDisabled = true;
    // your code...
  }
}

点击StackBlitz查看演示。


我想在可重用组件内完成它。 - Gelso77

0

首先,我们需要问自己为什么要创建一个新的按钮组件类型,当我们已经有本地的按钮组件了。可能是像这样:

  • 利用一些 Angular 助手,如动画
  • 在执行某个点击处理程序期间禁用按钮。
  • 进度指示。
  • ...

如果可以使用本机按钮(解决方案 0)解决要求,请坚持使用。否则,继续进行。

在创建可重复使用的按钮组件之前,我们需要知道两件重要的事情:

K1 只有少量 HTML 元素可以被禁用,这意味着即使元素被禁用,点击处理程序也会触发。https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements

K2 在 Angular 中,外部事件无法从内部进行控制。https://github.com/angular/angular/issues/12630

解决方案 1. 将点击处理程序作为输入

要解决K2的问题,您可以使用@Input回调而不是事件绑定。然后您就可以从内部控制它, 甚至可以在内部访问回调结果。代码示例如下:

<my-button [myClick]="doIt"> or with arguments <my-button [myClick]="doIt.bind(1)">

@HostListener('click') onClick(event) {
  this.myClick();
}

由于您完全控制回调,因此当其处于disabled状态时,您可以省略调用它。

需要这种解决方案的问题是带有进度指示的按钮。当您完全控制回调时,库按钮可以启动/停止进度条的动画,甚至在进行中时通过禁用它来阻止其他点击。与此模块中的进度按钮相比https://github.com/michaeldoye/mat-progress-buttons,您需要为每个按钮实例启动/停止动画!

缺点:外观不标准。您的库用户会问为什么回调是输入而不是事件绑定...

解决方案2. CSS

您可以尝试使用CSS pointer-events:none解决K1。它将在表面上起作用,阻止用户鼠标触发的点击事件。但是,您仍然可以在按钮上进行编程点击。myButton.click()仍然会在按钮处于“禁用”状态时触发。

缺点:'禁用'元素仍然可以点击。可能不适合您的库用户编写自动化测试。

解决方案3. 组件化本地按钮

为了使禁用和事件按预期工作,您需要直接在HTML按钮元素上应用组件。在Angular Material中,它看起来像<button mat-button>https://github.com/angular/components/blob/master/src/material/button/button.ts#L66

而且非常简单:

@Component({
  selector: 'button[my-button]',
  template: '<ng-content></ng-content>'
})

并且使用它:

<button my-button (click)="doIt()" [disabled]="isDisabled">Save</button>

现在当my-button被禁用时,点击事件不会触发。

缺点:必须有原生按钮,并且my-button看起来更像指令而不是组件。

结论

我建议选择最符合要求的解决方案,而不是CSS hack。


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