In my latest project, I am looking to develop a LoaderDirective that can fetch an observable, display a spinner while loading the data, and then switch to showing the actual data once loaded. I also want it to expose the loaded data using the 'as' keyword.
My desired usage for this directive would be:
<div *load="items$ as items">
Here is what I have managed to achieve so far:
@Directive({
// tslint:disable-next-line:directive-selector
selector: '[load]',
})
export class LoadDirective<T> {
private data: T;
@Input()
set load(observable: Observable<T>) {
observable.subscribe({
next: value => this.loaded(value),
error: error => this.showError(error)
});
}
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private componentFactoryResolver: ComponentFactoryResolver
) {
this.viewContainer.clear();
this.viewContainer.createComponent(this.componentFactoryResolver.resolveComponentFactory(SpinnerComponent));
}
private loaded(value: T) {
this.data = value;
this.viewContainer.clear();
this.viewContainer.createEmbeddedView(this.templateRef, {load: this.data});
}
private showError(e: string) {
this.viewContainer.clear();
const errorComponent = this.viewContainer.createComponent(this.componentFactoryResolver.resolveComponentFactory(ErrorComponent));
errorComponent.instance.error = e;
}
}
The challenge I'm facing is that the input data is expected to be an observable, thus necessitating the output to also be of type Observable. Is there a different method to handle this scenario similar to how we use TransformPipe
in pipes? How can I resolve this particular issue?