In my Angular 14 App I've got component with canvas
in template.
Whole template is under *ngIf
checking if data was emitted by Observable.
I need to create Chart
using data from Observable and canvas reference which I get with @ViewChild
.
The problem is, when I got data in Observable pipe, canvas element is not rendered yet.
I can use setTimeout in Observable pipe and it will work but I don't want to use it, so I've been looking for better solution and finally I've come up with one below.
1 I've got canvas @ViewChild
, BehaviorSubject canvasLoaded$
with initial value false, and canvasLoadedSubscription
:
@ViewChild('canvas') canvas!: ElementRef;
canvasLoaded$: BehaviorSubject<boolean> = new BehaviorSubject(false);
canvasLoadedSubscription!: Subscription;
2 I've got method subscribeToCanvasLoaded
where I subscribe to canvasLoaded$
saving subscription in canvasLoadedSubscription
, and call renderCharts
if canvasLoaded$
emits true:
subscribeToCanvasLoaded(data: any): void {
this.canvasLoadedSubscription = this.canvasLoaded$
.pipe(filter(Boolean))
.subscribe(() => this.renderCharts(data));
}
3 in data$
pipe I call subscribeToCanvasLoaded
method passing data:
this.data$ = this.dataService.data$.pipe(
filter(Boolean),
tap((data) => {
if(data.length) {
this.subscribeToCanvasLoaded(data)
}
})
);
4 in ngAfterContentChecked
if I already can access canvas and if canvasLoaded$
still false I set it to true and unsubscribe from canvasLoadedSubscription
:
ngAfterContentChecked(): void {
if(this.canvas && !this.canvasLoaded$.value) {
this.canvasLoaded$.next(true);
this.canvasLoadedSubscription.unsubscribe();
}
}
this way I don't have to save data in component, it goes straight to template where I use async pipe, I don't have to use seTimeout() and I know for sure when canvas is in DOM.
Are there any better solutions for my problem?