Réponses:
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';
}
}
Une alternative à la réponse d'AMagyar. Cette version fonctionne lorsque vous cliquez sur un élément qui est supprimé du DOM avec un ngIf.
http://plnkr.co/edit/4mrn4GjM95uvSbQtxrAS?p=preview
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;
}
La liaison au clic de document via @Hostlistener est coûteuse. Il peut avoir et aura un impact visible sur les performances si vous en abusez (par exemple, lors de la création d'un composant de liste déroulante personnalisé et que vous avez plusieurs instances créées dans un formulaire).
Je suggère d'ajouter un @Hostlistener () à l'événement de clic sur le document une seule fois dans le composant principal de votre application. L'événement doit pousser la valeur de l'élément cible sur lequel l'utilisateur a cliqué dans un sujet public stocké dans un service utilitaire global.
@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>()
}
Quiconque est intéressé par l'élément cible cliqué doit s'abonner au sujet public de notre service utilitaires et se désabonner lorsque le composant est détruit.
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
}
Les réponses mentionnées ci-dessus sont correctes, mais que se passe-t-il si vous effectuez un processus difficile après avoir perdu l'attention du composant concerné. Pour cela, je suis venu avec une solution avec deux indicateurs où le processus d'événement de mise au point n'aura lieu que lors de la perte du focus du composant pertinent uniquement.
isFocusInsideComponent = false;
isComponentClicked = false;
@HostListener('click')
clickInside() {
this.isFocusInsideComponent = true;
this.isComponentClicked = true;
}
@HostListener('document:click')
clickout() {
if (!this.isFocusInsideComponent && this.isComponentClicked) {
// do the heavy process
this.isComponentClicked = false;
}
this.isFocusInsideComponent = false;
}
J'espère que ceci vous aidera. Corrigez-moi si vous avez manqué quelque chose.
Vous pouvez utiliser la méthodeeclickOutside () à partir de https://www.npmjs.com/package/ng-click-outside package
La réponse de ginalx doit être définie par défaut imo: cette méthode permet de nombreuses optimisations.
Le problème
Disons que nous avons une liste d'éléments et que sur chaque élément, nous voulons inclure un menu qui doit être basculé. Nous incluons une bascule sur un bouton qui écoute un click
événement sur lui (click)="toggle()"
- même , mais nous voulons également basculer le menu chaque fois que l'utilisateur clique en dehors de celui-ci. Si la liste des éléments s'allonge et que nous attachons un @HostListener('document:click')
à chaque menu, alors chaque menu chargé dans l'élément commencera à écouter le clic sur tout le document, même lorsque le menu est désactivé. Outre les problèmes de performances évidents, cela n'est pas nécessaire.
Vous pouvez, par exemple, vous abonner chaque fois que le popup est basculé via un clic et commencer à écouter les "clics extérieurs" seulement alors.
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();
}
Et, dans *.component.html
:
<button (click)="toggle()">Toggle the menu</button>
tap
opérateur. Utilisez plutôt skipWhile(() => !this.isActive), switchMap(() => this._utilitiesService.documentClickedTarget), filter((target) => !this._elementRef.nativeElement.contains(target)), tap(() => this._toggleMenuSubject$.next(false))
. De cette façon, vous utilisez beaucoup plus de RxJ et sautez certains abonnements.
Améliorer @J. Frankenstein answear
@HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
@HostListener('document:click')
clickout() {
this.text = "clicked outside";
}
vous pouvez appeler la fonction d'événement comme (focusout) ou (flou) puis vous mettez votre code
<div tabindex=0 (blur)="outsideClick()">raw data </div>
outsideClick() {
alert('put your condition here');
}