检测Angular组件外部的单击

时间:2016-10-18 11:27:31

标签: html events angular typescript

如何在Angular?

中检测之外的组件

8 个答案:

答案 0 :(得分:108)

import { Component, ElementRef, HostListener, Input } from '@angular/core';

@Component({
  selector: 'selector',
  template: `
    <div>
      {{text}}
    </div>
  `
})
export class AnotherComponent {
  public text: String;

  @HostListener('document:click', ['$event'])
  clickout(event) {
    if(this.eRef.nativeElement.contains(event.target)) {
      this.text = "clicked inside";
    } else {
      this.text = "clicked outside";
    }
  }

  constructor(private eRef: ElementRef) {
    this.text = 'no clicks yet';
  }
}

A working example - click here

答案 1 :(得分:18)

替代AMagyar的答案。当您单击使用ngIf从DOM中删除的元素时,此版本有效。

http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p=preview

&#13;
&#13;
  private wasInside = false;
  
  @HostListener('click')
  clickInside() {
    this.text = "clicked inside";
    this.wasInside = true;
  }
  
  @HostListener('document:click')
  clickout() {
    if (!this.wasInside) {
      this.text = "clicked outside";
    }
    this.wasInside = false;
  }
&#13;
&#13;
&#13;

答案 2 :(得分:4)

ginalx's answer应该设置为默认的一个imo:此方法可以进行许多优化。

问题

说我们有一个项目列表,并且在每个项目上我们都想包含一个需要切换的菜单。我们在按钮上添加了一个切换按钮,该按钮可监听click本身的(click)="toggle()"事件,但是我们也想在用户单击菜单外部时切换菜单。如果项目列表增加,并且我们在每个菜单上都附加了@HostListener('document:click'),则即使菜单处于关闭状态,项目中加载的每个菜单也将开始侦听整个文档的单击。除了明显的性能问题外,这也是不必要的。

例如,您可以在通过点击切换弹出窗口时进行订阅,然后才开始监听“外部点击”。


isActive: boolean = false;

// to prevent memory leaks and improve efficiency, the menu
// gets loaded only when the toggle gets clicked
private _toggleMenuSubject$: BehaviorSubject<boolean>;
private _toggleMenu$: Observable<boolean>;

private _toggleMenuSub: Subscription;
private _clickSub: Subscription = null;


constructor(
 ...
 private _utilitiesService: UtilitiesService,
 private _elementRef: ElementRef,
){
 ...
 this._toggleMenuSubject$ = new BehaviorSubject(false);
 this._toggleMenu$ = this._toggleMenuSubject$.asObservable();

}

ngOnInit() {
 this._toggleMenuSub = this._toggleMenu$.pipe(
      tap(isActive => {
        logger.debug('Label Menu is active', isActive)
        this.isActive = isActive;

        // subscribe to the click event only if the menu is Active
        // otherwise unsubscribe and save memory
        if(isActive === true){
          this._clickSub = this._utilitiesService.documentClickedTarget
           .subscribe(target => this._documentClickListener(target));
        }else if(isActive === false && this._clickSub !== null){
          this._clickSub.unsubscribe();
        }

      }),
      // other observable logic
      ...
      ).subscribe();
}

toggle() {
    this._toggleMenuSubject$.next(!this.isActive);
}

private _documentClickListener(targetElement: HTMLElement): void {
    const clickedInside = this._elementRef.nativeElement.contains(targetElement);
    if (!clickedInside) {
      this._toggleMenuSubject$.next(false);
    }    
 }

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

而且,在*.component.html中:


<button (click)="toggle()">Toggle the menu</button>

答案 3 :(得分:2)

您可以使用https://www.npmjs.com/package/ng-click-outside软件包中的clickOutside()方法

答案 4 :(得分:2)

以上提到的答案是正确的,但是如果您失去了相关组件的关注之后却进行了繁重的工作,该怎么办。为此,我提供了一个带有两个标志的解决方案,其中仅当仅从相关组件中失去焦点时,才会进行聚焦事件处理。

bytes(l) -> b'\x01\x02\x03.......'

希望这会对您有所帮助。纠正我,如果错过了任何事情。

答案 5 :(得分:2)

通过@Hostlistener绑定到文档单击是昂贵的。如果您过度使用它,将会并且将会产生明显的性能影响(例如,在构建自定义下拉组件时,您在表单中创建了多个实例)。

我建议在主应用程序组件内仅将一次@Hostlistener()添加到文档单击事件。该事件应将单击的目标元素的值压入存储在全局实用程序服务中的公共主题中。

@Component({
  selector: 'app-root',
  template: '<router-outlet></router-outlet>'
})
export class AppComponent {

  constructor(private utilitiesService: UtilitiesService) {}

  @HostListener('document:click', ['$event'])
  documentClick(event: any): void {
    this.utilitiesService.documentClickedTarget.next(event.target)
  }
}

@Injectable({ providedIn: 'root' })
export class UtilitiesService {
   documentClickedTarget: Subject<HTMLElement> = new Subject<HTMLElement>()
}

对单击的目标元素感兴趣的人应订阅我们的实用程序服务的公共主题,并在组件被破坏时退订。

export class AnotherComponent implements OnInit {

  @ViewChild('somePopup', { read: ElementRef, static: false }) somePopup: ElementRef

  constructor(private utilitiesService: UtilitiesService) { }

  ngOnInit() {
      this.utilitiesService.documentClickedTarget
           .subscribe(target => this.documentClickListener(target))
  }

  documentClickListener(target: any): void {
     if (this.somePopup.nativeElement.contains(target))
        // Clicked inside  
     else
        // Clicked outside
  }

答案 6 :(得分:2)

改进@J。科学怪人answear

  
  @HostListener('click')
  clickInside($event) {
    this.text = "clicked inside";
    $event.stopPropagation();
  }
  
  @HostListener('document:click')
  clickOutside() {
      this.text = "clicked outside";
  }

答案 7 :(得分:-2)

您可以调用(focusout)或(blur)之类的事件函数,然后输入您的代码

<div tabindex=0 (blur)="outsideClick()">raw data </div>
 

 outsideClick() {
  alert('put your condition here');
   }