簡體   English   中英

Angular Click Outside Directive問題以及額外的點擊事件

[英]Angular Click Outside Directive issue with an extra click event

我已經實現了一個自定義的Click Outside Directive,它用於關閉模式對話框,通知,彈出窗口等(為簡單起見,我們稱它們為“彈出窗口”)。 但是,我正在努力為通過鼠標單擊(即按鈕單擊)打開的彈出窗口和通過其他操作(即鼠標懸停)打開的彈出窗口實現通用解決方案。

這是我面臨的問題的一個Plunker示例 :mouseover事件產生的彈出窗口需要單擊兩次才能關閉,而“ Click Me”按鈕產生的同一彈出窗口只需單擊一次即可關閉。

這是指令的代碼:

@Directive({
  selector: '[clickOutside]'
})
export class ClickOutsideDirective {
  @Output('clickOutside') clickOutsideEvent = new EventEmitter();

  private globalClick: Subscription;

  constructor(private elRef: ElementRef) { }

  ngOnInit() {
    this.globalClick = Observable
      .fromEvent(document, 'click')
      .skip(1)
      .subscribe((event: MouseEvent) => {
        this.clicked(event);
      });
  }

  ngOnDestroy() {
    this.globalClick.unsubscribe();
  }

  private clicked(event: MouseEvent) {
    let clickedInside = this.elRef.nativeElement.contains(event.target);

    if (!clickedInside) {
      this.clickOutsideEvent.emit();
    }
  }
}

在ngOnInit()內部,我初始化了一個可觀察的對象,該對象可監聽文檔上的單擊並將事件傳遞給clicked()函數,該函數將檢查單擊是否在外部進行,如果是,則引發一個事件。 可觀察對象使用跳過運算符過濾首次點擊事件。 這是一種解決方法,因為如果您單擊“ Click Me”按鈕,則該原始click事件就是我要跳過的事件,因為它對指令中的我沒有用。 如果我不跳過它,則彈出窗口將自行關閉,因為它將認為在外部單擊。 但是,此替代方法有一個副作用,現在需要兩次單擊才能關閉彈出窗口,該彈出窗口是通過其他方式觸發的,而不是通過單擊來觸發的(Plunker中的“鼠標懸停”示例演示了此問題)。

我正在努力想一個優雅而通用的解決方案來解決這個問題。 我可以想到多種“ hacky”解決方案:

Hacky解決方案1:我可以在“ Click Me”按鈕處停止事件傳播,這意味着ClickOutsideDirective將不會收到初始click事件,並且可以從可觀察到的文檔單擊中刪除.skip運算符。 從理論上講這應該可行(沒有嘗試過),但我不喜歡的是指令的使用者現在需要知道他們需要停止事件傳播,如果不這樣做,指令將無法按設計工作。

Hacky解決方案2:將布爾值標志傳遞給指令,該標志可用於確定是否需要跳過第一次單擊。 很容易做到,但是同樣,API的用戶現在需要知道他們需要設置此標志。 如果他們忘記了,該指令將無法按預期工作。

hacky解決方案3:跳過指令初始化后短時間內(例如100ms)發生的第一個事件。 這也很容易做到,該解決方案的優勢在於,API的用戶無需向指令提供任何其他信息(或停止任何事件傳播),並且該指令將“正常工作”。 這里的問題是弄清楚在所有瀏覽器和硬件上可以使用的延遲時間。 延遲必須不小於實例化指令后事件到達所必需的時間,但如果用戶決定在顯示彈出窗口后立即單擊鼠標,則該延遲必須足夠大以跳過合法的用戶單擊。

誰能想到一個更好,更簡單,更優雅的解決方案?

不要將邏輯保留在單獨的指令中,而是為什么不將其保留在popup組件中,因為這是它負責關閉的原因。

更改組件,以便在彈出窗口后面有一個帶有單擊偵聽器的固定位置div。 然后,只要單擊它,就發送clickOutside事件。

import {Component, Output, EventEmitter} from '@angular/core';

@Component({
  selector: 'popup',
  styles: [`
      .background {
    z-index: 1;
    position: fixed;
    top: 0;
    bottom: 0;
    left: 0;
    right: 0;
    backgrouc
      }

      .modal-content {
    left: 0;
    right: 0;
    z-index: 3;
      }
  `],
  template: `
    <div class="background" (click)="close()">
      <div class="modal-content">
    Click outside to close me!
      </div>
    </div>
  `,
})
export class PopupComponent {

  @Output()
  clickOutside = new EventEmitter();

  close() {
    this.clickOutside.emit();
  }
}

這是一個工作正常的plunkr: https ://plnkr.co/edit/9QBJ0PXOg2GAUpBacgQV ? p = preview

因此,我終於找到了答案。 該解決方案不像我希望的那樣干凈,但是基本上我添加了一個可觀察的計時器(值為零),它將觸發可觀察的.fromEvent(document,'click')。 我已經在所有現代瀏覽器(包括iPad和Android Chrome)中對此進行了測試,它似乎在任何地方都可以正常工作。

ngOnInit() {
   this.globalClick = Observable
      .timer(0)
      .switchMap(() => {
          return Observable.fromEvent(this.document, 'click');
      })
      .subscribe((event: MouseEvent) => {
        this.clicked(event);
      });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM