I am here to continue the discussion around AngularJS 2.0. Today, we will discuss Structural Directives in AngularJS 2. Also, in case, you did not have a look at the previous articles of this series, go through the links, mentioned below.
In my previous article, I already discussed about the attribute directives in Angular 2.0. In that article, we discussed about some inbuilt attribute directives and also discussed about how to create custom attribute directives. Now in this article, I will discuss about another part of the directives -- structural directives. Components are simply directives with templates and they are the most commonly used in our application. Attribute directives are basically used to change the appearance of an element in our application, whereas structural directives can be used to modify the DOM by adding or removing any DOM elements and for this reason, it can be used in different scenarios.
In Angular 2.0, there are some in built structural directives i.e. *ngIf, *ngFor, *ngSwitch etc. Normally structural directives are prefixed with an asterisk which signals to Angular that the structure of the DOM element within the directive may change on depend of the some conditions.
ngIf
In Angular version 1.x, there was the ng-show and ng-hide directives which would show or hide the DOM elements on what the given expression evaluates by setting the display CSS property. In Angular 2.0, these two directives have been removed from the framework and a new directive named ngIf was introduced. The main difference of ngIf directive over ng-show or ng-hide is that it actually removes the element or components entirely from DOM. For ng-show or ng-hide, angular keeps the DOM elements/components in the page, so any component behaviors may keep running even if the component is not visible in the page. In Angular 2.0, ng-show or ng-hide directive is not available but we can obtain the same functionality by using the [style.display] property of any element. Now, one question always arises in our mind which is why angular removes component or elements from DOM in the case of ngIf directives? Actually, although in the earlier version, it hides the component or element, still the elements or components are attached with DOM. So it continues to fire its event listener. Also it keeps changing while the model data has been changed due to model binding. So in this way, this invisible components or elements use resources which might be useful for somewhere else. The performance and memory burden can be substantial and the user may not benefit at all. Setting ngIf value to false does effect the component resource consumptions. Angular removes the element from DOM, stops the change detection for the associated component, detaches it from DOM events, and destroys the components. The component can be garbage collected to free up memory. Components often have child components which themselves have children. All of them have been destroyed when ngIf destroys the common ancestor.
For demonstrating the ngIf, do the following steps,
Step 1
First Create the app.module.ts file and add the below code,
- import { NgModule } from '@angular/core';
- import { FormsModule } from '@angular/forms';
- import { BrowserModule } from '@angular/platform-browser';
-
- import { ParentComponent } from './src/app.component.parent';
- import { NgIfComponent } from './src/app.component.ngIf';
-
- @NgModule({
- imports: [BrowserModule, FormsModule],
- declarations: [ParentComponent, NgIfComponent],
- bootstrap: [ParentComponent]
- })
- export class AppModule { }
Step 2
Now create another ts file named app.component.parent.ts and add the below code,
- import { Component, OnInit } from '@angular/core';
-
- @Component({
- moduleId: module.id,
- selector: 'structural-directive',
- templateUrl: 'app.component.parent.html',
- })
-
- export class ParentComponent implements OnInit {
- constructor() { }
-
- ngOnInit() { }
- }
Step 3
Now create a html file named app.component.parent.html and add the below code,
- <div>
- <h2 class="badge">Structural Directives Demonstrate</h2>
- <br />
- <toggle-text></toggle-text>
- </div>
Step 4
Now create another ts file named app.component.ngIf.ts and add the below code,
- import { Component, OnInit, Input } from '@angular/core';
-
- @Component({
- moduleId: module.id,
- selector: 'toggle-text',
- templateUrl: 'app.component.ngIf.html'
- })
-
- export class NgIfComponent implements OnInit {
- showInfo: boolean = false;
- caption: string = 'Show Text';
-
- constructor() { }
- ngOnInit() { }
-
- changeData(): void {
- this.showInfo = !this.showInfo;
- if (this.showInfo) {
- this.caption = 'Hide Text';
- }
- else {
- this.caption = 'Show Text';
- }
- }
- }
Step 5
Now create another html file named app.component.ngif.html and add the below code,
- <div>
- <input type="button" value="{{caption}}" (click)="changeData()"/>
- <br />
- <h2 *ngIf="showInfo"><span>Demonstrate of Structural Directives - *ngIf</span></h2>
- </div>
Step 6
Now create another html file named index.html and add the below code,
- <!DOCTYPE html>
- <html>
- <head>
- <title>Angular2 - Structural Directivestitle>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1">
- <link rel="stylesheet" href="../styles.css">
-
-
- <script src="../node_modules/core-js/client/shim.min.js"></script>
- <script src="../node_modules/zone.js/dist/zone.js"></script>
- <script src="../node_modules/reflect-metadata/Reflect.js"></script>
- <script src="../node_modules/systemjs/dist/system.src.js"></script>
-
- <script src="../systemjs.config.js"></script>
- <script>
- System.import('app').catch(function(err){ console.error(err); });
- </script>
- </head>
-
- <body>
- <structural-directive>Loading</structural-directive>
- </body>
- </html>
Now run the code and the output as below,
ngFor
The ngFor directives instantiates a template once per item from an iterable. The context of each instantiated template inherits from the outer context with the given loop variable. ngFor provides several exported values that can be used to local variables :-
- index will be set to the current loop iteration for each template context
- first will be set to a boolean value indicating whether the item is the first one in the iteration.
- last will be set to a boolean value indicating whether the item is the last one in the iteration.
- even will be set to a boolean value indicating whether this item has an even index.
- odd will be set to a boolean value indicating whether this item has an odd index
Angular uses object identity to track insertions and deletions within the iterator and reproduce those changes in the DOM. This has important implications for animations and any stateful controls (such as <input> elements which accept user input) that are present. Inserted rows can be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state such as user input.
For demonstrate the ngFor directive, do the following code,
Step 1
Add a ts file named app.component.ngFor.ts and add the below code,
- import { Component, OnInit, Directive } from '@angular/core';
-
- @Component({
- moduleId: module.id,
- selector: 'product-list',
- templateUrl: 'app.component.ngFor.html'
- })
-
- export class NgForComponent implements OnInit {
- productList: Array<string> = ['IPhone','Galaxy 7.0','Blackberry 10Z'];
-
- constructor() { }
- ngOnInit() { }
- }
Step 2
Add a html file named app.component.ngFor.html and add the below code,
- <div>
- <h2>Demonstrate ngFor</h2>
- <ul>
- <li *ngFor="let item of productList">
- {{item}}
- </li>
- </ul>
- </div>
Now add the product-list selector tag within app.component.parent.html file and also take the reference of ngForComponent within the app.module.ts file and then run the code,
ngSwitch
The ngSwitch directives is actually made of two directives, an attribute directive and a structural directive. It is similar to switch statement in javascript or other languages. ngSwitch stamps our nested views when their match expression value matches the value of the switch expression. The expression bound to the directives defines what will compared against in the switch structural directives. If an expression bound to ngSwitchCase matches the one given to ngSwitch, those components are created and the others destroyed. If none of the cases match, then components that have ngSwitchDefault bound to them will be created and the others destroyed. Note that multiple components can be matched using ngSwitchCase and in those cases all matching components will be created. Since components are created or destroyed be aware of the costs in doing so.
For demonstrating this, do the following,
Step 1
Add a ts file named app.component.ngSwitch.ts and add the below code,
- import { Component, OnInit, Directive } from '@angular/core';
-
- @Component({
- moduleId: module.id,
- selector: 'student-list',
- templateUrl: 'app.component.ngSwitch.html'
- })
-
- export class NgSwitchComponent implements OnInit {
- studentList: Array<any> = new Array<any>();
-
- constructor() { }
- ngOnInit() {
- this.studentList = [
- { SrlNo: 1, Name: 'Rajib Basak', Course: 'Bsc(Hons)', Grade: 'A' },
- { SrlNo: 2, Name: 'Rajib Basak1', Course: 'BA', Grade: 'B' },
- { SrlNo: 3, Name: 'Rajib Basak2', Course: 'BCom', Grade: 'A' },
- { SrlNo: 4, Name: 'Rajib Basak3', Course: 'Bsc-Hons', Grade: 'C' },
- { SrlNo: 5, Name: 'Rajib Basak4', Course: 'MBA', Grade: 'B' },
- { SrlNo: 6, Name: 'Rajib Basak5', Course: 'MSc', Grade: 'B' },
- { SrlNo: 7, Name: 'Rajib Basak6', Course: 'MBA', Grade: 'A' },
- { SrlNo: 8, Name: 'Rajib Basak7', Course: 'MSc.', Grade: 'C' },
- { SrlNo: 9, Name: 'Rajib Basak8', Course: 'MA', Grade: 'D' },
- { SrlNo: 10, Name: 'Rajib Basak9', Course: 'B.Tech', Grade: 'A' }
- ];
- }
- }
Step 2
Add a html file named app.component.ngSwitch.html and add the below code,
- <div>
- <h2>Demonstrate ngSwitch</h2>
- <table style="width:100%;border:solid;border-color:blue;border-width:thin;">
- <thead>
- <tr >
- <td>Srl No</td>
- <td>Student Name</td>
- <td>Course</td>
- <td>Grade</td>
- </tr>
- </thead>
- <tbody>
- <tr *ngFor="let student of studentList;" [ngSwitch]="student.Grade">
- <td>
- <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.SrlNo}}</span>
- <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.SrlNo}}</span>
- <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.SrlNo}}</span>
- <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.SrlNo}}</span>
- </td>
- <td>
- <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.Name}}</span>
- <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.Name}}</span>
- <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.Name}}</span>
- <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.Name}}</span>
- </td>
- <td>
- <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.Course}}</span>
- <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.Course}}</span>
- <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.Course}}</span>
- <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.Course}}</span>
- </td>
- <td>
- <span *ngSwitchCase="'A'" [ngStyle]="{'font-size':'18px','color':'red'}">{{student.Grade}}</span>
- <span *ngSwitchCase="'B'" [ngStyle]="{'font-size':'16px','color':'blue'}">{{student.Grade}}</span>
- <span *ngSwitchCase="'C'" [ngStyle]="{'font-size':'14px','color':'green'}">{{student.Grade}}</span>
- <span *ngSwitchDefault [ngStyle]="{'font-size':'12px','color':'black'}">{{student.Grade}}</span>
- </td>
- </tr>
- </tbody>
- </table>
- </div>
Step 3
Final code of app.module.ts file,
- import { NgModule } from '@angular/core';
- import { FormsModule } from '@angular/forms';
- import { BrowserModule } from '@angular/platform-browser';
-
- import { ParentComponent } from './src/app.component.parent';
- import { NgIfComponent } from './src/app.component.ngIf';
- import { NgForComponent } from './src/app.component.ngFor';
- import { NgSwitchComponent } from './src/app.component.ngSwitch';
-
-
- @NgModule({
- imports: [BrowserModule, FormsModule],
- declarations: [ParentComponent, NgIfComponent, NgForComponent, NgSwitchComponent],
- bootstrap: [ParentComponent]
- })
- export class AppModule { }
Step 4
Final code of app.component.parent.html file,
- <div>
- <h2 class="badge">Structural Directives Demonstrate</h2>
- <br />
- <toggle-text></toggle-text>
- <br />
- <product-list></product-list>
- <br />
- <student-list></student-list>
- </div>
Now run the code and output as below,