Angular 组件:禁用点击事件

Angular component: disable click event

我正在创建一个像这样的可重用组件:

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

我想在 属性 isDisabled 为 true 时禁用点击事件,我尝试过类似的方法但它不起作用。

packages/component/my-button.component.html

<button  [disabled]="isDisabled" #myButton>
        <ng-content></ng-content>
</button>

packages/component/my-button.component.ts

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

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

我们可以addeventlistener/remove根据需要使用ElementRef/HostListener,但简单的修复方法如下。

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

这样试试

<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>

我认为问题出在您的代码中:

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

应该是

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

您应该使用 [disabled] 属性 中提到的 documentation:

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

然后在你的代码中

export class AppComponent  {
  isDisabled = false;

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

查看演示的 StackBlitz

你可以在(click)属性上查看:

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

也是通过下面的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>

同时考虑 stackblitz modified from .

首先,我们需要问问自己,为什么我们要创建一种新型的按钮组件,而我们已经有了一个原生按钮组件。它可能是这样的:

  • 利用一些 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 你需要的地方 为按钮的每个实例启动/停止动画!

缺点:外观不标准。你的图书馆用户会想这是为什么 回调输入而不是事件绑定...

解决方案二。CSS

您可以尝试使用 CSS pointer-events:none 解决 K1。它 会在表面上工作,阻止用户鼠标触发点击 事件。但是,您仍然可以实用地单击 按钮。当按钮为 'disabled'.

时,myButton.click() 仍会触发

缺点:'Disabled' 元素仍然 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 解决方案。

如果您想在不使用输出的情况下保留旧版点击事件。 有一个基于前面的组合解决方案。

我的-button.component.html

<button [disabled]="disabled">
  <ng-content></ng-content>
</button>

我的-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>

我能够在 scss 中禁用禁用按钮的单击事件。这是我添加的

:host {
button {
&:disabled:active {
      pointer-events: none;
}
}
}

这可以通过使用布尔变量来检查某些条件是否为真来实现,如下所示:

<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