im working Angular 15 project, i have a table where I want to show a loading spinner in a column before getting data from observable.
<tr *ngFor="let course of courseItems$ | async">
<td>{{course.id}}</td>
<td>{{course.name}}</td>
<td>
<ng-container *ngIf="condition to show only before getting data from course.additionalInfo$">
Loading ...
</ng-container>
<ng-container *ngIf="(course.additionalInfo$ | async) as additionalInfo">
{{additionalInfo.description}}
</ng-container>
</td>
</tr>
do you have any idea how can catch the moment before data subscription ?
*ngIf
conditional template rendering.Following code is not optimized. Use with CAUTION.
<tr *ngFor="let course of courseItems$ | async">
<td>{{course.id}}</td>
<td>{{course.name}}</td>
<td *ngIf="
course.additionalInfo$ | async as additionalInfo;
then courseInfoTemplate
else loadingTemplate
">
<ng-template #courseInfoTemplate>
{{additionalInfo.description}}
</ng-template>
<ng-template #loadingTemplate>
Loading ...
</ng-template>
</td>
</tr>
*ngIf
directive can be used to switch between conditional templates.
Step-by-step
Syntax: ngIf="condition; then trueTemplate else falseTemplate"
switching between trueTemplate and falseTemplate. | Detailed Docs
Observable
that will live with current Template lifecycle.*ngIf
will load the corresponding template as content of the directive element.*ngFor
performance and readability / code maintainability<ng-template>
Elements defined into *ngFor
loops may provide memory leak or performance issues and isn't recommended! see note - |1|
<tr *ngFor="let course of courseItems$ | async">
<td *ngIf="
course.additionalInfo$ | async as additionalInfo;
then courseInfoTemplate
else loadingTemplate
">
</td>
</tr>
<ng-container>
<ng-template #courseInfoTemplate let-additionalInfo>
{{additionalInfo.description}}
</ng-template>
<ng-template #loadingTemplate>
Loading ...
</ng-template>
</ng-container>
In short:
We have seperated the templates from the loop *ngFor
.
We added let-additionalInfo
attribute to the <ng-template>
element. *ngIf will inject the resolved data as template variable (visible in the template scope). We named that variable: additionalInfo
.
Explanation for geeks:
Since we moved the <ng-template>
elmeents out of the scope(context) we cannot access additionalInfo
directly.
So we need to decode the
Structural directive syntax (angular.io)
After 10 minutes of stairing we begin to wonder whether we should seek an example from another 3rd-party source.
NgIf
may inject an context(data) to the if/else templateRefs.
Rare Magic Artefacts | Hidden behavior or magic effects |
---|---|
as keyword |
Define new named local variable . Example: *ngIf="expressionToEvaluate as expResult" will create local variable expResult visible within directive element scope. |
as keyword with additional condition operators |
books$ | async as books && booksListEnabled then listTemplate else emptyTemplate will create local variable books , but then emptyTemplate will be rendered. |
then and else |
NgIf API docs ngIfThen / ngIfElse are @Input() s promerties of NgIf Directive. Regarding to the official API - They are TemplateRef and inject NgIfContext to the EmbeddedViews of the templates. NgIfContext = expResult (see row |1|) In most cases NgIf context is injected using $implicit - that means the data is will be injected queitly into the template. And ONLY if we announce we will use it with let- key definition. (see next row) |
<ng-template let-blah> ... |
let- key define a local variable for the template. In this example let-blah will define blah local variable. NgIf will inject the books object into blah local variable. However Instead of using books[0].title we have to access it as blah[0].title . Note: Usually we want to stay sync and use let-books for the context. |
Notes:
Note |1|: Vary on different ng versions, angular compilator/cli versions or project configurations. Try to avoid this bad practice!