You can achieve that by using a structural directive, that looks like so
Here is a working stackblitz
@Directive({
selector: '[appDeafultView]'
})
export class DeafultViewDirective implements OnInit, OnDestroy {
@Input() appDeafultView: Observable<string>;
stateSub;
factoryMap = {
error: () => {
const factory2 = this.resolver.resolveComponentFactory(ErrorComponet);
this.viewContainer.createComponent(factory2);
},
loading: () => {
const factory = this.resolver.resolveComponentFactory(LoadingComponent);
this.viewContainer.createComponent(factory);
},
loaded: () => {
this.viewContainer.createEmbeddedView(this.templateRef);
}
};
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private resolver: ComponentFactoryResolver
) {}
ngOnInit() {
this.stateSub = this.appDeafultView.subscribe(state => {
console.log(state);
this.viewContainer.clear();
this.factoryMap[state]();
});
}
ngOnDestroy() {
this.stateSub.unsubscribe();
}
}
My idea is that based on observable, describing the state of request you can show different templates, where the ErrorComponent and LoadingComponent are predefined and in the case your data has been loaded successfully you will show the element on which you have attached your directive.
The usage of this directive will look like so
<p *appDeafultView="mix$">
Content loaded
</p>
Where mix$ will be an observable that will resolve as either loading, error or loaded, and based on that the appropriate thing will be rendered.