Jawaban:
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';
}
}
Alternatif jawaban AMagyar. Versi ini berfungsi saat Anda mengklik elemen yang dihapus dari DOM dengan 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;
}
Mengikat ke dokumen klik melalui @Hostlistener itu mahal. Ini dapat dan akan memiliki dampak kinerja yang terlihat jika Anda menggunakannya secara berlebihan (misalnya, saat membuat komponen tarik-turun khusus dan Anda memiliki beberapa contoh yang dibuat dalam satu formulir).
Saya sarankan menambahkan @Hostlistener () ke acara klik dokumen hanya sekali di dalam komponen aplikasi utama Anda. Peristiwa tersebut harus mendorong nilai elemen target yang diklik di dalam subjek publik yang disimpan dalam layanan utilitas 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>()
}
Siapa pun yang tertarik dengan elemen target yang diklik harus berlangganan subjek publik dari layanan utilitas kami dan berhenti berlangganan saat komponen dimusnahkan.
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
}
Jawaban yang disebutkan di atas benar tetapi bagaimana jika Anda melakukan proses yang berat setelah kehilangan fokus dari komponen yang relevan. Untuk itu, saya datang dengan solusi dengan dua flag dimana proses focus out event hanya akan berlangsung ketika kehilangan fokus dari komponen yang relevan saja.
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;
}
Semoga ini bisa membantu Anda. Koreksi saya Jika melewatkan sesuatu.
Anda dapat menggunakan metodeeclickOutside () dari https://www.npmjs.com/package/ng-click-outside package
Jawaban ginalx harus disetel sebagai default imo: metode ini memungkinkan banyak pengoptimalan.
Masalah
Katakanlah kita memiliki daftar item dan pada setiap item kita ingin menyertakan menu yang perlu diubah. Kami menyertakan sakelar pada tombol yang mendengarkan click
acara itu sendiri (click)="toggle()"
, tetapi kami juga ingin beralih menu setiap kali pengguna mengeklik di luarnya. Jika daftar item bertambah dan kami melampirkan @HostListener('document:click')
di setiap menu, maka setiap menu yang dimuat di dalam item akan mulai mendengarkan klik pada seluruh dokumen, bahkan saat menu dimatikan. Selain masalah kinerja yang jelas, ini tidak perlu.
Anda dapat, misalnya, berlangganan setiap kali popup diubah melalui satu klik dan mulai mendengarkan "klik luar" hanya setelah itu.
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();
}
Dan, di *.component.html
:
<button (click)="toggle()">Toggle the menu</button>
tap
operator. Sebagai gantinya, gunakan skipWhile(() => !this.isActive), switchMap(() => this._utilitiesService.documentClickedTarget), filter((target) => !this._elementRef.nativeElement.contains(target)), tap(() => this._toggleMenuSubject$.next(false))
. Dengan cara ini Anda memanfaatkan lebih banyak RxJ dan melewati beberapa langganan.
Meningkatkan @J. Frankenstein menjawab
@HostListener('click')
clickInside($event) {
this.text = "clicked inside";
$event.stopPropagation();
}
@HostListener('document:click')
clickout() {
this.text = "clicked outside";
}
Anda dapat memanggil fungsi acara seperti (focusout) atau (blur) lalu Anda memasukkan kode Anda
<div tabindex=0 (blur)="outsideClick()">raw data </div>
outsideClick() {
alert('put your condition here');
}