I like to be able to handle DOM events as an observable stream, calling filter, concatMap, etc. like the following example.
@Component({
template: `<button #btn>Submit<button`,
selector: 'app-test',
})
class TestComponent implements AfterViewInit, OnDestroy {
private destroy$ = new Subject<boolean>();
@ViewChild('btn') private btn: ElementRef<HtmlButtonElement>;
constructor(private testService: TestService) { }
ngAfterViewInit() {
fromEvent(btn.nativeElement, 'click').pipe(
exhaustMap(() = > {
return this.testService.save();
}),
takeUntil(this.destroy$),
).subscribe();
}
ngOnDestroy() {
this.destroy$.next(true);
this.destroy$.complete();
}
}
But sometimes the element I want to listen for events from is behind an *ngIf and doesn't exist when ngAfterViewInit runs. What's the best way to still listen for events in a reactive way?
One way I tried was to set up the same subscription as above in ngOnViewChecked behind an if statement that checked if the ElementRef exists, and with a flag to avoid multiple subscriptions. But I found that messy.
Is it a good practice to do something like this?
@Component({
template: `<button (click)="clickEvent.emit()">Submit<button`,
selector: 'app-test',
})
class TestComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<boolean>();
clickEvent = new EventEmitter<void>();
constructor(private testService: TestService) { }
ngOnInit() {
this.clickEvent.pipe(
exhaustMap(() = > {
return this.testService.save();
}),
takeUntil(this.destroy$),
).subscribe();
}
ngOnDestroy() {
this.destroy$.next(true);
this.destroy$.complete();
}
}
Should I replace the EventEmitter with a Subject? Is there a better way that any of these?
edit: just to be clear, my question has to do with subscribing to events from elements that might not exist due to an *ngIf