web-dev-qa-db-ja.com

Angularコンポーネントホストクリックが発動しないようにできますか?

ネイティブの_<button>_要素をいくつかの追加機能でラップするAngularコンポーネントを作成しています。ボタンが無効になっていて同じものを複製したい場合、ボタンはクリックイベントを起動しません機能性、すなわち、与えられた:

_<my-button (click)="onClick()" [isDisabled]="true">Save</my-button>
_

_my-button_がonClick()が呼び出されるのを防ぐ方法はありますか?

Angularこの方法でホストクリックイベントをリッスンし、イベントのpropagationを停止できます。

_//Inside my-button component
@HostListener('click', ['$event'])
onHostClick(event: MouseEvent) {
  event.stopPropagation();
}
_

これにより、イベントが先祖要素にバブリングするのを防ぎますが、組み込みの_(click)_出力が同じHost要素で発生するのを停止しません。

これを達成する方法はありますか?


編集1:これを解決する方法は、「onClick」と呼ばれる別の出力を使用することです。消費者は「click」ではなく「onClick」を使用することを知っている必要があります。理想的ではありません。

編集2: _<button>_要素で発生したクリックイベントは正常に停止します。しかし、私が持っているようにボタンタグ内に要素を配置すると、それらのターゲットのクリックイベントはホストまで伝播します。うーん、伝播を停止する別の要素でボタンをラップすることができるはずです...

13
Ryan Silva

次のことができます。

  • コンポーネントのclickイベントを再定義し、ボタンがクリックされたときにこのイベントを発行します
  • コンポーネントホストでCSSスタイル_pointer-events: none_を設定します
  • ボタンにCSSスタイル_pointer-events: auto_を設定します
  • ボタンクリックイベントハンドラーでevent.stopPropagation()を呼び出します

コンポーネント内の他の要素のクリックイベントを処理する必要がある場合は、それらにスタイル属性_pointer-events: auto_を設定し、クリックイベントハンドラーでevent.stopPropagation()を呼び出します。

this stackblitz でコードをテストできます。

_import { Component, HostListener, Input, Output, ElementRef, EventEmitter } from '@angular/core';

@Component({
  selector: 'my-button',
  Host: {
    "[style.pointer-events]": "'none'"
  },
  template: `
    <button (click)="onButtonClick($event)" [disabled]="isDisabled" >...</button>
    <span (click)="onSpanClick($event)">Span element</span>`,
  styles: [`button, span { pointer-events: auto; }`]
})
export class MyCustomComponent {

  @Input() public isDisabled: boolean = false;
  @Output() public click: EventEmitter<MouseEvent> = new EventEmitter();

  onButtonClick(event: MouseEvent) {
    event.stopPropagation();
    this.click.emit(event);
  }

  onSpanClick(event: MouseEvent) {
    event.stopPropagation();
  }
}
_

[〜#〜] update [〜#〜]

ボタンにはHTMLの子要素(spanimgなど)を含めることができるため、次のCSSスタイルを追加して、クリックが親に伝達されないようにすることができます。

_:Host ::ng-deep button * { 
  pointer-events: none; 
}
_

@ ErikWitkowski for his comment に感謝します。デモについては this stackblitz をご覧ください。

9
ConnorsFan

2016年の このgitの問題 でサポートされているように、イベントの発生を防ぐネイティブな方法があるとは思わない:

実行順序は赤字です。同じ要素のイベントが複数のリスナーに伝播される順序は現在定義されていません。これは現在仕様です。

問題は、リスナーに公開されるイベントが実際のDOMイベントであり、提供されたイベントでstopImmediatePropagation()を呼び出すと、この要素に登録されている他のリスナーの実行が停止することです。 ただし、Angularを介して登録されたすべてのリスナーは、(パフォーマンス上の理由により)このイベントでstopImmediatePropagationを呼び出すだけのdomリスナーによってプロキシされるため、効果はありません。

2
Zze

ネイティブの追加および削除EventListenersを使用できます。これは、angular用語で考えた場合、決して良い解決策ではありません。また、disabled属性をbuttonに入れた場合、これは機能しません。添付のeventListenersをオーバーライドします。代わりにdisabledクラスを使用する必要があります(または、buttonspanをラップし、そのテンプレート参照#btnを使用します)。

StackBlitz

import { Component, OnInit, OnChanges, HostListener, Input, Output, EventEmitter, SimpleChanges, ElementRef, ViewChild } from '@angular/core';

@Component({
  selector: 'app-my-button',
  template: `<button [class.disabled]="isDisabled" #btn><span>hey</span></button>`,
  styles: [`button.disabled { opacity:0.5 }`]
})
export class MyButtonComponent implements OnInit, OnChanges {
  disableClick = e => e.stopPropagation();
  @Input() isDisabled: boolean;
  @ViewChild('btn') btn: ElementRef;
  constructor() { }

  ngOnChanges(changes: SimpleChanges) {
    if(this.isDisabled) {
      this.btn.nativeElement.addEventListener('click', this.disableClick);
    } else {
      this.btn.nativeElement.removeEventListener('click', this.disableClick);
    }
  }
  ngOnInit() {
  }

}
0
sabithpocker