【问题标题】:HostListener on outside click Angular 6HostListener 在外部单击 Angular 6
【发布时间】:2018-09-13 06:39:14
【问题描述】:

这是我打开弹窗的方式:

<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);
        }
    }

}

【问题讨论】:

  • 您能在这里解释一下您的最终目标吗?如果您的意思是像模态或工具提示这样的“弹出框”,也许您应该使用 CDK 及其 OverlayModule 而不是指令。
  • (我告诉你,因为叠加层有一种非常简单的方法来监听背景中的点击事件)
  • @HostListener 有效吗?
  • 是否会委托事件挂钩来记录并按目标过滤?或者将适用于每个文档 onclicks ?或者根本不是那个“主机”监听器,而是事件监听器

标签: angular


【解决方案1】:

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

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

【讨论】:

  • 嘿@Yanis-git,我遇到了类似的问题。我想知道您是否找到了忽略首次点击的更好方法?
  • 嗨,Javan,您所说的忽略第一次点击是什么意思?你能给我提供一个预期用户流的样本吗?
猜你喜欢
  • 2019-01-08
  • 2017-12-11
  • 2018-02-10
  • 2019-08-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-29
  • 1970-01-01
相关资源
最近更新 更多