Antworten:
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';
}
}
Eine Alternative zu AMagyars Antwort. Diese Version funktioniert, wenn Sie auf ein Element klicken, das mit einer ngIf aus dem DOM entfernt wird.
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;
}
Das Binden an das Klicken von Dokumenten durch @Hostlistener ist kostspielig. Es kann und wird sichtbare Auswirkungen auf die Leistung haben, wenn Sie zu viel verwenden (z. B. beim Erstellen einer benutzerdefinierten Dropdown-Komponente und wenn mehrere Instanzen in einem Formular erstellt wurden).
Ich empfehle, dem Dokumentklickereignis nur einmal in Ihrer Haupt-App-Komponente einen @ Hostlistener () hinzuzufügen. Das Ereignis sollte den Wert des angeklickten Zielelements in ein öffentliches Subjekt verschieben, das in einem globalen Dienstprogrammdienst gespeichert ist.
@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>()
}
Wer sich für das angeklickte Zielelement interessiert, sollte das öffentliche Thema unseres Dienstprogrammdienstes abonnieren und sich abmelden, wenn die Komponente zerstört wird.
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
}
Die oben genannten Antworten sind korrekt, aber was ist, wenn Sie einen schweren Prozess ausführen, nachdem Sie den Fokus der relevanten Komponente verloren haben? Dafür habe ich eine Lösung mit zwei Flags geliefert, bei der der Fokus-Out-Ereignisprozess nur stattfindet, wenn der Fokus nur von der relevanten Komponente verloren geht.
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;
}
Hoffe das wird dir helfen. Korrigieren Sie mich, wenn Sie etwas verpasst haben.
Sie können die MethodeeclickOutside () unter https://www.npmjs.com/package/ng-click-outside package verwenden
Die Antwort von ginalx sollte als Standardantwort festgelegt werden: Diese Methode ermöglicht viele Optimierungen.
Das Problem
Angenommen, wir haben eine Liste mit Elementen, und zu jedem Element möchten wir ein Menü hinzufügen, das umgeschaltet werden muss. Wir fügen ein Umschalten auf eine Schaltfläche ein, die auf ein click
Ereignis an sich wartet (click)="toggle()"
, aber wir möchten auch das Menü umschalten, wenn der Benutzer außerhalb davon klickt. Wenn die Liste der Elemente wächst und wir @HostListener('document:click')
in jedem Menü ein Menü anhängen, wartet jedes im Element geladene Menü auf das Klicken auf das gesamte Dokument, auch wenn das Menü deaktiviert ist. Neben den offensichtlichen Leistungsproblemen ist dies nicht erforderlich.
Sie können beispielsweise abonnieren, wenn das Popup per Klick umgeschaltet wird, und erst dann auf "externe Klicks" warten.
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();
}
Und in *.component.html
:
<button (click)="toggle()">Toggle the menu</button>
tap
Operator zu stecken. Verwenden Sie stattdessen skipWhile(() => !this.isActive), switchMap(() => this._utilitiesService.documentClickedTarget), filter((target) => !this._elementRef.nativeElement.contains(target)), tap(() => this._toggleMenuSubject$.next(false))
. Auf diese Weise nutzen Sie viel mehr RxJs und überspringen einige Abonnements.
@J verbessern. Frankenstein answear
@HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
@HostListener('document:click')
clickout() {
this.text = "clicked outside";
}