That *ngIf structural directive is a nifty little tool, isn't it?
It lets you decide whether or not to display an element (and any of its children) on the page.
That's why it's called a structural directive. It dictates what does (or does not) appear to the end user.
But an if statement is often not quite as useful as it could be without a corresponding else.
After all, you'll frequently want to display some element based on one condition and another element if based on the opposite condition.
So yeah, you could use an else to go along with that *ngIf.
Now it might not be very obvious but there's a way to do it. And I'll show you how in this guide.
Let's say you're developing a CRM app and you want to output a table that displays all contacts.
That seems easy enough. But the problem is that your application first needs to retrieve the contacts via the downstream API. And until it does that, you don't want to display that table because it would be empty.
So the application needs to wait until the contacts get loaded before displaying the table.
Here's how to make that happen in the template:
<ng-template #loading> <div class="absolute-center"> <mat-spinner [diameter]="80"></mat-spinner> </div> </ng-template> <div *ngIf="dataSource$ | async as dataSource; else loading"> ...table code goes here </div>
There's a lot going on in that code block above so let me break it down.
Skip the <ng-template> element for now. Just focus on the lower <div> element.
That <div> element uses the *ngIf structural directive that you've seen a trillion times. Specifically, it's referencing a dataSource$ property in the corresping component class.
That dataSource$ property, as the dollar sign at the end of its name indicates, is an Observable.
The component class uses that Observable to retrieve contacts from the downstream API. The details about that aren't important here.
If you do want more info on how to populate Angular Material tables, feel free to read my guide on that subject.
The point of that *ngIf statement is this: it evaluates to false until the component class has subscribed to the Observable and retrieved data from the downstream service.
The Observable gets piped to async so that the component class doesn't need to subscribe to it. Reactive programming purists will be happy to see that.
The actual data retrieved from the Observable gets referenced in the rest of the template as dataSource (note the missing $). Hence the as statement.
But remember: that *ngIf statement returns false if the contacts data hasn't yet been loaded. So what happens in that case?
In that case, the logic follows the else statement.
Now that part of the logic is fairly compact: all it says is else loading.
Here, loading is a template reference variable. It's a reference to a named element.
And if you look again at the code above, you'll see that the top <ng-template> element includes a template reference variable named loading. The dead giveaway is the hashtag in front of the word "loading."
So that whole *ngIf statement says this in English: "If we have contacts data from the downstream service, show this element and all its children. Otherwise, show the element referenced by 'loading.'"
A little word about <ng-template>: it's a placeholder. It's not a real HTML element that appears on the page when you view source.
In other words, if the else logic is followed, Angular will render the HTML between the <ng-template> tags. But not the tags themselves.
That <ng-template> tag is handy if you want to enclose HTML within a parent element that doesn't get rendered. That's what I'm doing with it above.
Also: anything in <ng-template> is hidden by default. You have to write code that display its contents (as you see above).
An Easier Look
I realize the code above complicates things with the use of an Observable. So let me offer a "cleaner" example.
Let's say you subscribe to the Observable in the component class. This will upset the Reactive Programming Overlords, but you do it anyway.
Once the list of contacts is retrieved from the downstream service, you store them as an array in a property called contacts.
In that case, you could rewrite the code above with something like this:
<ng-template #loading> <div class="absolute-center"> <mat-spinner [diameter]="80"></mat-spinner> </div> </ng-template> <div *ngIf="contacts; else loading"> ...table code goes here </div>
Assuming the contacts property defaults to null or undefined until the data gets loaded from the downstream service, that code above will do the same thing as the previous code block.
Wrapping It Up
Now you know how to use else with *ngIf.
So why not take what you learned about here and use it in your applications? Then you won't have to include separate *ngIf statements for else conditions.