darcher
darcher

Reputation: 3134

Angular 2+ "click outside" of 2 components, not just 1

I have a multi-component sidebar toggle, app/button/sidebar are all in their own components, button/sidebar are children of app (or at least loaded within the app component, assume this is a child)

Still not quite up to speed on the cross-component communication aspect of Angular, I'm utilizing a service in this case.

What I'm after: if neither component button or sidebar are clicked (anything outside these two components)... collapse sidebar.

At current, I'm toggling a boolean state on a "visible" variable in a service to expand/collapse the nav.

I've tried all manner of methods with little success. What are some ways to accomplish this? Or at least somewhere I can read to get close and take it from there?

Code: https://stackblitz.com/edit/multi-component-communication

Upvotes: 2

Views: 1382

Answers (3)

Martin Parenteau
Martin Parenteau

Reputation: 73791

Your current code works if the document:click event handler toggles the visible flag:

@HostListener("document:click", ["$event"])
onClick(event) {
  this.visible = this._ref.nativeElement.contains(event.target);
}

To prevent the button click from reaching the document level, stop the event propagation:

<toggle-sidebar (click)="$event.stopPropagation()"></toggle-sidebar>

See this stackblitz for a demo.

Upvotes: 1

shhdharmen
shhdharmen

Reputation: 1463

You can handle clickOutSide in both sidebar and button. And if both are clicked outside, close your sidebar. You can achieve the same by @Output/EventEmitter in both of your child components and listen to them in your app.component.ts file.

So, to summarize:

  1. Check clickOutSide in sidebar, and emit the same to parent, i.e. app.component.ts
  2. Check clickOutSide in toggle button, and emit the same to parent, i.e. app.component.ts
  3. In app.component.ts, handle above emits and perform checks :
    • When toggle button is clicked outside, sidenav is clicked outside and sidenav is open, we should close the sidenav.
    • When toggle button is clicked inside, functionally, sidenav will be clicked outside. But, we have to change sidenav to be clicked inside. So, we will manually change that flag. Otherwise, if you click in sidenav but outside toggle button, sidenav will be closed.

I have edited your stackblitz here, considering the above scenarios.

Upvotes: 1

nircraft
nircraft

Reputation: 8478

You should be using a directive to listen to outside panel click:

import { Directive, ElementRef, EventEmitter, HostListener, Output } from '@angular/core';

@Directive({
    selector: '[clickOutside]'
})
export class ClickOutsideDirective {
    @Output() clickOutside: EventEmitter<Event> = new EventEmitter<Event>();

    constructor(private elementRef: ElementRef) { }

    @HostListener('document:mousedown', ['$event'])
    onDocumentClick(event: Event) {
        if (!this.isClickInElement(event)) {
            this.clickOutside.emit(event);
        }
    }

    private isClickInElement(event: Event): boolean {
        return this.elementRef.nativeElement.contains(event.target);
    }
}

Now where you have the menu attach this directive and bind to it's output and call the method to collapse sidebar or do your action. Ex:

<ol (clickOutside)="collapseSideBar()">

Upvotes: 2

Related Questions