Angular 2 is a client-side framework to build web applications. It is very flexible in terms of being used with both mobile and web platforms. The basic advantage of using Angular is that it follows the ECMAScript 6 standard and developers can do object-oriented programming, define classes and interfaces, implement classes, and define data structures using Plain Old JavaScript Objects (POJO) for binding data. Another big advantage in terms of performance is the unidirectional data flow. Unlike Angular 1.x, Angular 2 provides both the option of doing two-way data binding or unidirectional data binding. In certain cases, unidirectional binding is good for performance. For example, when submitting a form, two way bindings with controls may be overkill.
Angular2 consist of a number of components. Each component can be bound to the page by either a selector, for example <my-app> </my-app>
, or a routing module. Each component has a selector, template HTML or template reference link, directives, providers, and a controller class whose properties and methods can be accessed in the associated view. When the web application first starts, System.import
loads the main component of the application, which bootstraps the root component. Here is a sample main component bootstrapping an Angular app:
//Loading module through Import statement Import {AppComponent} from 'path of my component' bootstrap(AppComponent, [Providers]);
Providers can be defined inside square brackets. There are various providers available, which we will discuss in a later chapter.
This bootstrap
object is in angular2/platform/browser
, which can be imported into the TypeScript file with the import
command:
import {bootstrap} from 'angular2/platform/browser';
This bootstrap
object directs Angular to load the component defined in it. When the component is loaded, all the attributes or metadata defined for the component are evaluated. Each component should have the @Component
annotation, some properties to define metadata about the component, and one or more classes termed as component controllers that contain properties and methods accessible by the template defined in the @Component template
or templateUri
properties.
Here is a sample app.component.ts
that contains a selector, a template, and a class, AppComponent
:
//app.component.ts import { Component, View} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; @Component({ selector: "my-app", template: `<p>This is a first component</p>`, }) class AppComponent { } bootstrap(AppComponent);
When the component initializes, it goes through several events and has a very structured life cycle process. We can implement these events to do specific operations. The following table shows the list of events we can use in our component controller class:
A module represents a container that contains classes, interfaces, and more, to export functionality, so other modules can be imported using the import
statement. For example, here is math.ts
, used to perform different arithmetic operations:
//math.ts import {Component} from 'angular2/core'; @Component({ }) export class MathService { constructor() { } public sum(a: number, b: number): number { return a + b; } public subtract(a: number, b: number): number { return a - b; } public divide(a: number, b: number): number { return a / b; } public multiply(a: number, b: number): number { return a * b; } }
A component is a combination of the @Component
annotation to define metadata properties and the associated controller class that contains the actual code, such as the class constructor, methods, and properties. The @Component
annotation contains the following metadata properties:
@Component({ providers: string[], selector: string, inputs: string[], outputs: string[], properties: string[], events: string[], host: { [key: string]: string }, exportAs: string, moduleId: string, viewProviders: any[], queries: { [key: string]: any }, changeDetection: ChangeDetectionStrategy, templateUrl: string, template: string, styleUrls: string[], styles: string[], directives: Array < Type | any[] >, pipes: Array < Type | any[] >, encapsulation: ViewEncapsulation })
When defining a component, we can specify various properties, as listed previously. Here we will see some of the core properties that are often required when creating Angular 2 components:
The following real example contains the template and the selector defined in the component class. When the button is clicked, it will call the logMessage()
method, which prints the message in the <p>
element. If you notice, we have not used the export
keyword with the class because we have already bootstrapped the component on the same file and this component does not need to be referenced anywhere else:
import { Component, View } from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; @Component({ selector: "my-app", template: "<p> {{message}}</p><button (click)='logMessage()'>Log Message</button>" }) class AppComponent { logMessage() { this.message = "Hello World"; } message: string = ""; } bootstrap(AppComponent);
The app selector can be used anywhere in the HTML or index.cshtml
page if working on an ASP.NET project, and the template will be rendered inside it. Here is an example of using the custom tag my-app
:
<html> <body> <my-app></my-app> </body> </html>
Once the page runs, it will render the output with this generated source:
<html> <body> <p>Hello World</p> <button (click)='logMessage()'>Log Message</button> </body> </html>
Inputs allow developers to specify the custom attributes mapped to some property of the component class downward in the hierarchy of components, whereas outputs are used to define custom event handlers on the component that can be raised upward in the hierarchy of components. In short, inputs are used to send data from parent to child components, whereas outputs are used to invoke events from child to parent components. In the previous example, we saw how selectors can be used, and the associated template is rendered in place of a selector, with the provision of having all the members of the component class available. In certain cases, we have to specify some attributes in our custom selector to pass the value to handle particular actions. For example, we may need some attribute in the previous <my-app>
tag to specify the logging type, such as to log on to a developer's console or show an alert message.
In this example, we will create two input attributes, logToConsole
and showAlert
. We can define input attributes in the @Component
annotation. The following code snippet is the separate component defined in child.component.ts
and contains the selector as child; the template displays the Boolean values of the logToConsole
and showAlert
attributes specified in the child tag. The inputs contain the list of string variables that will be defined as the child tag attributes:
//child.component.ts import { Component} from 'angular2/core'; @Component({ selector: 'child', template: `<div> Log to Console: {{logToConsole}}, Show Alert: {{showAlert}} <button (click)="logMessage()" >Log</button> </div>`, inputs: ['logToConsole', 'showAlert'], })
Here is the ChildComponent
class that contains the logToConsole
and showAlert
Boolean variables. These variables actually hold the values supplied from the notification tag. Finally, we have the logMessage()
method that will be invoked on a button click event and either log the message on the developer's console or show an alert message based on the value that has been set by the parent component in the hierarchy:
export class ChildComponent { public logToConsole: boolean; public showAlert: boolean; logMessage(message: string) { if (this.logToConsole) { console.log("Console logging is enabled"); } if (this.showAlert) { alert("Showing alert message is enabled"); } } }
In the app.component.ts
file, where we have the main AppComponent
defined, we can use the child selector as shown in the following code. When defining the child selector, we can set the values for custom inputs defined in the ChildComponent
, logToConsole
and showAlert
. This way the parent component can specify the values to the child component through inputs. Here is the complete code of AppComponent
:
//app.component.ts import { Component, View } from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {ChildComponent} from './child.component'; @Component({ selector: "my-app", template: `<child [logToConsole]=true [showAlert]=true></child>`, directives: [ChildComponent] }) export class AppComponent { } bootstrap(AppComponent);
Outputs are used to invoke events on the parent component from child components in the hierarchy of components. We will modify the preceding example and add the outputs event in the ChildComponent
, then register it in the AppComponent
using the ChildComponent
selector.
Here is the modified code snippet for ChildComponent
:
//child.component.ts import { Component, EventEmitter, Output} from 'angular2/core'; @Component({ selector: 'child', template: `<div> Log to Console: {{logToConsole}}, Show Alert: {{showAlert}} <button (click)="logMessage()" >Log</button> </div>`, inputs: ['logToConsole', 'showAlert'] }) export class ChildComponent { public logToConsole: boolean; public showAlert: boolean; @Output() clickLogButton = new EventEmitter(); logMessage(message: string) { this.clickLogButton.next("From child"); } }
The @Output
property lists clickLogButton
as a custom event that ChildComponent
can emit, which its parent AppComponent
will receive.
We have added EventEmitter
in the import
statement. EventEmitter
is a built-in class that ships with Angular and provides methods for defining and firing custom events. Once the logMessage()
method is executed, it will execute the clickLogButton.next()
method from the ChildComponent
, which finally calls the event registered in the AppComponent
.
We have added the clickLogButton
in the AppComponent
, as shown in the following code. In Angular 2, we can specify the event by specifying the event name in brackets ()
followed by the method that will be called when the event is raised. This is how the event is registered. Here, logMessage
is the local method defined in the AppComponent
:
(clickLogButton)="logMessage($event)" Here is the code snippet for AppComponent: //app.component.ts import { Component, View } from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {ChildComponent} from './child.component'; @Component({ selector: "my-app", template: `<child [logToConsole]=true [showAlert]=true (clickLogButton)="logMessage($event)" ></child>`, directives: [ChildComponent] }) export class AppComponent { logMessage(value) { alert(value); } } bootstrap(AppComponent);
The logMessage
method is the method that will be invoked when the event is raised from the ChildComponent
.
Directives are custom tags that render the HTML at runtime but encapsulate the rendering content in the directive itself. We can relate it to the tag helpers in ASP.NET. There are three kinds of directives, components, structural directives, and attribute directives:
ngIf
, ngSwitch
, and ngFor
are structural directives.Directives can be created in a simple way, as a component is created, and can be referenced in the calling component through its selector tag.
Here is an example of HelloWorldComponent
that defines a simple directive to display a "Hello world"
message in the heading format:
//helloworld.component.ts import {Component} from 'angular2/core'; @Component({ selector: "helloworld", template: "<h1>Hello world</h1>" }) export class HelloWorldComponent { }
The following example is the component that uses this directive. When using any directive, it has to be first imported through the import
statement, then the @Component
metadata property needs to be set to access it in the associated template:
import { Component, View, provide, Inject } from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {HelloWorldComponent} from './helloworld.component'; @Component({ selector: "my-app", template: `<helloworld></helloworld>`, directives: [, HelloWorldComponent], }) export class AppComponent{ } bootstrap(AppComponent);
This directive can be used on the page as follows:
<helloworld></helloworld>
Structural directives can be used to add or remove DOM elements. For example, we can add the list of countries as a table through *ngFor
, as shown in the following code, and hide or unhide the div through the *ngIf
directive:
<div *ngIf="display"> <table> <thead> <tr> <th> Country </th> <th> Currency </th> </tr> </thead> <tbody *ngFor="#country of countries"> <tr><td>{{country.CountryName}}</td><td>{{country.Currency}}</td></tr> </tbody> </table> </div>
Here is the backend countries.component.ts
, which uses the HTTP module to call the ASP.NET Web API service. It returns a list of countries, which is assigned to the countries
array. The display
default value is set to true
, which generates the table. By setting the display
value to false
, the table will not be generated:
///<reference path="../../node_modules/angular2/typings/browser.d.ts" /> import {Component} from 'angular2/core'; import {Http, Response} from 'angular2/http'; @Component({ selector: 'app', templateUrl: 'Countries' }) export class TodoAppComponent { countries = []; display: boolean = true; //constructor constructor(private http: Http) { } //Page Initialized Event Handler ngOnInit() { this.getCountries(); } getCountries() { this.http.get("http://localhost:5000/api/todo").map((res: Response) => res.json()) .subscribe(data => { this.countries = data; }, err => console.log(err), () => console.log("done") ); } }
This is how structural directives can be used in Angular 2. In the following chapter, we develop a sample application and discuss each artifact for making HTTP GET
and POST
requests using Angular 2.
An attribute directive requires building a controller class annotated with @Directive
and defines a selector to identify the attribute associated with it. In the following example, we will develop a simple myFont
directive that changes the text to italic when it is applied to any page elements. Here is the font.directive.ts
file:
import { Directive, ElementRef, Input } from 'angular2/core'; @Directive({ selector: '[myFont]' }) export class FontDirective { constructor(el: ElementRef) { el.nativeElement.style.fontStyle = 'italic'; } }
For each matching element on the page, Angular creates a new instance and injects ElementRef
into the constructor. ElementRef
is a service through which we can directly access the element through its nativeElement
property and access other attributes. In the preceding code snippet, we are changing the font style to italic for the elements that have the myFont
directive applied.
On the page level, it can be used as follows:
<p myFont>myFont is an Attribute directive</p>
Providers are used to register the types that gets instantiated through the dependency injection framework of Angular 2. When a component is initialized, Angular creates a dependency injector which registers all the types specified in the providers array. Then at the constructor level, if there is any type defined in the providers array, it will get initialized and injected into the constructor.
The following example is MathComponent
, which will be injected into the main app component constructor and call the sum method to add two numbers together:
//math.component.ts import { Component } from 'angular2/core'; @Component({}) export class MathComponent { public sum(a: number, b: number) : number{ return a + b; } public divide(a: number, b: number): number { return a / b; } public subtract(a: number, b: number): number { return a - b; } public multiply(a: number, b: number): number { return a * b; } }
The following example is AppComponent
, showing how to import a math
component, then defining the provider and injecting it at the constructor level:
//app.component.ts import { Component, View } from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {MathComponent} from './servicemanager.component'; @Component({ selector: "my-app", template: "<button (click)="add()" >Log</button>", providers: [MathComponent] }) export class AppComponent { obj: MathComponent; constructor(mathComponent: MathComponent) { this.obj = mathComponent; } public add() { console.log(this.obj.sum(1, 2)); } } bootstrap(AppComponent);
Other primitive types can also be injected in a slightly different way using the Inject Angular module. We can also define a type using the provide
keyword, which takes a key and the value:
providers: [provide('Key', {useValue: 'Hello World'})]
The preceding syntax can also be used when defining types in providers, as follows:
providers: [provide(MathComponent, {mathComponent: MathComponent })]
One of the main benefits of defining providers
with the provide
keyword is when testing. When testing applications, we can replace the actual components with the mock or test components. For example, suppose we have a class that calls some SMS service to send SMS using some paid gateway, and in the testing cycle we don't want to use the production SMS gateway component, but rather we would like to have some custom test component that just inserts the SMS into a local database. In this case, we can associate some mock class, such as SMSTestComponent
, to perform testing scenarios.
The following example injects the string value into the constructor. We need to add the Inject module as specified in the following code, and then use @Inject
to inject the value associated to the key:
//app.component.ts import { Component, View, provide, Inject } from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {MathComponent} from './servicemanager.component'; @Component({ selector: "my-app", template: `button (click)="logMessage()" >Log</button>`, providers: [MathComponent, provide('SampleText', {useValue: 'Sample Value'})] }) export class AppComponent{ obj: MathComponent; Val: string; constructor(mathComponent: MathComponent, @Inject('SampleText') value) { this.obj = mathComponent; this.Val = value; } public logMessage() { alert(this.kVal); } } bootstrap(AppComponent);
Angular chains dependency injection and injects components into the child components if they are defined in the providers array of the parent component. However, a child component can define the same component in its own providers array. The scope of the component travels through the chain of components. However, components that are defined in the viewproviders
array aren't injected or inherited by the child components in the hierarchical chain.
Let's take a simple example that contains one main component in app.ts
and AppComponent
defines two providers: ChildComponent
and MathComponent
. ChildComponent
is the child of the parent component, whereas MathComponent
is used in both the parent and child components. If you notice, in the following code snippet, we have not specified the MathComponent
in the providers
array of the ChildComponent
, and as it is defined in the ParentComponent
, it is already injected by the Angular dependency injection module.
Here is the code snippet for AppComponent
(parent):
//app.component.ts import { Component} from 'angular2/core'; import {bootstrap} from 'angular2/platform/browser'; import {MathComponent} from './servicemanager.component'; import {ChildComponent} from './child.component'; @Component({ selector: "my-app", template: `<button (click)="callChildComponentMethod()">Log</button>`, providers: [MathComponent, ChildComponent] }) export class AppComponent { childObj: ChildComponent; constructor(childComponent: ChildComponent) { this.childObj = childComponent; } public callChildComponentMethod() { this.childObj.addNumbers(1, 2); } } bootstrap(AppComponent);
The following is the code snippet for MathComponent
, which contains some basic arithmetic operations:
//math.component.ts import { Component } from 'angular2/core'; @Component({}) export class MathComponent { public sum(a: number, b: number) : number{ return a + b; } public divide(a: number, b: number): number { return a / b; } public subtract(a: number, b: number): number { return a - b; } public multiply(a: number, b: number): number { return a * b; } }
Finally, here is the ChildComponent
code, which does not have the MathComponent
provider defined in the providers
array:
//child.component.ts import {Component} from 'angular2/core'; import {MathComponent} from './servicemanager.component'; @Component({ selector: 'child-app', template: '<h1>Hello World</h1>' }) export class ChildComponent { obj: MathComponent; constructor(mathComponent: MathComponent) { this.obj = mathComponent; } public addNumbers(a: number, b: number) { alert(this.obj.sum(a, b)); } }
Routing has an essential role when working with large applications. Routing is used to navigate to different pages. Routing can be defined in three steps:
@RouteConfig
at any component level:@RouteConfig([ { path: '/page1', name: 'Page1', component: Page1Component, useAsDefault: true }, { path: '/page2', name: 'Page2', component: Page2Component }] )
[routerLink]
attribute on the anchor HTML tag and specify the route name configured in @RouteConfig
.<router-outlet>
tag to render the page on the current navigated route.The following example contains two components, Page1Component
and Page2Component
, and the main AppComponent
has routing defined like this:
//app.component.ts import {Component} from 'angular2/core'; import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; import {Page1Component} from './page1.component'; import {Page2Component} from './page2.component'; @Component({ selector: "my-app", template: `{{name}} <a [routerLink]="['Page2']">Page 2</a> <router-outlet></router-outlet>`, directives: [ROUTER_DIRECTIVES], }) @RouteConfig([ { path: '/', name: 'Page1', component: Page1Component, useAsDefault:true }, { path: '/page2', name: 'Page2', component: Page2Component }] ) export class AppComponent { }
In the preceding code, first we imported the RouteConfig
and ROUTER_DIRECTIVES
from angular2/router
and then defined the RouteConfig
for page 1 and page 2. In the inline template, we placed the anchor tag and defined the route name for page 2. When the application runs, page 1 is set as a default page on a root path /
, so the page 1 content will be displayed in place of the router outlet. When the user clicks on the Page2
link, the page 2 content will be rendered in the same place.
Here is the code of page1.component.ts
:
//page1.component.ts import {Component} from 'angular2/core'; @Component({ template:'<h1>Page1 Content</h1>' }) export class Page1Component { }
Here is the code of page2.component.ts
:
//page2.component.ts import {Component} from 'angular2/core'; @Component({ template: '<h1>Page2 Content</h1>' }) export class Page2Component { }
3.21.166.99