HostListener在外部单击Angular 6

时间:2018-09-13 06:39:15

标签: angular

这是我打开弹出窗口的方法:

<div>
  <a (click)="trigger = true">
    <div>
      <i class="pe-7s-file"></i>
    </div>
  </a>
  <div popover clickOutside (clickOutside)="console.log('Outside')" *ngIf="trigger==true">
    <p>Hello Popover</p>
  </div>
</div>

当我单击第一个div时,它将在控制台中(外部)打印,我想在第一次打开弹出窗口而控制台上没有任何消息,而当我单击弹出式div外部时,控制台应该会打印。

这是我的主持人:

@Directive({
    selector: '[clickOutside]'
})
export class ClickOutsideDirective {

    constructor(private _elementRef: ElementRef) { }

    @Output('clickOutside') clickOutside: EventEmitter<any> = new EventEmitter();

    @HostListener('document:click', ['$event.target'])
    onMouseEnter(targetElement) {
        const clickedInside = this._elementRef.nativeElement.contains(targetElement);
        if (!clickedInside) {
            this.clickOutside.emit(null);
        }
    }

}

1 个答案:

答案 0 :(得分:0)

我不确定您到目前为止想要达到的目标。因此,根据我的解释,我已将您的代码修改如下:

html

<div>
    <a (click)="displayPopover()">
        <div>
            my content outside
        </div>
    </a>
    <ng-container *ngIf="isDisplay">
      <div clickOutside (clickOutside)="myHandler()" style="border: 1px solid black">
          <p>Click me, nothing will happen</p>
      </div>
    </ng-container>
</div>

仅用于示例的组件ts:

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  isDisplay: boolean = false;
  myHandler() {
    console.log('click outside');
    this.isDisplay = false;
  }

  displayPopover() {
    this.isDisplay = true;
  }
}

指令:

@Directive({
    selector: '[clickOutside]'
})
export class ClickOutsideDirective implements AfterViewInit, OnDestroy {
    @Output('clickOutside') clickOutside: EventEmitter<void> = new EventEmitter();
    squareMatrix: {
      x1: number,
      y1: number,
      x2: number,
      y2: number
    };
    sub: Subscription;
    constructor(private _elementRef: ElementRef) { }

    ngAfterViewInit() {
        /**
         * Populate current square x,y position.
         */
        this.populateMatrix();
        // Timeout is here to ignore first click. Not right way but do the job.
        setTimeout(() => {
        this.sub = fromEvent(window, 'click').subscribe((e: MouseEvent) => {
            if(!this.checkIfClickOnSquare(e.clientX, e.clientY)) {
              this.clickOutside.emit();
            }
        });
        },100); 
    }

    ngOnDestroy(){
      if(this.sub) {
        //Don't forget to unsubscribe.
        this.sub.unsubscribe();
      }
    }

    private populateMatrix() {
      const {x, y, width, height} = this._elementRef.nativeElement.getBoundingClientRect();

      this.squareMatrix = {
        x1 : x,
        y1: y,
        x2: x + width,
        y2: y + height
      };
    }

    private checkIfClickOnSquare(currentX, currentY): boolean {
      return (
          currentX > this.squareMatrix.x1 &&
          currentX < this.squareMatrix.x2 && 
          currentY > this.squareMatrix.y1 && 
          currentY < this.squareMatrix.y2
        );
    }
}

说明: 我创建了正方形矩阵,以具有实际绘制的Popover的点参考。基于这个正方形,在每次单击文档触发事件时,我检查当前x,y光标是否在当前DOMElement的内部或外部。

为防止首次单击,我在绑定事件之前已添加超时。做事的方法不正确。

如果愿意,可以查看live sample