Detect Click outside element in angular 4 using directives
Asked Answered
H

1

21

I have used a custom directive to detect click outside an element in angular 2 but the same is not possible in angular 4.

[plunkr] https://plnkr.co/edit/aKcZVQ?p=info

When I try using the same code in angular-4 I get the following errors:

1. Argument of type '{ template: string; directives: typeof ClickOutside[]; }' is not assignable to parameter of type 'Component'. ==> 

    @Component({
    templateUrl: "",
    directives: [ClickOutside]
    })


2. Type 'Subscription' is not assignable to type 'Observable<MouseEvent>'. in the directive inside ngOnInit() and ngOnDestroy() 

ngOnInit() {
    this.globalClick = Observable
        .fromEvent(document, 'click')
        .delay(1)
        .do(() => {
            this.listening = true;
         }).subscribe((event:MouseEvent) => {
            this.onGlobalClick(event);
         });
}

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

If there is any change in the directive declaration in angular 4 please let me know, the official docs are of no help in this matter.

Halbeib answered 2/9, 2017 at 5:47 Comment(3)
Oops, this one: https://mcmap.net/q/659154/-hostlistener-onclick-for-outside-click-does-not-work-in-firefoxPlastered
@Plastered I am following the same method but the only problem is that the application takes time of `2000ms to process each click, which is a bummer as this distorts the UXHalbeib
Also ng-click-outside node package seems to work for this purpose. Seems to work even with the latest Angular version (angular 6+ at the moment). (see this comment as a just-for-information one)Infinitude
T
50

There are few changes in relation to your plnkr.

  1. NgModules, or rather take a look on Archietcture of the framework. The module is the place where you should register your components, services and directices
  2. Once you registered your directive inside module you don't have to import it inside components

The directive itself looks fine for me. I compared your directive with mine that works fine in Angular 4.3.5.

Actually, you don't need any directive in that case, unless it won't be used repetitive. If you need to apply that clickOutside only for menu it would be better to do sth like that:

Bind click event to your "inside" selector like that. Let's say it's your menu:

  <ul id="menu" (click)="clickedInside($event)"> <li> .. </li> </ul>

then inside your component add clickedInside() function like this:

  clickedInside($event: Event){
    $event.preventDefault();
    $event.stopPropagation();  // <- that will stop propagation on lower layers
    console.log("CLICKED INSIDE, MENU WON'T HIDE");
  }

And finally you can use Host Listener in your component to bind click also to the rest of document

  @HostListener('document:click', ['$event']) clickedOutside($event){
    // here you can hide your menu
    console.log("CLICKED OUTSIDE");
  }
Tutor answered 2/9, 2017 at 6:30 Comment(4)
Here you have minimal working demo: plnkrTutor
please ignore the above comment Even if I Stop prorogation to lower elements the menu close time is `2300ms which seems like a bug, but yes it definitely worksHalbeib
what if the zone you're hiding is a dropzone? when I click it the "chose files window" opens but the clickedInside() fails to be called. how can I narrow the spectrum?Electrophone
answer to myself : then you'll want to test the event pretty specifically, with any luck the Component is very unique in it's element and class names : have this as one of your if's conditions to the deselect : !$event.path[0].classList[0].startsWith('dz')Electrophone

© 2022 - 2024 — McMap. All rights reserved.