11. Structural Directives
Builtin Structural Directives:
<blockquote>
<div *ngIf="technology">My {{ technology.name }}</div>
</blockquote>
<p>List of technologies</p>
<ul>
<li *ngFor="let technology of technologies">{{ technology.name }}</li>
</ul>
<div [ngSwitch]="technology?.emotion">
<app-happy-technology *ngSwitchCase="'happy'" [technology]="technology"></app-happy-technology>
<app-awesome-technology *ngSwitchCase="'awesome'" [technology]="technology"></app-awesome-technology>
<app-contemplative-technology *ngSwitchCase="'contemplative'" [technology]="technology"></app-contemplative-technology>
<app-unknownkiller-technology *ngSwitchDefault [technology]="technology"></app-unknownkiller-technology>
</div>
NgIf:
<p *ngIf="true">
expression is true ngIf is true. this paragraph is in DOM.
</p>
<p *ngIf="false">
expression is false ngIf is false. this paragraph is not in DOM.
</p>
<p [style.display]="'block'">expression sets display block. paragraph is visible.
</p>
<p [style.display]="'none'">expression sets display none. paragraph is hidden but still in DOM.
</p>
Asterisk Prefix with Desugaring Process:
<div *ngIf="myTechnology">{{ myTechnology.name }}</div>
<div template="ngIf myTechnology">{{ myTechnology.name }}</div>
<ng-template [ngIf]="myTechnology">
<div>{{ myTechnology.name }}</div>
</ng-template>
Inside ngFor:
<div *ngFor="let technology of technologies; let i = index; let odd = odd; trackBy: trackById;"
[class.odd]="odd">
({{ i }}) {{ technology.name }}
</div>
<hr>
<div template="ngFor let technology of technologies; let i = index; let odd = odd; trackBy: trackById;"
[class.odd]="odd">
({{ i }}) {{ technology.name }}
</div>
<hr>
<ng-template ngFor let-technology [ngForOf]="technologies" let-i="index" let-odd="odd"
[ngForTrackBy]="trackById">
<div [class.odd]="odd">({{ i }}) {{ technology.name }}</div>
</ng-template>
Inside NgSwitch Directives
Technology Switch Components:
import { Component, Input } from '@angular/core';
import { Technology } from './technology';
@Component({
selector: 'app-happy-technology',
template: `Wow. You like {{ technology.name }}. What a happy technology ... just like you.`
})
export class HappyTechnologyComponent {
@Input() technology: Technology;
}
@Component({
selector: 'app-awesome-technology',
template: `You are as awesome as {{ technology.name }}. May the sun always shine over you.`
})
export class AwesomeTechnologyComponent {
@Input() technology: Technology;
}
@Component({
selector: 'app-contemplative-technology',
template: `Are you as contemplative as {{ technology.name }}?`
})
export class ContemplativeTechnologyComponent {
@Input() technology: Technology;
}
@Component({
selector: 'app-unknownkiller-technology',
template: `{{ message }}`
})
export class UnknownKillerTechnologyComponent {
@Input() technology: Technology;
get message() {
return this.technology && this.technology.name ?
`${this.technology.name} is strange and mysterious` : 'Are you feeling a view to a kill?';
}
}
export const technologiesSwitchComponents = [
HappyTechnologyComponent, AwesomeTechnologyComponent,
ContemplativeTechnologyComponent, UnknownKillerTechnologyComponent
];
AppComponent NgSwitch desugared (1 chromebook && 1 pound of sugar for my Polymer Princess):
<div *ngFor="let technology of technologies">{{ technology.name }}</div>
<div [ngSwitch]="technology?.emotion">
<app-happy-technology *ngSwitchCase="'happy'" [technology]="technology"></app-happy-technology>
<app-awesome-technology *ngSwitchCase="'awesome'" [technology]="technology"></app-awesome-technology>
<app-contemplative-technology *ngSwitchCase="'contemplative'" [technology]="technology"></app-contemplative-technology>
<app-unknownkiller-technology *ngSwitchDefault [technology]="technology"></app-unknownkiller-technology>
</div>
<div [ngSwitch]="technology?.emotion">
<app-happy-technology template="ngSwitchCase 'happy'" [technology]="technology"></app-happy-technology>
<app-awesome-technology template="ngSwitchCase 'awesome'" [technology]="technology"></app-awesome-technology>
<app-contemplative-technology template="ngSwitchCase 'contemplative'" [technology]="technology"></app-contemplative-technology>
<app-unknownkiller-technology template="ngSwitchDefault" [technology]="technology"></app-unknownkiller-technology>
</div>
<div [ngSwitch]="technology?.emotion">
<ng-template [ngSwitchCase]="'happy'">
<app-happy-technology [technology]="technology"></app-happy-technology>
</ng-template>
<ng-template [ngSwitchCase]="'awesome'">
<app-awesome-technology [technology]="technology"></app-awesome-technology>
</ng-template>
<ng-template [ngSwitchCase]="'contemplative'">
<app-contemplative-technology [technology]="technology"></app-contemplative-technology>
</ng-template>
<ng-template ngSwitchDefault>
<app-unknownkiller-technology [technology]="technology"></app-unknownkiller-technology>
</ng-template>
</div>
Let's have a look at the results in our Chromium Web Browser:

Template Tag:
<p>Hip!</p>
<ng-template>
<p>Hip!</p>
</ng-template>
<p>Array!</p>
NgContainer:
<p>
I looked up
<ng-container *ngIf="technology">
and saw {{ technology.name }}. I greeted
</ng-container>
and continued coding.
</p>
<div>
Pick your favorite technology
(<label><input type="checkbox" checked (change)="showAwesome = !showAwesome">show
awesome</label>)
</div>
<select [(ngModel)]="technology">
<ng-container *ngFor="let tech of technologies">
<ng-container *ngIf="showAwesome || tech.emotion !== 'awesome'">
<option [ngValue]="tech">{{ tech.name }} ({{ tech.emotion }})</option>
</ng-container>
</ng-container>
</select>
Let's view the running application in our Browser:

Unless Directive, Structural Directive, Does Opposite of NgIf Directive:
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appUnless]'})
export class UnlessDirective {
private hasView = false;
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
constructor(private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
}
AppComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h1>{{ title }}</h1>
<div class="left">
<p>
The condition is currently
<span [ngClass]="{ 'a': !condition, 'b': condition, 'unless': true }">
{{ condition }}
</span>
<button (click)="condition = !condition" [ngClass]="{ 'a': condition, 'b': condition }">
Toggle condition to {{ condition ? 'false' : 'true' }}
</button>
</p>
<p *appUnless="condition" class="unless a">
(A) this paragraph is displayed because condition is false.
</p>
<p *appUnless="!condition" class="unless b">
(B) although the condition is true, this paragraph is displayed because
appUnless is set to false.
</p>
<h4>UnlessDirective with Template</h4>
<p *appUnless="condition">Show this sentence unless the condition is true.</p>
<p template="appUnless condition" class="unless">
(A) this paragraph is shown when condition is false.
</p>
<ng-template [appUnless]="condition">
<p class="unless">
(A) this paragraph is also only shown unless the condition is true.
</p>
</ng-template>
</div>
<div class="right">
<img [src]="imageUrl" class="img-round" alt="princess image">
</div>
`,
styles: [`
h1 { font-weight: bold; }
.unless { border: 3px solid; }
p.unless { width: 500px; }
button.a, span.a, .unless.a {
color: red;
border-color: gold;
background-color: yellow;
font-size: 140%;
}
button.b, span.b, .unless.b {
color: black;
border-color: green;
background-color: light-green;
font-size: 160%;
}
`]
})
export class AppComponent {
title = 'Tour of Technologies';
imageUrl = '../assets/polymer1.jpg-large';
condition = false;
}
Let's view the results in Chromium:

NgContainer:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
<app-panel>
<ng-container class="header"><h1>Angular 5</h1></ng-container>
<ng-container class="body">
<h3>One Framework. Mobile & desktop.</h3>
<p>Angular CLI. Tool, Scaffold, Build Maintain Angular Applications.</p>
<p>Angular Material. Beautiful CSS optimized for Angular.</p>
</ng-container>
</app-panel>
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
}
PanelComponent:
import { Component } from '@angular/core';
@Component({
selector: 'app-panel',
template: `
<div class="card text-white bg-success mb-3">
<div class="card-header">
<ng-content select=".header"></ng-content>
</div>
<div class="card-body">
<ng-content select=".body"></ng-content>
</div>
</div>
`
})
export class PanelComponent {
}
Let's view the results in our Chromium Web Browser:

NgIf:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<div *ngIf="technologies.length > 0; then technologiesList else noTechnologies;"></div>
<ng-template #technologiesList>
<h2 class="list-group-item list-group-item-danger">{{ title }}</h2></ng-template>
<ng-template #noTechnologies><b>No technologies yet</b></ng-template>
<ul class="list-group">
<li *ngFor="let technology of technologies" class="list-group-item list-group-item-action">
{{ technology }}
</li>
</ul>
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
technologies = ['Angular 5.0.0', 'Angular CLI 1.5.0', 'Angular Material 2.0.0', 'Ionic 3.0.0'];
}
Hidden Property:
<h2 [hidden]="technologies.length === 0" class="list-group-item list-group-item-danger">{{ title }}</h2>
<div [hidden]="technologies.length > 0">No technologies yet</div>
technologies = ['Angular 5.0.0', 'Angular CLI 1.5.0', 'Angular Material 2.0.0', 'Ionic 3.0.0'];
Let's view the results in our Chromium Browser:

NgSwitch Directives Revisited:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2 class="list-group-item list-group-item-danger">{{ title }}</h2>
<ul class="nav nav-pills">
<li class="nav-item">
<a class="nav-link" [class.active]="viewMode === 'list'"
(click)="viewMode = 'list'">List View</a>
</li>
<li class="nav-item">
<a class="nav-link" [class.active]="viewMode === 'card'"
(click)="viewMode = 'card'">Card View</a>
</li>
</ul>
<div [ngSwitch]="viewMode">
<div *ngSwitchCase="'list'">
<ul class="list-group">
<li *ngFor="let technology of technologies" class="list-group-item list-group-item-action">
{{ technology }}
</li>
</ul>
</div>
<div *ngSwitchCase="'card'">
<div class="card text-white bg-success mb-3">
<div *ngFor="let technology of technologies" class="card-title">
{{ technology }}
</div>
</div>
</div>
<div *ngSwitchDefault>Default</div>
</div>
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
viewMode = 'list';
technologies = ['Angular 5.0.0', 'Angular CLI 1.5.0', 'Angular Material 2.0.0', 'Ionic 3.0.0'];
}
NgFor:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
<ul>
<li *ngFor="let technology of technologies; index as i;
odd as isOdd; even as isEven; first as isFirst; last as isLast;">
({{ i }}) - {{ technology.name }}
<span *ngIf="isOdd">(ODD)</span>
<span *ngIf="isEven">(EVEN)</span>
<span *ngIf="isFirst">(FIRST)</span>
<span *ngIf="isLast">(LAST)</span>
</li>
</ul>
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
technologies = [
{ id: 1, name: 'Angular 5' },
{ id: 2, name: 'Angular CLI 1.5' },
{ id: 3, name: 'Angular Material 2' },
{ id: 4, name: 'Angular Dart 4' }
];
}
NgFor && Change Detection:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
<ul>
<li *ngFor="let technology of technologies">
{{ technology.name }}
<button class="btn btn-warning btn-sm" (click)="onChange(technology)">Update</button>
<button class="btn btn-danger btn-sm" (click)="onRemove(technology)">X</button>
</li>
</ul>
<button class="btn btn-primary" (click)="onAdd()">Add</button>
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
technologies = [
{ id: 1, name: 'Angular 5' },
{ id: 2, name: 'Angular CLI 1.5' },
{ id: 3, name: 'Angular Material 2' },
{ id: 4, name: 'Angular Dart 4' }
];
onAdd() {
this.technologies.push({id: 5, name: 'Angular Universal' });
}
onRemove(technology) {
const index = this.technologies.indexOf(technology);
this.technologies.splice(index, 1);
}
onChange(technology) {
technology.name = technology.name + ' UPDATED';
}
}
NgFor & TrackBy:
import { Component } from '@angular/core';
@Component({
selector: 'app-technologies',
template: `
<div class="row">
<div class="col-6">
<h2>{{ title }}</h2>
<button class="btn btn-danger" (click)="loadTechnologies()">Load Technologies</button>
<ul>
<li *ngFor="let technology of technologies; trackBy: trackTechnology;">
{{ technology.name }}
</li>
</ul>
</div>
<div class="col">
<img class="w-100 p-3 rounded-circle" [src]="imageUrl" />
</div>
</div>
`
})
export class TechnologiesComponent {
title = 'List of Technologies';
imageUrl = './assets/polymer1.jpg';
technologies;
loadTechnologies() {
this.technologies = [
{ id: 1, name: 'Angular 5' },
{ id: 2, name: 'Angular CLI 1.5' },
{ id: 3, name: 'Angular Material 2' },
{ id: 4, name: 'Angular Dart 4' }
];
}
trackTechnology(index, technology) {
return technology ? technology.id : undefined;
}
}
The Leading Star in NgIf (desugared):
<div *ngIf="technologies.length > 0; else noTechnologies;">
<h2>{{ title }}</h2>
</div>
<ng-template #noTechnologies>
<h2>No Technologies</h2>
</ng-template>
<ng-template [ngIf]="technologies.length > 0">
<div><h2>{{ title }}</h2></div>
</ng-template>
<ng-template [ngIf]="!(technologies.length > 0)">
<h2>No Technologies</h2>
</ng-template>