Angular 2 client-side TypeScript framework

Second Brew

In the Darkness Bind Them

Figure 4 takes a closer look at the binding mechanisms in Angular 2. The Template area on the left lists the directives. They point to either the val variable or the callback function chg() from the Component area on the right.

Figure 4: The binding mechanisms between the template and the component in Angular 2.

The arrows show the paths of changed values and states. For example, if the component saves a value of 3 in val, the expression {{val}} also changes to 3 in the template. The same is true for the text fields, which contain the attribute directives in the form [(ngModel)]="val" or [ngModel]="val".

Angular 2 also updates function return values, as shown in line 3 of Listing 3. It uses the avg() function to update the computed mean temperature and pressure values (Figure 1) as soon as the app saves another set of measurement values.

The new attribute directive ngModel – in contrast to its predecessor ng-model (note the hyphen) – offers one-way bindings in the form of [ngModel] and (ngModel). The [(ngModel)] combination arranges two-way bindings for the two form fields in lines 5 and 6, as in Angular 1. Perhaps the critical discussions on two-way bindings have convinced the makers of Angular to retrofit one-way bindings like competitor React [8].

Line 7 of Listing 3 uses parentheses to invoke the add() callback function in the component. The directive used in Angular 1 ng-submit is history, and ngFor (line 15) now takes over the work of ng-repeat. The star operator converts the selected tr element, along with its child elements, into a master template, which the app adds to the value of the control variable once only per list item.

In the value of the attribute directive ngFor, the let keyword declares the control variable data; the of operator iterates against the values in the datas list.

To extract the values from the control variable data, lines 16 to 18 use the interpolation operator ({{...}}). The first line passes the value of the component date through a pipe (|) to the date directive, formatting the date to reflect the locale.

Component Glue

Although Angular 2 was originally intended to support ECMA script 6, the current language standard for JavaScript, TypeScript, assumes this role; it is a superset of JavaScript created by Microsoft that complements the expressiveness and robustness of ECMA script with language elements and plausibility checks. Table 1 compares TypeScript with the relevant ECMA script standards and focuses on the language elements of Angular 2. It also shows how deep into the bag of tricks TypeScript needs to dip to convert an application to ECMA script 5.

Table 1

Comparison of Language Resources

Resource ECMA Script 5 ECMA Script 6 ECMA Script 7 TypeScript
Static Types Yes
Classes Yes Yes Yes
Interfaces Yes
Decorations Yes Yes
Observables Yes Yes

Because of the benefits offered by TypeScript, users would do well to use it in the future when they write new apps. Alternatively, you could turn to JavaScript or even Google's programming language Dart [9].

Listing 4 shows the handler from the app.component.ts file, which takes care of the myApp directive (Listing 2) and is implemented as a component. The second line imports the Component decorator and the OnInit interface from Angular's core package; the next line retrieves the StackService and Weather classes from the stack.service.ts (Listing 5) and weather.ts (Listing 6) files.

Listing 4

app/app.component.ts

01 // The component replaces the <myApp> directive with the actual application, </myApp>
02 import { Component, OnInit } from '@angular/core';
03 import { Weather }      from './weather';
04 import { StackService } from './stack.service';
05
06 @Component({
07   selector: 'myApp',
08   templateUrl: 'app/app.component.html',
09 })
10 export class AppComponent implements OnInit {
11   datas: Weather[] = [];
12   model: Weather = new Weather(8, 1080);
13   constructor(private stackService: StackService) {}
14   avg(field: string): number { return this.stackService.avg(field); };
15   add(): void { this.stackService.add(new Weather(this.model.temp, this.model.press)); };
16   ngOnInit(): void { this.datas = this.stackService.datas}
17 }

The code creates the component AppComponent as a class. To do so, the decorator @Component first provides the class definition (line 10) with the properties of a component; the two configuration objects are used for this purpose. In line 7, selector selects the user-defined myApp directive, which is also available in index.html (Listing 2) as the field of activity; templateUrl links the template with the HTML file from Listing 3 one line later.

In contrast to its predecessor, Angular 2 only allows templates in conjunction with components. The type declaration of a variable with TypeScript follows a colon (Listing 4, line 11). Thus, datas expects a list of user-defined weather types from the Weather class (discussed later) and stores all the sets of readings. The model variable, in comparison, is simply a weather type; it is used with the [(ngModel)] directive to read values from the form fields (Listing 3, lines 5 and 6). Incidentally, a simple (ngModel) is all you need for reading.

The component implements the OnInit interface at the same time. Classes that it implements leverage services like StackService. Conveniently, Angular can produce services as singletons [10]; it then distributes them (like components or pipes) via the directives' constructor function (line 13). The ngOnInit() handler then links the datas list with the component of the same name from the StackService type object. The avg() and add() methods wrap the methods of the same name from StackService.

Injection

Listing 5 shows the StackService class, a simple data store from the app/stack.service.ts file. Line 2 first imports the Injectable decorator, making the StackService class injectable. The datas list in line 6 stores the application data. The any data type guarantees a universally usable data store, because any accepts any type of data. The add() method acts as an interface for storing new elements.

Listing 5

app/stack.service.ts

01 // Service class for storing application data
02 import { Injectable } from '@angular/core';
03
04 @Injectable()
05 export class StackService {
06   public datas: any[] = [];
07   add(obj: any): void { this.datas.push(obj); };
08   avg(fld: string): number {
09     if (this.datas.length > 0) {
10       return this.rnd(this.datas.reduce((s, o) => s + parseFloat(o[fld]), 0)/this.datas.length);
11     };
12     return 0;
13   };
14   rnd(val: number, digits: number = 2) {
15     let pre = Math.pow(10, digits);
16     return Math.round(val*pre)/pre;
17   };
18 }

Line 10 computes the arithmetic mean of all stored objects for the component handed over in the fld call parameter. If the list is not empty, method reduce() uses the component to compute the sum of all stored objects. The sum is then divided by the length of the list. Then, rnd() rounds the calculated mean value to two decimal places. For an empty list, avg returns 0 here for demonstration purposes.

In line 14, the rnd() helper method grabs the number to be rounded as val and the number of decimal places in digits. The line below this generates the value of 10 to the power digits. Line 16 saves the desired decimal places by multiplying first before rounding, to then put them back behind the decimal point with division.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus