2

This problem has been driving me nuts. I have two components which both are toggleable dropdowns on the same page. I have something like this for both components:

@HostListener('click', ['$event'])
clickInside() {
  event.stopPropagation();
  this.showMenu = true;
}

@HostListener('document:click')
clickOutside() {
  if (this.showMenu) {
    this.showMenu = false;
  }
}

It works just fine for one component but when I have two components on the same page then clicking the other component will leave the first component open and the result is that they are both open at the same time which I do not want. The reason is of course the event.stopPropagation() but without this, a click inside the component will also be a click inside the document.

1 Answer 1

2

When we click inside component - it should be opened, when click was outside - it should be closed.

  constructor(private elementRef: ElementRef){}

  @HostListener('click')
  clickInside() {
    this.showMenu = true;
  }

  @HostListener('document:click', ['$event'])
  clickOutside(event) {
    if (this.showMenu && this.isClickOutside(event)) {
      this.showMenu = false;
    }
  }

  private isClickOutside(event: MouseEvent): boolean {
    return !this.elementRef.nativeElement.contains(event.target);
  }

This solution have at least one disadvantage - each component with listener on document will lead to addition change detection cycle. To prevent this we should handle event outside of angular but change the state inside.

private readonly onDestroy$ = new Subject();

constructor( private ngZone: NgZone,) {}

ngOnInit() {
    this.ngZone.runOutsideAngular(() => {
      fromEvent(window.document, 'click')
        .pipe(
          filter(
            (event: MouseEvent) =>
              this.isOpen() && this.isClickOutside(event)
          ),
          takeUntil(this.onDestroy$),
        )
        .subscribe(() => {
          this.ngZone.run(() => {
            this.close();
          });
        });
    });
  }

ngOnDestroy() {
    this.onDestroy$.next();
    this.onDestroy$.complete();
  }
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.