在 Angular 4 中从@HostListener(动态创建点击侦听器)捕获文档的下一次点击

Capturing next click on document from @HostListener (dynamically creating a click listener) in Angular 4

我有一个指令可以在单击时切换它的状态。如果状态处于活动状态,并且用户单击了页面上的任何其他位置,则应停用该状态。

我尝试使用新的 Renderer2 侦听函数动态生成侦听器来捕获 "next click" 来实现此目的 - 这里的问题是一旦创建侦听器就会执行回调函数!所以 - 单击一次激活,然后立即停用该元素。我正在寻求帮助以了解为什么会发生这种情况以及我需要做些什么来解决它。

指令:

import { Directive, ElementRef, Input, Output, HostListener, EventEmitter, Renderer2 } from '@angular/core';

@Directive({
  selector: '[emClickToClose]'
})
export class ClickToCloseDirective {
  @Input('emClickToClose') 
  openCtrl: boolean;

  @Output()
  ctrlUpdate: EventEmitter<boolean> = new EventEmitter();

  @HostListener('click', ['$event']) onClick(e: MouseEvent) {
    this.openCtrl = !this.openCtrl;
    this.ctrlUpdate.emit(this.openCtrl);
    if (this.openCtrl) {
      const removeRegisteredListener = this.renderer.listen('document', 'click', () => {
        console.log('the document was clicked', this.openCtrl);
        this.openCtrl = false;
        this.ctrlUpdate.emit(this.openCtrl);
        removeRegisteredListener();
      });
    }
  }

  constructor(private el: ElementRef, private renderer: Renderer2) {
   }

}

事件从子级冒泡到父级,并且由于您在事件仍在传播时添加了事件侦听器,因此作为最终父级的文档将收到您的点击事件。您需要通过使用来停止事件的传播。 e.stopPropagation(); 在您的点击处理程序中。

您应该使用如下指令和组件来实现此目的

将组件声明为

<demo-component [emitStatus]="false"></demo-component>

输入变量为emitStatus

创建指令为

import { Directive, HostListener, Renderer, ElementRef } from '@angular/core';
@Directive({
    selector: '[click]'
})
export class ChangeDirective{

    constructor(
        private renderer: Renderer,
        private el: ElementRef
    ){}

    @HostListener('click') onClick() {
      let status = this.el.nativeElement.attributes['emitStatus'];
       if(status){
           doo something
       }

    }
}

将选择器添加到组件中

<demo-component [emitStatus]="false" click></demo-component>

首先,您的 @HostListener('click', ...) 会将事件侦听器绑定到应用了 [emClickToClose] 属性的 dom 元素。我不确定您是如何使用它的,但是如果这与您要在外部单击以关闭的元素相同,则您需要改用 @HostListener('document:click', ...)

这是怎么回事,您的事件处理程序正在绑定、捕获点击并立即关闭 window 并删除自身。您需要确保点击来自指令元素之外,如下所示:

从'@angular/core'导入{指令、ElementRef、输入、输出、HostListener、EventEmitter、Renderer2};

@Directive({
  selector: '[emClickToClose]'
})
export class ClickToCloseDirective {
  @HostListener('document:click', ['$event']) documentClick(event) {
    if ( ! this.el.nativeElement.contains(event.target) && this.open) {
      this.openCtrl = false;
      this.ctrlUpdate.emit(this.openCtrl);
    }
  }

  constructor(private el: ElementRef, private renderer: Renderer2) {
  }
} 

此外,简单地将您的函数命名为 "removeRegisteredListener" 并不会真正使它删除事件侦听器,但是当指令被销毁时事件侦听器将被删除。我希望这有帮助。