Custom ngIf directive with embedded else template

2020-07-22 09:36发布

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.

2条回答
SAY GOODBYE
2楼-- · 2020-07-22 10:08

I assume data-view is not your custom component otherwise placing the #progress template inside your data-view template and handling it there would be easier with less repeated code. In your case you can achieve what you wanted with dynamically making content projections through directives. These answers could help on that direction:

查看更多
你好瞎i
3楼-- · 2020-07-22 10:15

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.

查看更多
登录 后发表回答