When I use *ngIf with a then and/or else statement, why do I have to bind to a template variable that is attached to a ng-template
element? For example:
This works:
<div *ngIf="show; else elseBlock">Text to show</div>
<ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
But this does not work:
<div *ngIf="show; else elseBlock">Text to show</div>
<div #elseBlock>Alternate text while primary text is hidden</div>
I also noticed adding a class does not work either:
<ng-template #elseBlock class="my-class">
Alternate text while primary text is hidden
</ng-template>
What's so special about the ng-template
? How is it different?
This is because all structural directives in Angular create embedded views. An embedded view is created using both templateRef
and viewContainerRef
. You can read more about them in the Exploring Angular DOM manipulation techniques using ViewContainerRef.
An embedded view is similar to host views that are created for components. A view contains all the nodes that you see in the component template or inside the ng-template
tag. So embedded view is like a component template without the component class. Here are the few examples of how structural directives create embedded views.
ngIf
private _updateView() {
if (this._context.$implicit) {
...
if (this._thenTemplateRef) {
this._thenViewRef =
// here embedded view is created
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
}
}
} else {
if (!this._elseViewRef) {
...
this._elseViewRef =
// here embedded view is created
this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
}
}
}
}
ngFor
private _applyChanges(changes: IterableChanges<T>) {
const insertTuples: RecordViewTuple<T>[] = [];
changes.forEachOperation(
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
if (item.previousIndex == null) {
// here embedded view is created
const view = this._viewContainer.createEmbeddedView(
this._template, new NgForOfContext<T>(null !, this.ngForOf, -1, -1), currentIndex);
ng-template is how Angular implemented and extended the template tag.
Since all the structural directives, what starts with *, like *ngIf, *ngFor, etc., are changing the DOM it's actually using the ng-template behind the scenes all the time. The directive placed on an element is just some syntactic sugar over this.
The else block does not work with any other element since it needs to be added just if needed, so it has to be in the ng-template.
Here is some more information about this.
<ng-template>
comes very 'handy' when you want to show or hide a part of the template based on the condition and its reverse.
In your example:
<div *ngIf="show; else elseBlock">Text to show</div>
<ng-template #elseBlock>Alternate text while primary text is hidden</ng-template>
it lets you do what you could've done like this:
<div *ngIf="show">Text to show</div>
<div *ngIf="!show>Alternate text while primary text is hidden</div>
The advantage of is that you can isolate part of your template and 'activate' it when needed, as plein does with JS. You can put it not always just after. It gives you more structured code.
There is also (since Angular 4.3 I think) <..*ngIf="condition"; then #template1 else #template2>
also very 'handy'.
This is a partial answer - for class. But links to related info in one place too.
I can give you a bit of detail about and another similar concept .
<ng-template>
is a placeholder, not only for else part of *ngIf, but also in *NgFor
<ng-container>
is something similar, you didn't ask about, but you'll come across later when dealing with content projection. There's a video Josh Morony gave on Youtube about this. It relates to @ContentChild and @ContentChildren.
The Angular Cheatsheet tells you the syntax for class..
来源:https://stackoverflow.com/questions/45310277/what-is-ng-template-and-why-do-i-bind-ngif-then-else-to-it