Introduction to Angular 2
Introduction to Angular 2
AJ Meyghani
Buy on Leanpub

Table of Contents

Notes

  • The book assumes that you are working in a Unix-like environment. If you are on Windows you can use Cygwin so that you can follow along with the bash terminal commands.
  • All the project files for the book are hosted on github: https://github.com/aminmeyghani/angular2-intro. You can clone the repository and check out the project files. Throughout the book, you will see references to the project files. Those refer to this repository. For example, angular2-intro/project-files/hello-angular refers to the hello-angular folder inside the project-files folder.
  • Make sure you have git installed on your machine. That is, make sure you get an output for git --version.
  • The book assumes that you have a working knowledge of JavaScript and Angular 1.x
  • Node is heavily used throughout the book. Make sure that you follow the “Node” chapter to install Node and set permissions correctly.
  • All the keyboard shortcuts are mac-based. But if you are using a non-mac machine, you can almost always replace command with ctrl and you should be good. For example, if you a see a shortcut like command + shift + b, you can use ctrl + shift + b where ctrl is obviously the control key!

Installing Node

You can use nvm to install and manage Node on your machine. Copy the install script and run it:

1 curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.30.1/install.sh | b\
2 ash

After that, make a new terminal window and make sure that it is installed, by running:

1 nvm --help

Now you can use nvm to install Node 0.12.9 by running:

1 nvm install 0.12.9

After that, nvm is going to load version 0.12.9 automatically. If it doesn’t, you can load it in the current shell, with:

1 nvm use 0.12.9

Note that you can load any node version in the current shell with nvm use 0.x.y after installing that version.

Also note that if you want to make 0.12.9 the default Node version on your machine, you can do so by running the following:

1 nvm alias default 0.12.9

Then you can verify that it is the default version by making a new terminal window and typing node -v.

Permissions

Never use sudo to install packages, never do sudo npm install <package>. If you get permission errors while installing without sudo, you can own the folders instead. So for example, if you get an error like:

1 Error: EACCES, mkdir '/usr/local'

you can own the folder with:

1 sudo chown -R `whoami` /usr/local

You can own folders until Node doesn’t complain.

Installing live-server

Install a package to verify that node is installed and everything is wired up correctly. We are going to use live-server through the book. So let’s install that with:

1 npm i -g live-server

Then, you should be able to run live-server in any folder to serve the content of that folder:

1 mdkir ~/Desktop/sample && cd $_
2 live-server .

Visual Studio Code

Visual Studio Code is a good IDE for developing web apps. In this chapter we will look at installing and configuring VSCode.

Visual Studio Code Basics

  • Install Visual Studio Code from: https://code.visualstudio.com/
  • You can open new projects by going to the File > Open tag, to etierh open a folder containing your project or a single file
  • Some useful keyboard shortcuts are:
    • command + b: to close/open the file navigator
    • command + shift + p: to open the prompt
  • To install extensions open the prompt with command + shift + p and type:
    1   > install extension
    
  • You can change the keyboard shortcuts settings from Preferences > Keyboard Shortcuts. Open the settings and then you can add your own shortcuts:
     1   // Place your key bindings in this file to overwrite the defaults
     2   [
     3     {
     4       "key": "cmd+t",
     5       "command": "workbench.action.quickOpen"
     6     },
     7     {
     8       "key": "shift+cmd+r",
     9       "command": "editor.action.format",
    10       "when": "editorTextFocus"
    11     }
    12   ]
    

Setting up VSCode for TypeScript

In this section we are going to set up Visual Studio Code for TypeScript. The project files for this chapter are in angular2-intro/project-files/vscode. You can either follow along or check out the folder to see the final result.

Installing TypeScript

Before anything, we need to install the TypeScript compiler. You can install the TypeScript compiler with npm:

1 npm i typescript -g

Then to verify that it is installed, run tsc -v to see the version of the compiler. You will get an output like this:

1 message TS6029: Version 1.7.5

In addition to the compiler, we also need to install the TypeScript Definition manager for DefinitelyTyped (tsd). You can install tsd with:

1 npm i tsd -g

Using TSD, you can search and install TypeScript definition files directly from the community driven DefinitelyTyped repository. To verify that tsd is installed, run tsd with the version flag:

1 tsd --version

You should get an output like this:

1 >> tsd 0.6.5

After tsd and tsc are installed, we can compile a hello world program:

make a file called hello.ts on your desktop:

1 touch ~/Desktop/hello.ts

Then, put some TypeScript code in the file:

1 echo "const adder = (a: number, b: number): number => a + b;" > ~/Desktop/hello.\
2 ts

Then you can compile the file to JavaScript:

1 tsc ~/Desktop/hello.ts

It should output a file in Desktop/hello.js:

1 var adder = function (a, b) { return a + b; };

Now that your TypeScript compiler setup, we can move on to configuring Visual Studio Code.

Add VSCode Configurations

  • First download and install Visual Studio Code from the VSCode Website
  • After installing VSCode, open it and then make a new window: File > New Window
  • Then, make a folder on your desktop for a new project: mkdir ~/Desktop/vscode-demo
  • After that, open the folder in VSCode: File > open and select the vscode-demo folder on your desktop.
  • Now we need to make three configuration files:
    1. tsconfig.json: configuration for the TypeScript compiler
    2. tasks.json: Task configuration for VSCode to watch and compile files
    3. launch.json: Configuration for the debugger

The tsconfig.json file should be in the root of the project. Let’s make the file and put the following in it:

 1 {
 2   "compilerOptions": {
 3     "experimentalDecorators": true,
 4     "emitDecoratorMetadata": true,
 5     "module": "commonjs",
 6     "target": "es5",
 7     "sourceMap": true,
 8     "outDir": "output",
 9     "watch": true
10   }
11 }

Now to make the tasks.json file. Open the prompt with command + shift + p and type:

1 > configure task runner

Then put the following in the file and save the file:

1 {
2   "version": "0.1.0",
3   "command": "tsc",
4   "showOutput": "silent",
5   "isShellCommand": true,
6   "problemMatcher": "$tsc"
7 }

The last thing that we need to set up is the debugger, i.e. the launch.json file. Right click on the .vscode folder in the file navigator and make a new file called launch.json and put in the following:

 1 {
 2   "version": "0.1.0",
 3   "configurations": [
 4     {
 5       "name": "TS Debugger",
 6       "type": "node",
 7       "program": "main.ts",
 8       "stopOnEntry": false,
 9       "sourceMaps": true,
10       "outDir": "output"
11     }
12   ]
13 }

After you save the file, you should be able to see the debugger in the debugger dropdown options.

Now, we are ready to make the main.ts file in the root of the project:

main.ts

1 const sum = (a: number, b: number): number => a + b;
2 const r = sum(1,2);
3 console.log(r);

Now you can start the task to watch the files and compile as you work. Open the prompt with command + shift + p and type:

1 > run build tasks

you can also use the command + shift + b keyboard shortcut instead. This will start the debugger and watch the files. After making a change to main.ts, you should be able to see the output in the output folder.

After the build task is running, we can put a breakpoint anywhere in our TypeScript code. Let’s put a breakpoint on line 2 by clicking on the margin. Then start the debugger by going to the debugger tab and clicking the green play icon.

Now you should see that the program will stop at the breakpoint and you should be able to step over or into your program.

To stop the task you can terminate it. Open the prompt and type:

1 > terminate running task

You can learn more about running TypeScript with VSCode on MSDN’s blog.

Running VSCode from the Terminal

If you want to run VSCode from the terminal, you can follow the guide on VSCode’s website. Below is the summary of the guide:

MAC

Add the following to your “bash” file:

1 function code () { VSCODE_CWD="$PWD" open -n -b "com.microsoft.VSCode" --args $*\
2 ; }

Linux

1 sudo ln -s /path/to/vscode/Code /usr/local/bin/code

Windows

You might need to log off after the installation for the change to the PATH environmental variable to take effect.

Debugging App from VSCode

The “vscode-chrome-debug” extension allows you to attach VSCode to a running instance of chrome. This makes it very convenient because you can put breakpoints in your TypeScript code and run the debugger to debug your app. Let’s get started.

In order to install the extension open the prompt in VSCode with command + shift + p and type:

1 > install extension

hit enter and then type:

1 debugger for chrome

Then just click on the result to install the extension. Restart VSCode when you are prompted.

After installing the extension, we need to update or create a launch.json file for debugging. You can create one in the .vscode folder. After you created the file, put in the following:

 1 {
 2   "version": "0.1.0",
 3   "configurations": [
 4     {
 5       "name": "Launch Chrome Debugger",
 6       "type": "chrome",
 7       "request": "launch",
 8       "url": "http://localhost:8080",
 9       "sourceMaps": true,
10       "webRoot": "${workspaceRoot}",
11       "runtimeExecutable": "/Applications/Google Chrome.app/Contents/MacOS/Googl\
12 e Chrome",
13       "runtimeArgs": ["--remote-debugging-port=9222", "--incognito"]
14     }
15   ]
16 }

Notes:

  • Depending on your platform you need to change the runtimeExecutable path to Chrome’s executable path. After configuring the debugger you need to have a server running serving the app. You can change the url value accordingly. Also make sure that the webRoot path is set to the root of your web server.
  • After that it is a good idea to close all the instances of chrome. Then, put a breakpoint in your code and run the debugger. If everything is set up correctly, you should see an instance of chrome running in incognito mode. To trigger the breakpoint, just reload the page and you should be able to see the debugger paused at the breakpoint.
  • Also make sure that you have the compiler running so that you can use the JavaScript output and the sourcemaps to use the debugger. See the TypeScript and VSCode set up for more details.

TypeScript Crash-course

In this chapter we will quickly go through the most important concepts in TypeScript so that you can have a better understanding of Angular code that you will write. Knowing TypeScript definitely helps to understand Angular, but again it is not a requirement. The project files for this chapter are in angular2-intro/project-files/typescript.

TypeScript Basics

  • TypeScript is a superset of JavaScript with additional features, among which optional types is the most notable. This means that any valid JavaScript code (ES 2015/2016…) is valid TypeScript code. You can basically change the extension of the file to .ts and compile it with the the TypeScript compiler.
  • TypeScript defines 7 primary types:
    • boolean: var isDone: boolean = false;
    • number: var height: number = 6;
    • string: var name: string = "bob";
    • array: var list:number[] = [1, 2, 3]; also var list:Array<number> = [1, 2, 3];
    • enum: enum Color {Red, Green, Blue};
    • any: var notSure: any = 4;
    • void: function hello(): void { console.log('hello'); }

Interface

  • An Interface is defined using the interface keyword
  • Interfaces are used only during compilation time to check types
  • By convention, interface definitions start with an I, e.g. : IPoint
  • Interfaces are used in classical object oriented programming as a design tool
  • Interfaces don’t contain implementations
  • They provide definitions only
  • When an object implements an interface, it must adhere to the contract defined by the interface
  • An interface defines what properties and methods an object must implement
  • If an object implements an interface, it must adhere to the contract. If it doesn’t the compiler will let us know.
  • Interfaces also define custom types

Basic Interface

Below is an example of an Interface that defines two properties and three methods that implementers should provide implementations for:

 1 interface IMyInterface {
 2   // some properties
 3   id: number;
 4   name: string;
 5 
 6   // some methods
 7   method(): void;
 8   methodWithReturnVal():number;
 9   sum(nums: number[]):number;
10 }

Using the interface above we can create an object that adheres to the interface:

 1 let myObj: IMyInterface = {
 2   id: 2,
 3   name: 'some name',
 4 
 5   method() { console.log('hello'); },
 6   methodWithReturnVal () { return 2; },
 7   sum(numbers) {
 8     return numbers.reduce( (a,b) => { return a + b } );
 9   }
10 };

Notice that we had to provide values to all the properties defined by the Interface, and the implementations for all the methods defined by the Interface.

And then of course you can use your object methods to perform operations:

1 let sum = myObj.sum([1,2,3,4,5]); // -> 15

Classes as Interfaces

Because classes define types as well, they can also be used as interfaces. If you have an interface you can extend it with a class for example:

1 class Point {
2   x: number;
3   y: number;
4 }
5 interface Point3d extends Point {
6   z: number;
7 }
8 const point3d: Point3d = {x: 1, y: 2, z: 3};
9 console.log(point3d.x); // -> 1

First we are defining a class called Point that defines two fields. Then we define an interface called Point3d that extends the Point by adding a third field. An then we create a point of type point3d and assign a value to it. We read the value and it outputs 1.

Classes

  • Classes are heavily used in classical object oriented programming
  • It defines what an object is and what it can do
  • A class is defined using the class keyword followed by a name
  • By convention, the name of the class start with an uppercase letter
  • A class can be used to create multiple objects (instances) of the same class
  • An object is created from a class using the new keyword
  • A class can have a constructor which is called when an object is made from the class
  • Properties of a class are called instance variables and its functions are called the class methods
  • Access modifiers can be used to make them public or private
  • The instance variables are attached to the instance itself but not the prototype
  • Methods however are attached to the prototype object as opposed to the instance itself
  • Classes can inherit functionality from other classes, but you should favor composition over inheritance or make sure you know when to use it
  • Classes can implement interfaces

Let’s make a class definition for a car and incrementally add more things to it. The project files for this section are in angular2-intro/project-files/typescript/classes/basic-class.

Adding an Instance Variable

The Car class definition can be very simple and can define only a single instance variable that all cars can have:

1 class Car {
2   distance: number;
3 }
  • Car is the name of the class, which also defines the custom type Car
  • distance is a property that tracks the distance that car has traveled
  • Distance is of type number and only accepts number type.

Now that we have the definition for a car, we can create a car from the definition:

1 let myCar:Car = new Car();
2 myCar.distance = 0;
  • myCar:Car means that myCar is of type Car
  • new Car() creates an instance from the Car definition.
  • myCar.distance = 0 sets the initial value of the distance to 0 for the newly created car

Adding a Method

So far our car doesn’t have any definitions for any actions. Let’s define a move method that all the cars can have:

1 class Car {
2   distance: number;
3   move():void {
4     this.distance += 1;
5   }
6 }
  • move():void means that move is a method that does not return any value, hence void.
  • The body of the method is defined in { }
  • this refers to the instance, therefore this.distance points to the distance property defined on the car instance.
  • Now you can call the move method on the car instance to increment the distance value by 1:
1 myCar.move();
2 console.log(myCar.distance) // -> 1

Using Access Modifiers

If you wanted to tell the compiler that the distance variable is private and can only be used by the object itself, you can use the private modifier before the name of the property:

1 class Car {
2   private distance: number;
3   constructor () {
4     ...
5   }
6   ...
7 }
  • There are 3 main access modifiers in TypeScript: private, public, and protected:
  • private modifier means that the property or the method is only defined inside the class only.
  • protected modifier means that the property or the method is only accessible inside the class and the classes derived from the class.
  • public is the default modifier which means the property or the method is the accessible everywhere and can be accessed by anyone.

Adding a constructor

A constructor is a special method that gets called when an instance is created from a class. A class may contain at most one constructor declaration. If a class contains no constructor declaration, an automatic constructor is provided.

Let’s add a constructor to the Car class that initializes the distance value to 0. This means that all the cars that are crated from this class, will have their distance set to 0 automatically:

1 class Car {
2   distance: number;
3   constructor () {
4     this.distance = 0;
5   }
6   move():void {
7     this.distance += 1;
8   }
9 }
  • constructor() is called automatically when a new car is created
  • Parameters are passed to the constructor in the ()
  • The body of the constructor is defined in the { }

Now, let’s customize the car’s constructor to accept distance as a parameter:

1 class Car {
2   private distance: number;
3   constructor (distance) {
4     this.distance = distance;
5   }
6 }
  • On line 3 we are passing distance as a parameter. This means that when a new instance is created, a value should be passed in to set the distance of the car.
  • On line 4 we are assigning the value of distance to the value that is passed in

This pattern is so common that TypeScript has a shorthand for it:

1 class Car {
2   constructor (private distance) {
3   }
4 }

Note that the only thing that we had to do was to add private distance in the constructor parameter and remove the this.distance and distance: number. TypeScript will automatically generate that. Below is the JavaScript outputed by TypeScript:

1 var Car = (function () {
2   function Car(distance) {
3     this.distance = distance;
4   }
5   return Car;
6 })();

Now that our car expects a distance we have to always supply a value for the distance when creating a car. You can define default values if you want so that the car is instantiated with a default value for the distance if none is given:

1 class Car {
2   constructor (private distance = 0) {
3   }
4   getDistance():number { return this.distance; }
5 }

Now if I forget to pass a value for the distance, it is going to be set to zero by default:

1 const mycar = new Car();
2 console.log(mycar.getDistance()); //-> 0

Note that if you pass a value, it will override the default value:

1 const mycar = new Car(5);
2 console.log(mycar.getDistance()); //-> 5

Setters and Getters (Accessors)

It is a very common pattern to have setters and getters for properties of a class. TypeScript provides a very simple syntax to achieve that. Let’s take our example above and add a setter and getter for the distance property. But before that we are going to rename distance to _distance to make it explicit that it is private. It is not required but it is a common pattern to prefix private properties with an underscore.

1 class Car {
2   constructor (private _distance = 0) {}
3   getDistance():number { return this._distance; }
4 }

In order to create the getter method, we are going to use the get keyword and the name for the property followed by ():

1 class Car {
2   constructor (private _distance = 0) {}
3   get distance() { return this._distance; }
4 }

Now we can get the value of distance:

1 const car2 = new Car(5);
2 console.log(car2.distance) //-> 5

Note on line 2 that we didn’t call a function. Behind the scenes, TypeScript creates a property for us, that’s why it is not a method. Below is the relevant generated JavaScript:

1 Object.defineProperty(Car.prototype, "distance", {
2   get: function () { return this._distance; },
3   enumerable: true,
4   configurable: true
5 });

JavaScript behind the scenes calls the get function for you to get the value, and that’s why we simply did car2.distance as opposed to car2.distance(). For more information about Object.defineProperty checkout the MDN docs.

Similar to the getter, we can define a setter as well:

1 class Car {
2   constructor (private _distance = 0) {}
3   get distance() { return this._distance; }
4   set distance(newDistance: number) { this._distance = newDistance; }
5 }

Now we can both get and set the distance value:

1 const coolCar = new Car();
2 console.log(coolCar.distance); // -> 0
3 
4 coolCar.distance = 55;
5 console.log(coolCar.distance); // -> 55

Note that if we take out the setter, we won’t be able to assign a new value to distance.

Static Methods and Properties

Static methods and properties belong to the class but not the instances. For example, the Array.isArray method is only accessible through the Array but not an instance of an array:

1 var x = [];
2 x.isArray // -> undefined
3 Array.isArray(x) //-> true
  • On line 2 we are trying to access the isArray method, but obviously it is not defined because isArray is a static method.
  • On line three we are calling the static isArray method from Array and we can check if x is an array.

If you look at the Array documentation you can see that methods and properties are either defined on the Array.prototype or Array:

  • Array.prototype.x: makes x available to all the instances of Array
  • Array.x: x is static and only available through Array.

Now that we have some context, let’s see how you can define static methods and properties in TypeScript. Consider the code below:

 1 class Car {
 2   static controls: {isAuto: boolean } = {
 3     isAuto: true
 4   };
 5   static isAuto():boolean {
 6     return Car.controls.isAuto;
 7   }
 8   constructor (private _distance = 0) {}
 9   get distance() { return this._distance; }
10 }
11 
12 console.log(Car.controls); // -> { isAuto: true }
13 console.log(Car.isAuto()); // -> true
  • On line 2 we are defining a static property called controls using the static modifier. Then we specify the form and then assign a value for it.
  • On line 5 we are defining a static method called isAuto using the the static modifier. This method simply returns the value of isAuto from the static control object. Not that we get access to the class using the name of the class as opposed to using this. i.e. return Car.controls.isAuto

Implementing an Interface

Classes can implement one or multiple interfaces. We can make the Car class implement two interfaces:

1 interface ICarProps {
2   distance: number;
3 }
4 interface ICarMethods {
5   move():void;
6 }

Making the Car class implement the interfaces:

1 class Car implements ICarProps, ICarMethods {
2   distance: number;
3   constructor () {
4     this.distance = 5;
5   };
6   move():void {
7     this.distance += 1;
8   };
9 }

The above example is silly, but it shows the point that a class can implement one or more interfaces. Now if the class does not provide implementations for any of the interfaces, the compiler will complain. For example, if we leave out the distance instance variable, the compiler will print out the following error:

error TS2420: Class ‘Car’ incorrectly implements interface ‘ICarProps’. Property ‘distance’ is missing in type ‘Car’.

Inheritance

In Object-oriented programming, a class can inherit from another class which helps to define shared attributes and methods among objects. Although this pattern is very useful, it should be used cautiously as it can lead to code that is hard to maintain. You can learn more about classical inheritance and prototypical inheritance by watching Eric Elliot’s talk at O’Reilly’s Fluent Conference. The project files for this section are in angular2-intro/project-files/typescript/classes/inheritance.

Let’s get started by creating a base class called Vehicle. This class is going to be the base class for other classes that we create later.

 1 // Vehicle.ts
 2 export class Vehicle {
 3   constructor( private _name: string = 'Vehicle',
 4                private _distance: number = 0 ) { }
 5   get distance(): number { return this._distance; }
 6   set distance(newDistance: number) { this._distance = newDistance; }
 7   get name(): string { return this._name;}
 8   set name(newName: string) { this._name = newName; }
 9   move() { this.distance += 1 }
10   toString() { return this._name; }
11 }

There is nothing special in this class. We are just creating a class that has two private properties (name, distance) and we are creating the setters and getters for them. Additionally, we are defining the toString method that JavaScript internally calls in “textual contexts”. The constructor is the most notable of all the other methods:

  • It sets the name property to “Vehicle” for all the instances
  • It also sets the distance property to 0.

This means that when a class extends the Vehicle class, it will have to call the constructor of Vehicle using the super keyword. Let’s do that now by creating two classes called Car and Truck that inherit from the Vehicle class:

cars.ts

 1 import {Vehicle} from './vehicle';
 2 export class Car extends Vehicle {
 3   constructor(name?: string) {
 4     super();
 5     this.name = name || 'Car';
 6   }
 7 }
 8 export class Truck extends Vehicle {
 9   constructor(name?: string) {
10     super();
11     this.name = name || 'Truck';
12   }
13 }
  • The Car class and the Truck class both look almost identical. They both inherit from the Vehicle using the extends keyword.
  • They both call the Vehicle’s constructor in their own constructor method before implementing their own: constructor(name?: string) { super(); }
  • They both take an optional name property to set the name of the vehicle. If not name is provided, it will be set to either ‘Car’ or ‘Truck’

Now let’s create the main file and run the file:

 1 import {Car, Truck} from './cars';
 2 
 3 /**
 4  * Creating a new car from `Car`
 5  */
 6 const car = new Car();
 7 console.log(car.name);
 8 car.distance = 5;
 9 car.move();
10 car.move();
11 console.log(car.distance);
12 /**
13  * Creating a new Truck.
14  */
15 const truck = new Truck();
16 console.log(truck.name);
  • On line 1 we are importing the Car and the Truck class.
  • and then we create a Car and Truck instance and log their names and distance to the console.

Run the build task (command + shift + b) and run the file (F5) and you should see the output:

1 node --debug-brk=7394 --nolazy output/main.js
2 Debugger listening on port 7394
3 Car
4 7
5 Truck

You can play around with the code above an try passing a string when instantiating a Car or a Truck to see the name change.

TODO

  • constructor overloading

Class Decorators

There are different types of decorators in TypeScript. In this section we are going to focus on Class Decorators.

TODO

add content

Modules

  • In TypeScript you can use modules to organize your code, avoid polluting the global space, and expose functionalities for others to use.
  • Multiple modules can be defined in the same file. However, it makes more sense to keep on module per file
  • If you want, you can split a single module across multiple files
  • If you decide to split a module across different files, this is how you would do it:
    • Create the module file: mymodule.ts and declare your module there: module MyModule {}
    • Create another file: mymodule.ext1.ts and on top of the file add: /// <reference path="mymodule.ts" />. Then in the file, you can use the same name of the module and add more stuff to it: module MyModule { // other stuff... }
    • Then in your main file, you need two things on top of the file:
      • /// <reference path="mymodule.ts" />
      • /// <reference path="mymodule.ext1.ts" />
    • Then, you can use the name of your module to refer to the symbols defined: MyModule.something, MyModule.somethingElse
  • TypeScript has two system: one used internally and the other used externally
  • External modules are used if your app uses CommonJS or AMD modules. Otherwise, you can use TypeScript’s internal module system
  • Using TypeScript’s internal module system, you can:
    • use the module keyword to define a module: module MyModule { ... }
    • split modules into different files that contribute to a single module
    • use the /// <reference path="File.ts" /> tag to tell the compiler how files are related to each other when modules are split across files
  • Using TypeScript’s external module system:
    • you cannot use the module keyword. The module keyword is used only by the internal module system.
    • instead of the reference tag, you can use the import keyword to define the relationship between modules
    • you can import symbols using the file name: import mymodule = require('mymodule')

The project files for this chapter are in angular2-intro/project-files/typescript/modules.

Simple Module

Let’s create a simple module that contains two classes. The first class is a vehicle class and the second is a car class that inherits from the vehicle class. Then we are going to expose the car class to the outside world and import it from another file. The project files for this section are in angular2-intro/project-files/typescript/modules/basic-module.

First, create the main.ts file and copy paste the following:

main.ts

1 module MyModule {
2   class Vehicle {
3     constructor (public name: string = 'Vehicle', private _distance: number = 0)\
4  {}
5     get distance():number { return this._distance; }
6     set distance(newDistance: number) { this._distance = newDistance; }
7     move() { this.distance += 1 }
8   }
9 }
  • On line 1 we are defining the module called MyModule.
  • Inside this module we have defined a class called Vehicle that has a distance property and a setter and getter.

Now we want to create a class and export it so that it can be imported by others:

main.ts

 1 module MyModule {
 2   class Vehicle {
 3     constructor (public name: string = 'Vehicle', private _distance: number = 0)\
 4  {}
 5     get distance():number { return this._distance; }
 6     set distance(newDistance: number) { this._distance = newDistance; }
 7     move() { this.distance += 1 }
 8   }
 9   // -> adding the car class
10   export class Car extends Vehicle {
11     constructor (public name: string = 'Car') {
12       super();
13     }
14   }
15 }
  • On line 9 we are using the export keyword to indicate that the Car class is exposed and can be used by others.

Now, let’s create a car using the Car class defined in the MyModule module:

1 const mycar = new MyModule.Car('My Car');
2 console.log(mycar.name);

Note that we accessed the Car class using the MyModule symbol: MyModule.Car. Now we can split up the module into its own file and import it into the main file. Let’s create a file called MyModule.ts and move the module definition to that file. Now in our main file we are just going to import the module and use the car class from it.

main.ts

1 /// <reference path="MyModule.ts" />
2 const mycar = new MyModule.Car('My Car');
3 console.log(mycar.name);

Note that we can create an alias to the MyModule using import AliasName = MyModule. Now you can reference the module name with AliasName:

1 /// <reference path="MyModule.ts" />
2 import AliasName = MyModule;
3 const mycar = new AliasName.Car('My Car');
4 console.log(mycar.name);

Now if we run this in debug mode, the compiler will complain that it can’t find the MyModule reference. Because of that we need to make some changes to our config files. First, we are going to add the out property in the tsconfig.json file. This will tell the compiler to compile all the files into a single file:

1 "out": "output/run.js",

So our tsconfig.json file will look like this:

 1 {
 2   "compilerOptions": {
 3     "experimentalDecorators": true,
 4     "emitDecoratorMetadata": true,
 5     "module": "commonjs",
 6     "target": "es5",
 7     "sourceMap": true,
 8     "outDir": "output",
 9     "out": "output/run.js",
10     "watch": true
11   }
12 }

Now if you run the build, you should see that all the project has been compiled into output/run.js. In addition to the tsconfig.json file, we are going to update the launch.json file and add a new configuration field:

1 {
2   "name": "TS All Debugger",
3   "type": "node",
4   "program": "output/run.js",
5   "stopOnEntry": false,
6   "sourceMaps": true
7 }

Now we should be able to use the debugger and put breakpoints in our TypeScript files. Select TS All Debugger from the debugger dropdown and run the debugger and it should stop if you put a breakpoint in any of your TypeScript files.

NOTE Using the configuration files above we can compile all the TypeScript files into a single JavaScript file. But sometimes that is not what you want. Be aware that using the above configuration you will not get an output for each TypeScript file.

Splitting Internal Modules

Internal modules in TypeScript are open ended. This means that you can define a module with the same name in different files and keep adding to it. This is also known as merging. In this section we are going to demonstrate merging multiple files that contribute to a single module called Merged. The project files for this section are in angular2-intro/project-files/typescript/modules/merged-module.

First, we are going to make two files: A.ts and B.ts. In each file we are going to define the Merged module:

1 // A.ts
2 module Merged {
3   const name = 'File A'; // not exported
4   export class Door {
5     constructor (private _color = 'white') {}
6     get color() { return this._color; }
7     set color(newColor) { this._color = newColor; }
8   }
9 }

and then the B.ts file:

1 // B.ts
2 module Merged {
3   const name = 'File B'; // not exported
4   export class Car {
5     constructor(public distance = 0) {}
6     move () {this.distance += 1;}
7   }
8 }

We just created two files called A.ts and B.ts and each file we defined the Merged module and added a class to each and exported it. Now we are going to make the main.ts file and reference these two files:

1 // main.ts
2 /// <reference path="./A.ts" />
3 /// <reference path="./B.ts" />

And now we can use the classes defined in the Merged module, that is the Car and the Door class:

1 /// <reference path="./A.ts" />
2 /// <reference path="./B.ts" />
3 const car: Merged.Car = new Merged.Car();
4 const door: Merged.Door = new Merged.Door();
5 door.color = 'blue';
6 car.move();
7 car.move();
8 console.log(car.distance);
9 console.log(door.color);

if you run the build task (command + shift + b) and hit F5 you should see the following output:

1 node --debug-brk=19237 --nolazy output/run.js
2 Debugger listening on port 19237
3 2
4 blue

External Modules

In addition to TypeScript’s internal module system, you can use external modules as well. In this section we are going to demonstrate how you can use external modules in TypeScript. The project files for this section are in angular2-intro/project-files/typescript/modules/external-module.

Let’s say I have a JavaScript Node module defined in CommonJS format in a file called common.js:

1 // common.js
2 module.exports = function () {
3   this.name = 'CommonJS Module';
4 };

In order to import this we need to do two things: first, we need to install Node’s Type Definitions. Then we need to require the module. To install Node’s Type Definitions run the following the terminal in the root of your project:

1 tsd install node --save

Now you should see a folder called typings containing the type definitions. Now that we have Node’s type definitions, let’s add a reference to it on top of main.ts:

1 // main.ts
2 /// <reference path="./typings/node/node.d.ts" />

and then we are going to require the module and log it to the console:

1 // main.ts
2 /// <reference path="./typings/node/node.d.ts" />
3 const common = require('./common');
4 console.log(common()); // --> CommonJS Module

After running the build task ( command + shift + b ), and running the file (F+5) you should see the following output:

1 node --debug-brk=32221 --nolazy run.js 
2 Debugger listening on port 32221
3 CommonJS Modules

Note the configuration files that we are using:

tsconfig.json

 1 {
 2   "compilerOptions": {
 3     "experimentalDecorators": true,
 4     "emitDecoratorMetadata": true,
 5     "module": "commonjs",
 6     "target": "es5",
 7     "sourceMap": true,
 8     "outDir": "output",
 9     "out": "run.js",
10     "watch": true
11   }
12 }

launch.json

 1 {
 2   "version": "0.1.0",
 3   "configurations": [
 4     {
 5       "name": "TS All Debugger",
 6       "type": "node",
 7       "program": "./run.js",
 8       "stopOnEntry": false,
 9       "sourceMaps": true
10     }
11   ]
12 }

Decorators

  • Decorators can be used to add additional properties and methods to existing objects.
  • Decorators are a declarative way to add metadata to code.
  • There are four decorators: ClassDecorator, PropertyDecorator, MethodDecorator, ParameterDecorator
  • TypeScript supports decorators and does not know about Angular’s specific annotations.
  • Angular provides annotations that are made with decorators behind the scenes

Method Decorators

Goals: - make a method decorator called log. - Decorate someMethod in a class using @log

1 class SomeClass {
2   @log
3   someMethod(n: number) {
4     return n * 2;
5   }
6 }

In the usage, someMethod has been decorated with log using the @ symbol. @log is decorating someMethod because it is placed right before the method.

  • Decorator Implementation:
 1 function log(target: Function, key: string, value: any) {
 2   return {
 3     value: function (...args: any[]) {
 4       var a = args.map(a => JSON.stringify(a)).join();
 5       var result = value.value.apply(this, args);
 6       var r = JSON.stringify(result);
 7       console.log(`Call: ${key}(${a}) => ${r}`);
 8       return result;
 9     }
10   };
11 }

A method decorators takes a 3 arguments:

  • target: the method being decorated.
  • key: the name of the method being decorated.
  • value: a property descriptor of the given property if it exists on the object, undefined otherwise. The property descriptor is obtained by invoking the Object.getOwnPropertyDescriptor function.

TODO

  • Add decorator content for each type.

Angular

This chapter will walk you through the main concepts in Angular. We will start by looking at components, and then we move onto pipes, services, events and other concepts. By the end of the chapter you should have a basic understanding of the most important concepts in Angular.

The goal of this chapter is to get your feet wet without scaring you with a lot of details. Don’t worry, there will be a lot coming in later chapters.

Project Files

Running the Project Files

First, make sure that you have cloned the code repo somewhere on your machine:

1 cd ~/Desktop && git clone git@github.com:aminmeyghani/angular2-intro.git

In order to run the project files, you need to do two things:

  • First, install the server dependencies and run the server in the root of the repo:
    1   cd angular2-intro && npm i && npm start
    

    After the dependencies are installed, it will open up the browser at port 8080.

  • The next step is to install the dependencies for angular examples. Go to project-files/angular-examples and install the dependencies:
    1   cd project-files/angular-examples && npm i
    

After following the steps above, you should be able to see the examples in the browser. For example, if you want to see the basic-component example, you can go to the following url:

1 http://localhost:8080/project-files/angular-examples/basic-component/index.html

Starter Project

There is a starter project in angular-examples/starter. You can make a copy of that folder if you want to work on something new. The steps for running the project is the same for all the projects:

  • Install the dependencies for the dev server in the root of the repo with npm i (needed once)
  • Start the dev server in the root of the repo with npm start
  • Install the dependencies for angular examples: cd project-files/angluar-examples && npm i (needed once)
  • Open your project in VSCode: code project-files/angular-examples/starter
    • Close all chrome instances (quit out of Chrome)
    • In VSCode start the build with command + shift + b and run the app by hitting F5 on the keyboard
  • If you don’t want to use VSCode, you can use any other editor that you want. But make sure that you run the TypeScript compiler in the project folder: cd project-files/angular-examples/starter && tsc -w.

Using the Docs

Angular API reference can be found at: https://angular.io/docs/ts/latest/api.

If you are looking for annotations or decorators, look for the keyword followed by metdata. For example, if you want to look up the Component decorator, you would look for: ComponentMetadata. Below are the common metadata class names:

  • ComponentMetadata
  • DirectiveMetadata
  • PipeMetadata
  • InjectMetadata
  • InjectableMetadata

TODO

Common Interfaces

  • OnInit

TODO

Common Enums

  • ChangeDetectionStrategy

TODO

Metadata Classes Cheatsheet

  • Angular uses Metadata to decorate classes, methods and properties.
  • The most notable Metadata is the @component Metadata.
  • Metadta classes are very convenient and they make it easy to work with components, services and the dependency injection system

Below is a list of Angular’s core Metadata classes categorized under directives/components, pipes and di.

Directive/component Meta-data

  • Component: used to define a component
    • View: used to define the template for a component
    • ViewChild: used to configure a view query
    • ViewChildren: used to configure a view query
  • Directive: used to define a directive
    • Attribute used to grab the value of an attribute on an element hosting a directive
    • ContentChild: used to configure a content query
    • ContentChildren: used to configure a content query
    • Input: used to define the input to a directive/component
    • Output: used to define the output events of a directive/component
    • HostBinding: used to declare a host property binding
    • HostListener: used to declare a host listener

Pipes

  • Pipe: used to declare reusable pipe function

DI

  • Inject: parameter metadata that specifies a dependency.
  • Injectable: a marker metadata that marks a class as available to Injector for creation.
  • Host: Specifies that an injector should retrieve a dependency from any injector until reaching the closest host.
  • Optional: parameter metadata that marks a dependency as optional
  • Self: Specifies that an Injector should retrieve a dependency only from itself.
  • SkipSelf: Specifies that the dependency resolution should start from the parent injector.
  • Query: Declares an injectable parameter to be a live list of directives or variable bindings from the content children of a directive.
  • ViewQuery: Similar to QueryMetadata, but querying the component view, instead of the content children.

TODO

Component Basics

  • Technically speaking components are directives that extend directives with views
  • A component encapsulates a specific piece of functionality and components work together to deliver app’s functionality
  • Generally speaking, every app has a root component that bootstraps the application. And when the app is bootstraped, Angular starts from the root component and resolves the sub trees of components

In this section we are going to write a simple HelloAngular component, compile it and run it in the browser. In addition, we will configure VSCode to build the TypeScript files as we go.

Note that there is a lot to talk about components. We are going dive into components a lot more in later chapters, but for now let’s just keep things simple.

The project files for this chapter are in angular2-intro/project-files/angular-examples/basic-component You can either follow along or just look at the final result

In order to run the project files, please refer to the Running the Project Files section.

Getting Started

Make a folder on your desktop called hello-angular and navigate to it:

1 mkdir ~/Desktop/hello-angular && cd $_

Start npm in this folder with npm init and accept all the defaults.

After that, add the dependencies and devDependencies field to your package.json file:

 1 "dependencies": {
 2   "angular2": "^2.0.0-beta.1",
 3   "es6-promise": "^3.0.2",
 4   "es6-shim": "^0.33.3",
 5   "reflect-metadata": "0.1.2",
 6   "rxjs": "5.0.0-beta.0",
 7   "zone.js": "0.5.10"
 8 },
 9 "devDependencies": {
10   "systemjs": "^0.19.16"
11 }

your package.json file should look something like the follwoing:

 1 {
 2   "name": "hello-angular",
 3   "version": "1.0.0",
 4   "description": "",
 5   "main": "index.js",
 6   "scripts": {
 7     "test": "echo \"Error: no test specified\" && exit 1"
 8   },
 9   "author": "Amin Meyghani <meyghania@gmail.com> (http://github.com/aminmeyghani\
10 )",
11   "license": "ISC",
12   "dependencies": {
13     "angular2": "^2.0.0-beta.1",
14     "es6-promise": "^3.0.2",
15     "es6-shim": "^0.33.3",
16     "reflect-metadata": "0.1.2",
17     "rxjs": "5.0.0-beta.0",
18     "zone.js": "0.5.10"
19   },
20   "devDependencies": {
21     "systemjs": "^0.19.16"
22   }
23 }

Then run npm i to install the dependencies.

After all the dependencies are installed, start VSCode in this folder with code .

Then create a index.html file in the root of the project and put in the following:

index.html

 1 <html>
 2 <head>
 3   <title>Hello Angular</title>
 4 
 5   <script src="/node_modules/angular2/bundles/angular2-polyfills.js"></script>
 6   <script src="/node_modules/systemjs/dist/system.src.js"></script>
 7   <script src="/node_modules/rxjs/bundles/Rx.js"></script>
 8   <script src="/node_modules/angular2/bundles/angular2.dev.js"></script>
 9 
10   <!-- add systemjs settings later -->
11 
12 </head>
13 
14 <body>
15   <!-- add app stuff later -->
16 </body>
17 
18 </html>

This loads all the necessary scripts that we need to run Angular in the browser.

Note

If you need to support older browsers, you need to include the es6-shims before everything else:

1 <script src="/node_modules/es6-shim/es6-shim.js"></script>

Making a Simple Component

Let’s start by making the main.ts file in the root of the project. In this file we are going to define the main component called HelloAngular and then bootstrap the app with it:

main.ts

 1 import {Component, OnInit} from 'angular2/core';
 2 import {bootstrap} from 'angular2/platform/browser';
 3 
 4 @Component({
 5   selector: 'app',
 6   styles: [`h1 { line-height: 100vh; text-align: center }`],
 7   template: `<h1>{{ name }}</h1>`
 8 })
 9 class HelloAngular implements OnInit {
10   name: string;
11   constructor() { this.name = 'Hello Angular'; }
12   ngOnInit() { console.log('component linked'); }
13 }
14 
15 bootstrap(HelloAngular, []);
  • On line 1 we are importing the component meta data (annotation) and the onInit interface.
  • On line 2 we are loading the bootstrap method that bootstraps the app given a component.
  • On line 4, we are defining a component using the component decorator. The @component is technically a class decorator because it precedes the HelloAngular class definition.
  • On line 5, we are telling angular to look out for the app tag. So when Angular looks at the html and comes across the <app></app> tag, it is going to load the template (on line 6) and instantiates the class for it (defined on line 9).
  • On line 9, we are defining a class called HelloAngular that defines the logic of the component. And for fun, we are implementing the OnInit interface to log something to the console when the component is ready with its data. We will learn more about the lifeCycle hooks later.
  • Last but not least, we call the bootstrap method with the HelloAngular class as the first argument to bootstrap the app with the HelloAngular component.

Compiling the Component

Now we need to compile the file to JavaScript. We can do it from the terminal, but let’s stick to VSCode. In order to that, we need to make two config files:

  1. First is the standard tsconfig.json file
  2. And the tasks.json file for VSCode to do the compiling

Create the tsconfig.json file in the root of the project and put in the following:

tsconfig.json

 1 {
 2   "compilerOptions": {
 3     "target": "es5",
 4     "module": "system",
 5     "moduleResolution": "node",
 6     "sourceMap": true,
 7     "emitDecoratorMetadata": true,
 8     "experimentalDecorators": true,
 9     "removeComments": false,
10     "noImplicitAny": false,
11     "outDir": "output",
12     "watch": true
13   },
14   "exclude": [
15     "node_modules"
16   ]
17 }

Then create the tasks.json in the .vscode folder in the root of the project and put in the following:

.vscode/tasks.json

1 {
2   "version": "0.1.0",
3   "command": "tsc",
4   "showOutput": "silent",
5   "isShellCommand": true,
6   "problemMatcher": "$tsc"
7 }
  • Now we can build the TypeScript files as we work. We just need to start the build task with command + shift + b or using the prompt. If you want to use the prompt do the following:
    • Use command + shift + p to open the prompt
    • Then, type > run build task and hit enter to start the build task.
  • After you run the build task, you should see an output file generated with main.js and the source maps in it.
  • The task is watching the files and compiling as you go. To stop the task, open the prompt and type:
    1   > terminate running task
    

Loading the Component

After compiling the component, we need to load it to the index.html file with Systemjs. Open the index.html file and replace <!-- add systemjs settings later --> with the following:

 1 <script>
 2   System.config({
 3     packages: {
 4       output: {
 5         format: 'register',
 6         defaultExtension: 'js'
 7       }
 8     }
 9   });
10   System.import('output/main')
11   .then(null, console.error.bind(console));
12 </script>

Now we can use our component in the body of the html:

1 <body>
2   <app>Loading ...</app>
3 </body>

It is finally time to serve the app. You can serve the app in the current directory using the live-server:

1 live-server .

If everything is wired up correctly, you should be able to see the following:

Running a basic component in the browser
Running a basic component in the browser

Debugging the component

You can connect chrome’s debugger to VSCode using the chrome debugger extension for Visual Studio Code. See the Debugging App from VSCode section in case you missed to install it. But, assuming that you have the extension installed, you can debug your app from VSCode. In order to do that, we need to create a launch.json file in the .vscode folder:

1 touch .vscode/launch.json

After you created the file, put in the following configuration in the file:

 1 {
 2   "version": "0.1.0",
 3   "configurations": [
 4     {
 5       "name": "Launch Chrome Debugger",
 6       "type": "chrome",
 7       "request": "launch",
 8       "url": "http://127.0.0.1:8080/",
 9       "sourceMaps": true,
10       "webRoot": "${workspaceRoot}",
11       "runtimeExecutable": "/Applications/Google Chrome.app/Contents/MacOS/Googl\
12 e Chrome",
13       "runtimeArgs": [
14         "--remote-debugging-port=9222",
15         "--incognito"
16       ]
17     }
18   ]
19 }

Before running the debugger:

  • Make sure that all instances of chrome are closed. It makes it easier to run the debugger from VSCode itself.
  • Make sure that the runtimeExecutable path is valid. This value would be different depending on your OS.
  • Make sure that the url value is valid as well. The url value has to match the path that you see when you run a server serving the files.
  • Set a breakpoint on a line in main.ts file and then run the debugger under the debugger tab.

In order to run the debugger, select Launch Chrome Debugger in the dropdown under the debugger tab and either click on the play icon or hit F5 on the keyboard. After that, an instance of Chrome should be opened in incognito mode. In order to trigger the debugger just refresh the page and you should be able to see the debugger pausing in VSCode. If everything is set up correctly you should be able to see something like the following screenshot:

Debugging the app with Chrome Debugger in VSCode
Debugging the app with Chrome Debugger in VSCode

Component Inputs

  • You can pass data to a component.
  • You can either use the inputs array on a component or annotate an instance variable with the Input decorator
  • Once you specify the inputs to your component, they become available in the ngOnInit method
  • You can implement the ngOnInit and access the input instance variables
  • You can use the [propname]="data" to set the propname to whatever data evaluates to
  • Note that if you set [propname]="'data'", propname will be set to the literal data string

Project files

The project files for this section are in angular2-intro/project-files/angular-examples/component-input.

Getting Started

In order to demonstrate component inputs, we are going to create a user component and pass name, lastName, and userId to it. So our final html tag would look something like the following:

1 <user name="Tom" lastName="Johnson" uesrId="1"></user>

And the template for the component will be:

1 <h1>Hello, {{ name }} {{ lastName }}, id: {{ userId }}</h1>

which would output: Hello, Tom Johnson id: 1.

To get started, let’s define the User component:

1 @Component({
2   selector: 'user',
3   template: '<h1>Hello, {{ name }} {{ lastName }} id: {{ userId }}</h1>',
4   inputs: ['name', 'lastName', 'userId'] // <- specifying the inputs to the `Use\
5 r` component
6 })
7 class User {}
  • On line 4 we are defining the inputs as an array of strings

Then, we are going to use the User component inside our app’s template:

1 @Component({
2   selector: 'app',
3   template: `<user name="Tom" lastName="Johnson" uesrId="1"></user>`
4 })
5 class Root {}

because we are using the User component in the app, we need to register it with the app by adding User class to the list of directives of the app component:

1 @Component({
2   selector: 'app',
3   template: `<user name="Tom" lastName="Johnson" userId="1"></user>`,
4   directives: [User] // <- register the component
5 })
6 class Root {}

and at the end we need to bootstrap the app:

1 bootstrap(Root, [])

Now, notice that instead of adding the inputs to the inputs array, we could have decorated the instance variables with the @Input decorator:

 1 import {Input} from 'angular2/core'; // <- importing the Input decorator
 2 @Component({
 3   selector: 'user',
 4   template: '<h1>Hello, {{ name }} {{ lastName }} id: {{ userId }}</h1>'
 5   // <- removing the inputs array.
 6 })
 7 class User {
 8   @Input() private name: string;
 9   @Input() private lastName: string;
10   @Input() private userId: number;
11 }

Binding Data to Properties

Now, let’s see how we can bind to a property from another component. For this example, we are going to continue with our User component and create a new component called Permission. Then we are going to use the the Permission component inside the User component and set the uid of Permission by the userId of the User.

The Permission component is defined as follows:

 1 @Component({
 2   selector: 'permission',
 3   template: '<h2> Restriction is: {{ restriction }}'
 4 })
 5 class Permission {
 6   @Input() private uid: string;
 7   private restriction: string;
 8   constructor() {
 9     this.restriction = 'none';
10   }
11   ngOnInit() {
12     this.restriction = this.uid === '1' ? 'admin' : 'normal';
13   }
14 }
  • On line 6 we are defining uid to be an input instance variable. It’s value is set from outside.
  • In the constructor we are setting a default value for the restriction.
  • Then in the ngOnInit hook, we are evaluating the value of restriction based on the given id provided by other components, in this case the User component
  • In this silly example, if the passed id is 1, we will set the restriction to admin, otherwise we set it to normal.

then we are going to register the Permission component with the User component so that we can use it in the User template:

1 @Component({
2   selector: 'user',
3   ///...
4   directives: [Permission] // <-
5 })
6 class User {}

then we can update the User template to include the Permission:

 1 @Component({
 2   selector: 'user',
 3   template: `
 4   <h1>Hello, {{ name }} {{ lastName }}, id: {{ userId}}</h1>
 5   <div>
 6     <permission [uid]="userId"></permission>
 7   </div>
 8   `,
 9   inputs: ['name', 'lastName', 'userId'],
10   directives: [Permission]
11 })
12 class User {}
  • Note that on line 6 we are setting the uid of Permission by userId available from the User component.

If you run the app you should see the following printed to the page:

Input to components
Input to components

Binding to DOM Properties

In addition to custom properties, you can bind to DOM properties. Below are some examples:

Binding to style

We can bind to the style property of DOM nodes. In the example below, if the value of isDone is true, we set the style.textDecoration to line-through, otherwise we won’t set it to anything

1 <div [style.textDecoration]="isDone ? 'line-through' : ''"> Todo Item </div>

Binding to class

In addition to the style property, we can also bind to the class property. In the example below, we are setting the class name to “collapsed” if the isCollapsed value is true and expanded if the the value is false:

1 <div [className]="isCollapsed ? 'collapsed' : 'expanded'">Element</div>

Binding to ‘hidden’

You can bind to the hidden property of a DOM node and show or hide the element. In the example below, we are hiding the element if the isVisible value is true:

1 <div [hidden]="isVisible">To Hide element</div>

Binding to textContent

We can bind to the textContent property and set the text content of a node. In the example below, we are setting the text content by reading the value of an input:

1 <div [textContent]="textValue"></div>
2 <input type="text" #contentInput>
3 <button (click)="setTextContent()">Set Text Content</button>

Component Output/Events

  • Events can be emitted from components. These events can be either custom or they could be DOM events
  • The syntax is (eventname)="fn()" where eventname is the name of the event and fn is the handler function
  • The handler function is called when the event is fired
  • For example, if you want to handle a click event you can do: (click)="handler()". In this case the hander is called whenever the click event is fired off
  • You can use Angular’s EventEmitter to fire off custom events
Custom Output Events

Project Files

The project files for this section are in angular2-intro/project-files/angular-examples/component-output-events.

Final Result

The goal of this section is to show you how to create a component that contains a button that when is clicked, calls a handler defined by the component’s class. The final html will look like the following:

1 <p>Value: {{ value }}</p>
2 <button (click)=addOne()>Add +</button>

That idea is very simple: every time we click on the button we want to increment the value by one. In addition to that, we want to be able to hook into a custom event and run the addOne method whenever the event is fired:

1 <p>Value: {{ value }}</p>
2 <span adder-auto (myevent)=addOne()>adding ...</span>

Getting Started

Let’s get started by defining our Adder component:

 1 @Component({
 2   selector: 'adder',
 3   template:`
 4   <p>Value: {{ value }}</p>
 5   <button (click)=addOne()>Add +</button>
 6   `
 7 })
 8 class Adder {
 9   private value: number;
10   constructor() {
11     this.value = 0;
12   }
13   addOne() {
14   this.value += 1;
15   console.log(this.value);
16   }
17 }

Now, we are just going to register Adder with our root component:

1 @Component({
2   selector: 'app',
3   directives: [Adder],
4   template: '<adder></adder>'
5 })
6 class App {}

after you bootstrap the app and run it you should be able to see a button that when clicked increments the value by one.

Using EventEmitter

Now, let’s see how we can use the EventEmitter to increment the value by one every time a custom event is fired every second. In order to achieve that, we are going to create an attribute directive called AdderAuto. Start by importing the Directive metadata class:

1 import {Directive} from 'angular2/core';

and then define the selector for the directive:

1 @Directive({
2   selector: '[adder-auto]'
3 })
  • selector: '[adder-auto]' means that angular will target any element that has the adder-auto attribute and will create an instance of the class. Now we need to define the class for our directive:
1 class AdderAuto {
2   // custom event definition
3 }

In this class we need to define a custom event output hook. We are going to call it myevent. The same way that you can hook into (click), we want to be able to use (myevent). To achieve that, we need to create an instance variable and decorate it with the Output decorator:

1 // -> importing `EventEmitter` and `Output` decorator.
2 import {EventEmitter, Output} from 'angular2/core';
3 class AdderAuto {
4   @Output() myevent: EventEmitter<string>;
5   constructor() {
6     this.myevent = new EventEmitter();
7   }
8 }
  • If you notice, myevent is of type EventEmitter that emit events of type string
  • In the constructor we are creating an instance of EventEmitter. So now we can use myevent to emit events
  • We can use setInterval to emit event from our custom event every second
1 class AdderAuto {
2   @Output() myevent: EventEmitter<string>;
3   constructor() {
4     this.myevent = new EventEmitter();
5     setInterval(()=> {this.myevent.emit('myevename')}, 1000);
6   }
7 }

Now we can register AdderAuto with the Adder component and run the addOne method every second:

1 @Component({
2   selector: 'adder',
3   ...
4   directives: [AdderAuto] // <- register `AdderAuto`
5 })

and then we can update the template:

1 <p>Value: {{ value }}</p>
2 <button (click)="addOne()">Add +</button>
3 <!-- using the event. -->
4 <h2>Using Emitter</h2>
5 <span adder-auto (myevent)="addOne($event)"> EVENT: </span>
  • first we are adding the attribute directive adder-auto on the span
  • second, we are using the myevent hook and attaching addOne handler to it. This means that whenever the myevent event is triggered, run the addOne handler.

The Adder component now looks like the following with the updated template:

 1 @Component({
 2   selector: 'adder',
 3   template:`
 4   <p>Value: {{ value }}</p>
 5   <button (click)="addOne()">Add +</button>
 6   <h2>Using Emitter</h2>
 7   <span adder-auto (myevent)="addOne($event)"> EVENT: </span>
 8   `,
 9   directives: [AdderAuto]
10 })

Now if you run the code, you should be able to see the number incrementing by one every second.

Binding to DOM Events

In addition to custom output events, we can bind to almost all of the native DOM events. Below you can find some common events with examples:

Inputs

  • keyup
    1   <input type="text" #inputField2 (keyup)="setInputFieldValue2(inputField2.value\
    2 )">
    3   <pre> {{ keyupValue }}</pre>
    
  • input
    1   <input type="text" #inputField (input)="setInputFieldValue(inputField.value)">
    2   <pre> {{ inputValue }}</pre>
    

Mouse

  • click
    1   <button (click)="sayHello()"> Say Hello! </button>
    2   <pre>{{ hello }}</pre>
    
  • dblclick
    1   <button (dblclick)="sayDoubleHello()"> Say Hello! </button>
    2   <pre>{{ doubleHello }}</pre>
    
  • mousedown
    1   <button (mousedown)="sayDownHello()">Say Hello !</button>
    2   <pre>{{ downHello }}</pre>
    
  • mouseup
    1   <button (mouseup)="sayUpHello()">Say Hello !</button>
    2   <pre>{{ upHello }}</pre>
    
  • mouseenter
    1   <h2 (mouseenter)="sayEnterHello()" (mouseleave)="clearEnterHello()">Mouseenter\
    2 /leave</h2>
    3   <pre>{{ enterHello }}</pre>
    
  • mouseleave
    1   <h2 (mouseenter)="sayEnterHello()" (mouseleave)="clearEnterHello()">Mouseenter\
    2 /leave</h2>
    3   <pre>{{ enterHello }}</pre>
    
Event Delegation/Bubbling

When an event is raised by an element, by default it bubbles up the html hierarchy. For example, if you have a table and attached a click handler to the table itself, you can catch the row that was clicked by only attaching a single handler. This method is useful when you are dealing with situations where you don’t want to attach event handlers to every elements, and you just want to attach one. Below is an example of event delegation, detecting the row that was clicked on the table:

 1 <pre>{{rowClicked}}</pre>
 2 
 3 <table (click)="catchBubbledEvent($event)">
 4   <tr>
 5     <td>1</td>
 6   </tr>
 7   <tr>
 8     <td>2</td>
 9   </tr>
10   <tr>
11     <td>3</td>
12   </tr>
13   <tr>
14     <td>4</td>
15   </tr>
16 </table>

Notice how we have a single click handler only on the table itself. The $event is the bubbled event that we can catch to do interesting stuff with. In this case we are just reading the textContent from the target: event.target.textContent:

1 catchBubbledEvent(event) {
2   this.rowClicked = event.target.nodeName === 'TD' ? event.target.textContent : \
3 '';
4 }

In the method above we are checking if a td was clicked on. If so, we set the this.rowClicked to the td’s value, otherwise we set it to an empty string.

ViewChildren

  • The children elements located inside of its template of a component are called
  • Metadata classes: @ViewChildren, @ViewChild

TODO

ContentChildren

  • Elements used between the opening and closing tags of the host element of a given component
  • Metadata classes: @ContentChildren, @ContentChild

TODO

ViewProviders

TODO

Providers

TODO

Directives

  • Directives and components hand-in-hand are the fundamental building blocks of any Angular app
  • Directives are components without templates. Conversely, components are directives without templates
  • Directives allow you to attach behavior to elements in the DOM
  • A directive is defined using the @directive decorator
  • There are three types of directives in Angular:
    • Structural
    • Attribute
    • Components
  • Evey directive metadata, has the following options:
    • selector
    • host
  • The selector attribute uses a css selector to find the element. However, parent-child relationship selectors are not supported
  • You can use the following possible selectors:
    • element
    • [attribute]
    • .classname
    • :not()
    • .some-class:not(div)
  • The host option defines:
    • Property bindings
    • Event handlers
    • attributes

TODO(other decorator options)

Web Components Basics

Web Components are made up four specifications:

  • Custom Elements: enabling custom html tags
  • Shadow DOM: enabling isolation for custom elements
  • HTML Templates: enabling to define html template fragments
  • HTML Imports: enabling html fragment imports

Custom Elements

TODO

Shadow DOM

  • Enables a node to express three subtrees:
    • Light DOM: visible DOM notes inside the custom element tag/DOM supplied by the consumer
    • Shadow DOM: private to the element and hidden from others and attached to the element’s shadow root
    • Composed DOM: Rendered by the browser by distributing the light DOM into the Shadow DOM
    • Logical DOM = Light DOM + Shadow DOM. The developer interacts with this layer

TODO

HTML Templates

TODO

HTML Imports

TODO

host

TODO

selector

TODO

Attribute Directives

  • The Attribute directive changes the appearance or behavior of an element.
  • Angular has several built-in attribute directives, namely NgClass and NgStyle
  • It is a good idea to prefix your directives with a prefix. You cannot use the ng prefix since it’s already used by Angular.
  • When you apply the attribute directive to an element, the element will be knownn as the host.
  • For example, if you had a directive called my-directive and applied it in <div class="hello"> <span my-directive> ... </span> </div>, the span would be the host.

TODO (writing a custom attribute directive)

 1 @Directive({
 2   selector: '[simple-directive]',
 3   host: {
 4     '(mouseleave)': 'onMouseLeave()',
 5     '(click)': 'onClick()',
 6     '[hidden]': 'isHidden',
 7     '[class.done]': 'isDone',
 8     'role': 'button'
 9   }
10 })
11 class SimpleDirective implements OnInit {
12   @Input() private color: string
13   @Output() myevent: EventEmitter<string>;
14   private isHidden: boolean = false;
15   private isDone: boolean = false;
16   private defaultColor:string = 'magenta';
17   private elm: any;
18 
19   constructor(private elmRef: ElementRef, private renderer: Renderer) {
20     this.elm = elmRef.nativeElement;
21     this.myevent = new EventEmitter();
22     setInterval(() => {this.myevent.emit('myevename')}, 1000);
23   }
24   ngOnInit() {
25     this.defaultColor = this.color || this.defaultColor;
26     this.setColor(this.color || this.defaultColor);
27   }
28   private setColor(color: string) {
29     this.renderer.setElementStyle(this.elm, 'color', color);
30   }
31   set setIsHidden(state) { this.isHidden = state; }
32   set setIsDone(state) { this.isDone = state; }
33 
34   onMouseLeave() { this.setColor(this.defaultColor); }
35   onClick() { this.setColor('orange') }
36 }

selector TODO: details

host TODO: details

Input TODO: details

Output TODO: details

ElementRef TODO: details**

Renderer TODO: details**

Structural Directives

  • The Structural directive changes the DOM layout by adding and removing DOM elements
  • Angular has several built-in structural directives, namely NgIf, NgSwitch, and NgFor
  • When working with structural directives, we should ask ourselves to think carefully about the consequences of adding and removing elements and of creating and destroying components
  • Angular uses the html5 <template> tag to add or remove DOM elements
  • By default, Angular replaces <template> with <script> tag if no behavior is attached
  • The * before a directive name is a shorthand for including the directive content in the <template> tag
  • Below you can see the built-in NgIf directive with and without the asterisks *:

With *

1 <p *ngIf="condition"></p>

Without *

1 <template [ngIf]="condition">
2   <p></p>
3 </template>

Notice how the <p> tag is wrapped with a <template> and the condition is bound to the [ngIf] property of the directive

TODO (writing a custom structural directive)

 1 @Directive({
 2   selector: '[myUnless]'
 3 })
 4 class UnlessDirective {
 5 
 6   constructor(
 7     private tRef: TemplateRef,
 8     private vContainer: ViewContainerRef
 9   ) { }
10 
11   @Input() set myUnless(condition: boolean) {
12     if (!condition) {
13       this.vContainer.createEmbeddedView(this.tRef);
14     } else {
15       this.vContainer.clear();
16     }
17   }
18 }

TemplateRef: TODO: details

ViewContainerRef: TODO: details

@Input() set myUnless(condition: boolean) {}: TODO: details

Built-in Directives

Angular has a couple of useful built-in directives.

TODO(Note on directive names, docs and template usage)

NgClass
  • import {NgClass} from 'angular2/common';, directives: [NgClass]
  • Template Usage: <div class="button" [ngClass]="{active: isActive, disabled: !isActive}"

Note that we are using ngClass in the template, but not NgClass

NgIf

Usage

1 <div *ngIf="isDone">{{ list }}</div>

or in long-hand form:

1 <template>
2   <div [ngIf]="isDone">{{ list }}</div>
3 </template>

Details

From the docs: “The ngIf directive does not hide the element. Using browser developer tools we can see that, when the condition is true, the top paragraph is in the DOM and the bottom disused paragraph is completely absent from the DOM! In its place are empty <script> tags. We could hide the unwanted paragraph by setting its css display style to none. The element would remain in the DOM while invisible. Instead we removed it with ngIf.

The difference matters. When we hide an element, the component’s behavior continues. It remains attached to its DOM element. It continues to listen to events. Angular keeps checking for changes that could affect data bindings. Whatever the component was doing it keeps doing.

Although invisible, the component — and all of its descendant components — tie up resources that might be more useful elsewhere. The performance and memory burden can be substantial and the user may not benefit at all.

On the positive side, showing the element again is very quick. The component’s previous state is preserved and ready to display. The component doesn’t re-initialize — an operation that could be expensive.

ngIf is different. Setting ngIf to false does affect the component’s resource consumption. Angular removes the element from DOM, stops change detection for the associated component, detaches it from DOM events (the attachments that it made) and destroys the component. The component can be garbage-collected (we hope) and free up memory.

Components often have child components which themselves have children. All of them are destroyed when ngIf destroys the common ancestor. This cleanup effort is usually a good thing.

Of course it isn’t always a good thing. It might be a bad thing if we need that particular component again soon.

The component’s state might be expensive to re-construct. When ngIf becomes true again, Angular recreates the component and its subtree. Angular runs every component’s initialization logic again. That could be expensive … as when a component re-fetches data that had been in memory just moments ago.”

NgSwitch

Usage

1 <div [ngSwitch]="status">
2   <template [ngSwitchWhen]="'inProgress'">In Progress</template>
3   <template [ngSwitchWhen]="'isDone'">Finished</template>
4   <template ngSwitchDefault>Unknown</template>
5 </div>

TODO

NgFor

Usage

1 <ul>
2    <li *ngFor="#item of items">{{ item }}</li>
3 </ul>

or in long-hand form:

1 <ul>
2   <template ngFor #item [ngForOf]="items">
3     <li>{{ item }}</li>
4   </template>
5 </ul>

TODO(Details)

Accessing Directives from Parents

TODO (access directives on parent elements)

Accessing Directives from Children

TODO (access directives on children and descendants)

Change Detection

  • In Angular2 you can limit the change detection scope to components
  • Using chageDection property we can choose a change detection strategy for a component
  • The changeDetection field accept one of the following values:
    • ChangeDetectionStrategy.Default: sets detector mode to CheckAlways
    • ChangeDetectionStrategy.OnPush: sets detector mode to CheckOnce. This will limit change detection to the bindings affecting the component only
    • ChangeDetectionStrategy.Detached: change detector sub tree is not a part of the main tree and should be skipped
    • ChangeDetectionStrategy.CheckAlways: after calling detectChanges the mode of the change detector will remain CheckAlways
    • ChangeDetectionStrategy.Checked: change detector should be skipped until its mode changes to CheckOnce
    • ChangeDetectionStrategy.CheckOnce: after calling detectChanges the mode of the change detector will become Checked
  • Having the ability to specify change detection strategy can reduce the number of checks and improve app’s performance

Pipes

  • Pipes allow you to transform values in templates before they are outputed to the view.
  • Pipes were formerly known as filters in Angular 1.x
  • A pipe is defined using the @pipe class decorator
  • The pipe decorator takes name as a parameter defining the name of the pipe: @pipe({ name: 'myPipe' })
  • Every pipe class has a transform method that transforms input to outputs:
    • The first parameter is the input to the pipe
    • The second parameter is the list of arguments passed to the pipe
  • Give the following pipe in a template: {{ data | somePipe:1:'px'}}:
    • data is the input to pipe – the first parameter of the transform method
    • [1, 'px'] is the arguments to the pipe – the second parameter of the transform method
  • A pipe can be as simple as:
    1   @pipe({name: 'simplePipe'})
    2   class MyPipe {
    3     transform(input, args) { return input + 'px'; }
    4   }
    
  • If you want to use a pipe, you need to register your pipe class with the components in the pipes array:
    1   @component({
    2     selector: '...',
    3     pipes: [MyPipe] // adding pipe to the array of pipes.
    4   })
    5   class MyComponent {}
    
  • Pipes can be chained: input | pipe1 | pipe2 | pipe3
    • input | pipe1 : output1
    • output1 | pipe2: output2
    • output2 | pipe3 : finalOutput

Basic Pipe

Let’s make a basic pipe called pixel that takes a value as the input and appends ‘px’ to the end of it. The project files for this section are in angular2-intro/project-files/angular-examples/pipes/basic-pipe.

Start by making a copy of the “starter” folder and call it “basic-pipe” and put it in project-files/angular-examples. Then, open the folder in VSCode: code project-files/angular-examples/basic-pipe and start the build with command + shift + b.

Then, create a file for the pipe and call it pixel.pipe.ts in the root of the project.

After that we need to do couple of things to define the pipe:

  • Import the Pipe Class Metadata from angular core: import {Pipe} from 'Angular/core'
  • Then create a class defining the Pipe:
    1   class PixelPipe {
    2 
    3   }
    
  • Implement the transform method in the class:
    1   class PixelPipe {
    2     transform(input) {
    3       return input + 'px';
    4     }
    5   }
    
  • After implementing the method, we need to decorate the class and give the pipe a name that we want to use in our templates:
    1   @Pipe({name: 'pixel'}) // <- adding the decorator
    2   class PixelPipe {
    3     transform(input) {
    4       return input + 'px';
    5     }
    6   }
    
  • As the last step we are going to export the class by putting the export keyword behind the class:
    1   ...
    2   export class PixelPipe {
    3     ...
    4   }
    

Now, your file should look like the following:

1 import {Pipe} from 'angular2/core';
2 @Pipe({name: 'pixel'}) // <- adding the decorator
3 export class PixelPipe {
4   transform(input) {
5     return input + 'px';
6   }
7 }

Now, let’s go back to the main.ts file and import our pipe:

1 import {Component} from 'angular2/core';
2 import {bootstrap} from 'angular2/platform/browser';
3 import {PixelPipe} from './pixel.pipe'; // <- importing pipe

After importing our pipe, we should register it with our component by adding it to the pipes array:

1 @Component({
2   selector: 'app',
3   templateUrl : 'templates/app.tpl.html',
4   pipes: [PixelPipe] // <- registering the pipe
5 })

Now that we have registered the pipe, we can use it in our template in templates/app.tpl.html:

1 <h1>{{ name }}</h1>
2 <p>Pixel value: {{ 25 | pixel }}</p>

You should be all set now. You can set the url in your launch.json file and hit F5:

1 ...
2 "url": "http://localhost:8080/project-files/angular-examples/basic-pipe/index.ht\
3 ml",
4 ...

If your server is running you should be able to see the following output:

Running the pixelPipe in the browser
Running the pixelPipe in the browser

Chaining Pipes

Let’s continue where we left off with the “pixelPipe” and add another pipe called “round” that rounds down given values, that is:

1 25.3 | round | pixel -> 25px

The project files for this section are in angular2-intro/project-files/angular-examples/pipes/pipe-chaining.

We are going to add the “roundPipe” to our “basic-pipe” project. Let’s get started by adding the round.pipe.ts file in the root of the project:

1 import {Pipe} from 'angular2/core';
2 @Pipe({name: 'round'})
3 export class RoundPipe {
4   transform (input) {
5     return Math.floor(+input); // <- convert input to number and then floor it.
6   }
7 }

This Pipe is not complicated at all. We are just returning the floor of the input. We are also converting the input to number by putting a + before input.

Now, let’s import the pipe into our main.ts file:

1 import {Component} from 'angular2/core';
2 import {bootstrap} from 'angular2/platform/browser';
3 import {PixelPipe} from './pixel.pipe';
4 import {RoundPipe} from './round.pipe'; // <- importing `RoundPipe`

and then we have to add the pipe to the list of pipe array:

1 @Component({
2   selector: 'app',
3   templateUrl : 'templates/app.tpl.html',
4   pipes: [PixelPipe, RoundPipe] // <- registering the pipe
5 })

after that we are going to add the following to our templates/app.tpl.html file:

1 <p>Pixel value: {{ 34.4 | round | pixel }}</p>

After running the app you should see 34.px as the output on the page.

Pipes with Parameters

In this section we are going to extend our ‘pixel’ pipe to accept an optional parameter to set the unit. As a result, we are going to rename the ‘pixel’ pipe to ‘unit’ to make it more generic. This pipe will take the unit as an optional argument. If no argument is passed, it will default to ‘px’. That is:

1 25 | unit -> 25px
2 25 | unit:'em' -> 25em
3 34.5 | round | unit:'%' -> 34%

You can look at the project files in angular2-intro/project-files/angular-examples/pipes/pipe-unit.. AFter refactoring the name of the Pipe, we just need to change the implementation of the “UnitPipe”:

unit.pipe.ts

1 import {Pipe} from 'angular2/core';
2 @Pipe({name: 'unit'})
3 export class UnitPipe {
4   transform(input, args:string) {
5     const unit = args[0] || 'px';
6     return input + unit;
7   }
8 }
  • On line 5, we are grabbing the first parameter that is passed in and setting it to the unit variable. And if the value is not set, we are setting ‘px’ as the default value.
  • And finally we are returning input + unit.

That’s basically all we have to do. Note that you can pass multiple parameters separated by : and they all become available in the args array. So if you wanted to expand this pipe, this is how your template would look like:

1 {{ 25 | unit:'em':2}}

And the args array would be: ['em', 2].

Async Pipes

Async Pipes can be used for values that will be resolved after some asynchronous operation like getting a value after making a http call.

TODO

Built-in Pipes

In this section we are going to look at the pipes that Angular provides out of the box.

  • AsyncPipe: used to work with asynchronous values
  • CurrencyPipe: used to format a number as a local currency
  • DatePipe: used to format a date object to a readable string
  • DecimalPipe: used to format numbers
  • JsonPipe: calls JSON.stringify on the input and useful for debugging
  • LowerCasePipe: used to convert a string to lowercase letters
  • PercentPipe: used to format a number as percentage
  • SlicePipe: used to create a subset of list or string
  • UpperCasePipe: used to transform a text to upper case
Date
1 {{ input | date:optionalFormat}}
  • input: a date object or a number (milliseconds since UTC epoch)
  • optionalFormat: a string used to format the output. It specifies which components of date and time to include in the output

Using predefined formats

Usage Example: {{ input | date:'short'}}

+————–+—————————+ | Name | Example | +==============+===========================+ | short | 9/3/2010, 12:05 PM | +————–+—————————+ | shortDate | 9/3/2010 | +————–+—————————+ | medium | Sep 3, 2010, 12:05:08 PM | +————–+—————————+ | mediumDate | Sep 3, 2010 | +————–+—————————+ | longDate | September 3, 2010 | +————–+—————————+ | fullDate | Friday, September 3, 2010 | +————–+—————————+ | shortTime | 12:05 PM | +————–+—————————+ | mediumTime | 12:05:08 PM | +————–+—————————+

Using Custom Formats

  • Generally speaking every date object has a year, month, day, hour, minute, and second.
  • Using a custom string, you can specify which component you would like to include in the output.

+——+—————–+—–+————-+————+——–+——–+ | Year | Month | Day | Weekday | Hour | Minute | Second | +======+=================+=====+=============+============+========+========+ | y | M, MMM, MMMM | d | EEE, EEEE | j, h, H | m | s | +——+—————–+—–+————-+————+——–+——–+ | 2016 | 1, Jan, January | 1 | Sun, Sunday | 1, 1 AM, 1 | 1 | 1 | +——+—————–+—–+————-+————+——–+——–+

Note that every single letter identifier can be used twice to denote a double digit numeric value. For example, yy will result in 15 for the year value. Below is a table just to be thorough:

Double Digit

+——+——-+—–+—————-+——–+——–+ | Year | Month | Day | Hour | Minute | Second | +======+=======+=====+================+========+========+ | yy | MM | dd | jj, hh, HH | mm | ss | +——+——-+—–+—————-+——–+——–+ | 16 | 01 | 01 | 01, 01 AM, 01 | 01 | 01 | +——+——-+—–+—————-+——–+——–+

Details for Month/Weekday/Hour are summarized in the tables below:

Month Details

+——-+———–+———–+ | M | MMM | MMMM | +=======+===========+===========+ | Digit | Abbr Name | Full Name | +——-+———–+———–+ | 1 | Jan | January | +——-+———–+———–+

Weekday Details

+———–+———–+ | EEE | EEEE | +===========+===========+ | Abbr Name | Full Name | +———–+———–+ | Sun | Sunday | +———–+———–+

Hour Details

+——-+————–+———-+ | j | h | H | +=======+==============+==========+ | Digit | Hour12 AM/PM | Military | +——-+————–+———-+ | 13 | 1 PM | 13 | +——-+————–+———-+

Slice
  • The slice pipe is useful when you want a subset of a list or string. One of the common use cases are in when iterating over a list with ngFor for example.

TODO

Decimal
  • Used for formatting numbers given a decimal formatter

TODO

Percent

TODO

Currency

TODO

Async

TODO

Json

TODO

LowerCase

TODO

UpperCase

TODO

Dependency Injection

Dependency Injection is a coding pattern in which a class receives its dependencies from external sources rather than creating them itself. In order to achieve Dependency Injection we need a Dependency InjectionFramework to handle the dependencies for us. Using a DI framework, you simply ask for a class from the injector instead of worrying about the dependencies inside the class itself.

Angular has a standalone module that handles Dependency Injection. This framework can also be used in non-Angular applications to handle Dependency Injection.

TODO

Services and Providers

  • A service is nothing more than a class in Angular 2. It remains nothing more than a class until we register it with the Angular injector.
  • When you bootstrap your app, Angular creates an injector on the fly that can inject services and other dependencies throughout the app.
  • You can register the service or the dependencies during when bootstrapping the app or when defining a component.
  • If you have a class called MyService, you can register it with the Injector and then you can inject it everywhere:
    1   bootstrap(App, [MyService]); // second param is an array of providers
    
  • Providers is a way to specify what services are available inside the component in a hierarchical fashion.
  • A provider can be a class, a value or a factory.
  • Providers create the instances of the things that we ask the injector to inject.
  • [SomeService]; is short for [provide(SomeService, {useClass:SomeService})]; where the first param is the token, and the second is the definition object.
  • A simple object can be passed to the Injector to create a Value Provider:
    1   beforeEachProviders(() => {
    2     let someService = { getData: () => [] };
    3     // using `useValue` instead of `useClass`
    4     return [ provide(SomeSvc, {useValue: someService}) ];
    5   });
    
  • You can also use a factory as a provider.
  • You can use a factory function that creates a properly configured Service:
     1   let myServiceFactory = (dx: DepX, dy: DepY) => {
     2     return new MyService(dx, dy.value);
     3   }
     4 
     5   // provider definition object.
     6   let myServiceDefinition = {
     7      useFactory: myServiceFactory,
     8      deps: [DepX, DepY]
     9   };
    10 
    11   // create provider and bootstrap
    12   let myServiceProvider = provide(MyService, myServiceDefinition);
    13   bootstrap(AppComponent, [myServiceProvider, DepX, DepY]);
    
  • Defining object dependencies is simple. You can make a plain JavaScript object available for injection using a string-based token and the @Inject decorator:
     1   var myObj = {};
     2 
     3   bootstrap(AppComponent, [
     4     provide('coolObjToken', {useValue: myObj})
     5   ]);
     6 
     7   // and you can inject it to a component
     8 
     9   import {Inject} from 'angular2/core'
    10   constructor(dx: DepX, @Inject('coolObjToken') config)
    

Simple Service

In this section we are going to make a simple service and use it in our root component.

Project Files

The project files for this section are in angular2-intro/project-files/angular-examples/services/simple-service;

Getting Started

Let’s get started by creating a class, called StudentSvc that represents our service:

 1 class StudentSvc {
 2   private students: any[];
 3   constructor() {
 4     this.students = [
 5       {name: 'Tom', id: 1},
 6       {name: 'John', id: 2},
 7       {name: 'Kim', id: 3},
 8       {name: 'Liz', id: 4}
 9     ];
10   }
11   getAll() {
12     return this.students;
13   }
14 }

There is nothing special about this class. It’s just a class the has a method to return the list of all students. Now, we are going to make it special by decorating it with the Injectable decorator. But, first we need to import Injectable from Angular:

1 import {Injectable} from 'angular2/core';

After importing the Injectable metadata class, we can decorate our class:

 1 /**
 2  * Student service
 3  */
 4 @Injectable() // <- decorating with `Injectable`
 5 class StudentSvc {
 6   private students: any[];
 7   constructor() {
 8    // ...
 9   }
10   // ...
11 }

Now we have an injectable class and the injector would know how to create an instance of it when we need to inject it. And that’s what we are going to do next. We are going to add StudentSvc in the list of viewProviders of the root component:

1 @Component({
2   selector: 'app',
3   templateUrl : 'templates/app.tpl.html',
4   viewProviders: [StudentSvc] // <- registering the service
5 })

The last thing we need to do is to inject the service in the constructor of our root component:

1 class Root  {
2   private name: string;
3   private students: any[];
4   constructor (studentSvc: StudentSvc) { // <- injecting the service
5     this.name = 'Simple Service Demo';
6     this.students = studentSvc.getAll(); // <- calling the `getAll` method
7   }
8 }
  • In the constructor, we are defining a variable to be of type StudentSvc. By doing that the injector will create an instance from the StudentSvc to be used
  • And on line 6 we are calling the getAll method from the service to get a list of all students.

Finally, we can verify that the getAll method is actually called by printing the students in the template:

app.tpl.html

1 <h1>{{ name }}</h1>
2 
3 <ul>
4   <li *ngFor="#student of students">Name: {{ student.name }}, id: {{ student.id \
5 }}</li>
6 </ul>

and it would output:

1 Name: Tom, id: 1
2 Name: John, id: 2
3 Name: Kim, id: 3
4 Name: Liz, id: 4

Data and State Management

  • Angular is flexible and doesn’t prescribe a recipe for managing data in your apps
  • Since observables are integrated into Angular, you can take advantage of observables to manage data and state
  • You ca use services to manage streams that emit models
  • Components can subscribe to the streams maintained by services and render accordingly.
    • For example, you can have a service for a Todo app that contains a stream of todos and a ListComponent can listen for todos and render when a new task is added.
    • You may have another component that listens for the user that has been assigned to a task provided by a service.
  • The steps for creating different parts of an app can be summarized in three steps:
    • Defining a Model using a class
    • Defining the service
    • Defining the component

Observables

  • Observables can help manage async data
  • Observables are similar to Promises but with a lot of differences
  • Observables emit multiple values over time as opposed to one
  • Angular embraces observables using the RxJS library.
  • Observables emit events and observers observe observables.
  • An observer subscribes to events emitted from an observable.
  • RxJS has an object called subject that can be used both as an observer or an observable. Subject can be imported from RxJS very easily:
    1   import {Subject} from 'rxjs/Subject';
    
  • A subscription can be canceled by calling the unsubscribe method.

TODO

State Management with Observables

  • There are several ways to manage state, one of them is using observables
  • Observables can be used to represent the state of the app
  • Changes in the state are represented as an observable

TODO

Http

  • Using the Http class, you can interact with API endpoints
  • Http is available as an injectable class
  • Http has a request method that returns an Observable which will emit a single Response when a response is received.
  • You can inject http in the constructor of a class: constructor(http: Http) {...}

Getting Data from Server

In this section we are going to use the http class to get a list of students from a server by hitting /api/students

Project Files

The project files for this section are in angular2-intro/project-files/angular-examples/http/get-students

Getting Started

Before anything, let’s add the http.js file from Angular’s bundle. In your index.html file add the following to the head tag:

1 <script src="/node_modules/angular2/bundles/http.js"></script>

After that, we are going to make a service that handles getting data from the endpoint. We are going to call this StudentSvc:

1 @Injectable()
2 class StudentSvc {
3   constructor(private http: Http) {} /* Inject Http */
4   getStudents(): Observable<Response> {
5     return this.http.get('/api/students');
6   }
7 }
  • On line 1, we are using the Injectable decorator to make our class injectable
  • In the constructor we are injecting the Http service and making a reference to it in a private variable http
  • The getStudents method makes a GET call to our local endpoint an returns an Observable

Now that we have the StudentSvc service, we can create a component and inject the StudentSvc to it:

1 @Component({
2   selector: 'app',
3   templateUrl :'templates/app.tpl.html',
4   providers: [StudentSvc] // <- adding to the list of providers
5 })

In addition to the StudentSvc, we also need to add HTTP_PROVIDERS in the providers array:

1 @Component({
2   selector: 'app',
3   templateUrl :'templates/app.tpl.html',
4   providers: [HTTP_PROVIDERS, StudentSvc] // <- adding `HTTP_PROVIDERS`
5 })

After adding the providers, we can define the component class:

1 @Component({...})
2 class HttpGetExample  {
3   private name: string;
4   private students: Observable<Response>;
5   constructor (studentSvc: StudentSvc) {
6     this.name = 'HTTP Get';
7     studentSvc.getStudents().subscribe(resp => this.students = resp.json());
8   }
9 }

If you notice, we are injecting the StudentSvc in the constructor and we are calling the getStudents method in the constructor. The getStudents returns an observable that we can subscribe to get the data out as they arrive. We also call the json method on each response to get the JSON data.

After getting the data, we can print the result in the view:

app.tpl.html

1 <h1>{{ name }}</h1>
2 <ul>
3   <li *ngFor="#student of students">
4     {{ student.name }}, {{ student.lastname }}
5   </li>
6 </ul>

Here we are using the built-in ngFor directive to loop through the array of students and print their name and last name to the page.

Working with Forms

Angular has convenient methods for working with forms, including validation.

TODO

Angular Router

Angular has a stand-alone module responsible for handling routing.

TODO

Unit Testing

Unit testing with Angular requires some set up. First, let’s gather all the libraries and modules that we need.

TODO

Deep Dive

Let’s deep dive into Angular and RxJS concepts

Components in Depth

  • A component declares a reusable building block of an app
  • A TypeScript class is used to define a component coupled with the @component decorator

The @component decorator defines the following:

  • selector: string value defining the css selector targeting an html element
  • inputs: array of string values defining the inputs to the component
  • outputs: array of string values defining the output of the component
  • properties: array of string values defining the properties
  • events: array of string values defining the events
  • host?: {[‘string’]: ‘string’},
  • providers: array of objects: defines the set of injectable objects that are visible to a directive/component and its light DOM children.
  • exportAs: string value defining the exported value
  • moduleId: string value defining the module id
  • viewProviders?: array of objects: defines the set of injectable objects that are visible to a directive/component view DOM children.
  • queries: {[key: string]: any},
  • changeDetection: ChangeDetectionStrategy object defining the strategy for detecting changes:
    • ChangeDetectionStrategy.Default: sets detector mode to CheckAlways
    • ChangeDetectionStrategy.OnPush: sets detector mode to CheckOnce
    • ChangeDetectionStrategy.Detached: change detector sub tree is not a part of the main tree and should be skipped
    • ChangeDetectionStrategy.CheckAlways: after calling detectChanges the mode of the change detector will remain CheckAlways
    • ChangeDetectionStrategy.Checked: change detector should be skipped until its mode changes to CheckOnce
    • ChangeDetectionStrategy.CheckOnce: after calling detectChanges the mode of the change detector will become Checked
  • templateUrl: string value for the url path to the template
  • template: string value for the template
  • styleUrls: array of string values defining url paths to css files
  • styles: array of string values defining css styles:
    • styles: [‘.myclass { color: #000;}’],
  • directives: array of directives used in the component
  • pipes: array of pipes used in the component
  • encapsulation: ViewEncapsulation value that defines template and style encapsulation options:
    • ViewEncapsulation.None: means do not provide any style encapsulation
    • ViewEncapsulation.Emulated: No Shadow DOM but style encapsulation emulation using extra attributes on the DOM (default method)
    • ViewEncapsulation.Native: means provide native shadow DOM encapsulation and styles appear in component’s template inside the shadow root.

TODO

RxJS

  • RxJS is a library for reactive programming
  • Reactive programming is a natural way of thinking about asynchronous code which concerns itself with operations on event streams
  • RxJS is used in this paradigm to compose asynchronous data/event streams
  • Using the reactive paradigm, you can perform operations on streams using a consistent interface regardless of the data source
  • Streams of events/data are known as Observables, i.e. a data/event stream = observable
  • A program can be just composed of different data streams (observables)

TODO