3
votes

It's a common pattern in Angular apps to display some data coming from Observable with ngIf directive and provide else template to show placeholder while data is loading.

<data-view *ngIf="data$ | async as data; else progress" [items]="data">
</data-view>

<ng-template #progress>
  <mat-icon></mat-icon>
  <mat-progress></mat-progress>
</ng-template>

However It requires it multiple repetition of else template, async pipe, and as clause. Is it possible to avoid this boilerplate all together with custom directive like this:

<data-view *ngWait="data$" items="data">
</data-view>

I understand how one can combine ngIf with async pipe, but I can't figure out how to embed else template into custom directive.

1
I am not sure if this is possible using a directive as you might not be able to project the else template in place of the original component. However, have you explored creating a custom wrapper component. That way you can consume Observable via inputs and control the display of else template. Also using ng-content you can project the child component and pass the right data. This way the user of your component only has to worry about their implementation and not handling the else scenario.ashish.gd
Sure, a wrapper is a simple way to go. However wrapper will make it harder to control layout and increase boilerplate in html. So I want to explore possibility to use directive for the purpose.Rem

1 Answers

2
votes

You can use the createComponent of ViewContainerRef inside your structural directives

@Directive({
  selector: "{Your-Selector}"
})
export class StructuralDirective implements OnInit {
  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
    private resolver: ComponentFactoryResolver
  ) {}

  @Input() {Your-Selector}: boolean;

  ngOnInit() {
    if (this.{Your-Selector}) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    } else {
      let factory = this.resolver.resolveComponentFactory({Your component that holds default message});
      this.viewContainer.createComponent(factory);
    }
  }
}

You must also add {Your component that holds default message} inside your entry components.

Here is a simple demo at CodeSandbox that you can use as reference.