Search code examples
angularangular5angular6encapsulation

Angular encapsulation and click-outside directive


Problem: I need add a way to bind and close sidebar menu if user click outside that.

I've create a directive (Something like found on this) that check if click is bind outside my element, but when user click exactly on my font-awesome icon on sidebar, it's considered a outside click and menu is not opened (The directive is works good, I checking it clicking on button padding)...

I guess it's a problem on my directive caused by angular encapsulations...

My code:

<aside [ngClass]="{'open': menuStatus}" (clickOutside)="menuStatus = false">
    <button (click)="menuStatusToggle()">
        <fa-icon [icon]="['fas', 'bars']" *ngIf="!menuStatus"></fa-icon>
        <fa-icon [icon]="['fas', 'times']" *ngIf="menuStatus"></fa-icon>
    </button>
    (Menu Content...)
</aside>

Bind event:

@HostListener('document:click', ['$event.target'])
  public onClick(targetElement) {

    // Variable content always false when click on icon of _elementRef
    const clickedInside = this._elementRef.nativeElement.contains(targetElement);

    if (!clickedInside) {
      this.clickOutside.emit(null);
    }
  }

Someone have a suggestion? Encapsulation is really a problem?


Solution

  • To make sure that the font-awesome icon does not catch the click event, define the following style in the component CSS:

    fa-icon {
      pointer-events: none;
    }
    

    With that style attribute, the button receives the click event, which can then be handled correctly.

    See this stackblitz for a demo.


    An alternative solution is to handle the click event on the host element, and to stop the propagation. As a consequence, all the click events reaching the document are outside the host element:

    @HostListener('click', ['$event'])
    public onHostClick(event: Event) {
      event.stopPropagation();
    }
    
    @HostListener('document:click', ['$event'])
    public onDocumentClick(event: Event) {
      this.clickOutside.emit(null);
    }
    

    See this stackblitz for a demo.