Angular2 first contact

April 15, 2016

  • Components and their Templates
  • Template Syntax
  • Services using Dependency Injetion
  • Routing
  • HTTP and Data

We can use ES5, ES2015, or TypeScript to write Angular 2.

Resources

Architecture

Language Choices

Language Compile Types JS
ES5 No No Yes
ES6/ES2015 Yes (BabelJS) No Yes
Typescript Yes Yes Yes
Dart Yes Yes No

Components

Angular1 Controller

<body ng-controller="MyController as vm">
	<h3 ng-bind="vm.story.name"></h3>
</body>
(function() {
	'use strict';

	angular.module("app")
	.controller('MyController',[MyController]);

	function MyController() {
		var vm = this;
		vm.story = {id:100, name: 'Super cool title'};
	}
})();

Angular2 Component

<my-story></my-story>
import { Component } from 'angular2/core';

@Component({
// this is a decorator and adds functionality to the class
	selector: 'my-story',
	template: '<h3></h3>'
})
export class StoryComponent {
	story = {id: 100, name: 'Super Cool Title'};
	color = 'blue';
}

Bootstrapping Angular

Angular1

<html ng-app="app">

Angular2

import { bootstrap } from 'angular2/platform/browser';
import { AppComponent } from './app.component';

boostrap(AppComponent);

Structural Built-In Directives

Angular1

<ul>
	<li ng-repeat="vehicle in vm.vehicles">
		
	</li>
</ul>
<div ng-if="vm.vehicles.length">
	<h3>You have  vehicles</h3>
</div>

Angular2

ngFor <– Structural directive Indicate by the ‘’ prefix. Changes the structure.

<ul>
	<li *ngFor="#vehicle of vehicles">
		
	</li>
</ul>
<div *ngIf="vehicles.length">
	<h3>You have  vehicles</h3>
</div>

Data Binding

  • Interpolation
  • One Way Binding
  • Event Binding
  • Two Way Binding

Interpolation

Angular1

In angular1 we had to give a context and therefore we added the vm..

<h3></h3>
Angular2
<h3></h3>

One Way Binding

Angular1
<h3 ng-bind="vm.story.name"></h3>
Angular2
<h3 [innerText]="story.name"></h3>

<div [style.color]="color"></div>

We can do this with Any html property.

Event Binding

Angular1
<button
	ng-click="vm.log('click')"
	ng-blur="vm.log('blur')">OK></button>
Angular2

There are no directives, these are HTML EVENTS!

<button
	(click)="log('click')"
	(blur)="log('blur')">OK></button>

Two Way Binding

Angular1
<input ng-model="vm.story.name">
Angular2

Banana in a Box

<input [(ngModel)]="story.name">

Example

<div>
	<h3></h3>
	<div>
		2 Way Binding
		<input [(ngModel)]="story.name">
	</div>
	<p></p>
	<div>
		1 Way Binding
		<input [value]="story.name">
	</div>
</div>

Less Directives

40+ Angular1 Built-In Directives that go away.

Now we just bind to the html events for example.

<!-- Angular1 -->
<div ng-style="vm.story ? {visibility: 'visible'} : {visibility: 'hidden'}"></div>

<!-- Angular2 -->
<div [style.visibility]="story ? 'visible' : 'hidden'"></div>
<!-- Angular1 -->
<img ng-src="">
<!-- Angular2 -->
<img [src]="imagePath">
<!-- Angular1 -->
<a ng-href=""></a>
<!-- Angular2 -->
<a [href]="link"></a>

Services

  • Angular1
    • Factories
    • Services
    • Providers
    • Constants
    • Values
  • Angular2
    • Class

Angular1

(function() {
	'use strict';

	angular.module("app")
	.service('VehicleService',[VehicleService]);

	function VehicleService() {
		this.getVehciles = function () {
			return [
				{id:1, name="xyz" },
				{id:2, name="xyz-342" },
				{id:3, name="a2000" },
			];
		};
	}
})();

Angular2

import {Injectable} from 'angular2/core';

@Injectable()
export class VehicleService {
	getVehicles = () => [
		{id:1, name="xyz" },
		{id:2, name="xyz-342" },
		{id:3, name="a2000" }
	];
}

Dependency Injection

Angular1 Registration

angular
	.module('app')
	.service('VehicleService', VehicleService);

Angular2 Registration

See the providers attribute

import {VehicleService} from '/vehicle.service';

@Component({
	selector: 'my-vehicles',
	templateUrl: 'app/vehicles.component.html',
	providers: [VehicleService]
})

Angular1 Injection

angular
	.module('app')
	.controller('VehiclesController', VehiclesController);

	VehiclesController.$inject = ['VehiclesService']; // <-- We are injecting here the service

Angular2 Injection

See the injection in the constructor sentence.

import { VehicleService} from './vehicle.service';

@Component({
	selector: 'my-vehicles',
	templateUrl: 'app/vehicles.component.html',
	providers: [VehicleService]
})
export class VehiclesComponent {
	constructor(private _vehicleService: VehicleService) {}
	vehicles = this._vehicleService.getVehicles();
}

Essentials

  • Modules
  • Components
  • Templates
  • Metadata

Modules

A module exports an asset such as a Service, Component, or a shared value.

We use ES6 style modules with Angular2.

Exporting Modules

// vehicle.service.ts

export interface Vehicle {
	id: number;
	name: string;
}

export class VehicileService {
	// ...
}

Importing Modules

Modules and their contents can be imported using the import keyword.

We import the Vehicle and VehicleService using destructuring.

// vehicle.component.ts
import { Component } from 'angular2/core';
import { Vehicle, VehicleService } from '../vehicle.service';

Components

A component contains application logic that controls a region of the user interface that we call view.

import { Component } from 'angular2/core';
import { Vehicle } from './vehicle.service';

// @Component is Metadata, we describe the component
@Component({
	selector: 'story-vehicles',
	templateUrl: './vehicles.component.html'
})

// We define the component export class VehicleListComponent { vehciles: Vehicle[]; }

Structure Example

  • App Component // main.ts
    • Header Component
    • Nav Component
    • Content Component
    • Footer Component
// main.ts

// Entry point for the app
// This is where we start

import { bootstrap } from 'angular2/platform/browser';
import { StoryComponent } from './story.component';

bootstrap(StoryComponent);
// story.component.ts

@Component({
	selector: 'my-story',
	templateUrl: 'app/story.component.html'
})
export class StoryComponent {
	name = 'Han Solo';
}
<!-- story.component.html -->
<h3>My name is </h3>
<!-- index.html -->
<my-story></my-story>

Templates

Templates are mostly HTML, with a little help from Angular. They tell Angular how to render the Component.

  • directives
  • interpolation
  • nested components
<ul>
	<li *ngFor="#vehicle of vehicles">
		 
	</li>
</ul>

<vehicle *ngIf="selectedVehicle" [vehicle]="selectedVehicle"></vehicle>

Nested Components

Here we can see how our CharacterListComponent is using our CharacterComponent.

// character-list.component.ts
import { Component } from 'angular2/core';
import { Character } from './character';
import { CharacterComponent } from './character.component';

// directives: [CharacterComponent] <-- is very important because tells the nested components

@Component({
	selecctor: 'my-chacter-list',
	templateUrl: 'app/character-list.component.html',
	directives: [CharactercComponent]
})
export class CharacterListComponent {
	selectedCharacter: Character;
	characters = [
		new Character(1, 'Han Solo'),
		new Character(2, 'Luke Skywalker'),
		new Character(3, 'BB-8'),
		new Character(4, 'Rey')
	];

	select(character: Character) {
		this.selectedCharacter = character;
	}
}
<!-- CharacterListComponent -->
<ul>
	<li *ngFor="#character of characters" (click)="select(character)"></li>
</ul>

<my-character *ngIf="selectedCharacter" [character]="selectedCharacter"></my-character>
// character.component.ts
import { Component, Input } from 'angular2/core';
import { Character } from './character';

@Component({
	selector: 'my-character',
	templateUrl: 'app/character.component.html'
})
export class CharacterComponent {
	@Input() character: Character;
}
<h3>We selected </h3>

Metadata

We use Metadata to tell Angular about the objects we build.

@Component({
    slector: 'story-characters',
    templateUrl: './app/characters.component.html',
    styleUrls: ['./app/characters.component.css'],
    directives: [CharacterDetailComponent],
    providers: [HTTP_PROVIDERS, CharacterService]
})
@Component({
    selector: 'story-characters',
    templateUrl: './app/characters.component.html',
    styleUrls: ['./app/characters.component.css'],
    directives: [CharacterComponent],
    providers: [CharacterService]
})
export class CharactersComponent implements OnInit {
    @Output() changed = new EventEmitter<Character>();
    @Input() storyId: number;
    characters: Character[];
    selectedCharacter: Character;

    constructor(private _characterService: CharacterService) {}

    ngOnInit() {
        this._characterService.getCharacters(this.storyId)
            .subscribe(characters => this.characters = characters);
    }

    select(selectedCharacter: Character) {
        this.selectedCharacter = selectedCharacter;
        this.changed.emit(selectedCharacter);
    }
}

Communication with Input and Output

Components allow input properties to flow in, while output events allow a child Component to communicate with a parent Component.

export class CharactersComponent implements OnInit {
    @Output() changed = new EventEmitter<Character>();
    @Input() storyId: number;
    characters: Character[];
    selectedCharacter: Character;

    select(selectedCharacter: Character) {
        this.selectedCharacter = selectedCharacter;
        this.changed.emit(selectedCharacter);
    }
}
<div>
    <h1>Storyline Tracker</h1>
    <h3>Component Demo</h3>
    <story-characters [storyId]="7" (changed)=changed($event)></story-characters>
</div>

ViewChild

Use ViewChild when a parent Component needs to access a member of its child Component

Child

export class FilterComponent {
    @Output() changed: EventEmitter<string>;
    filter: string;

    clear() {
        this.filter = '';
    }
    // ...
}

Parent

export class CharacterListComponent { characters: Character[]; @ViewChild(Filtercomponent) filter: FilterComponent;

filtered = this.characters;

getCharacters() {
    this._characterService.getCharacters()
        .subscribe(characters => {
            this.characters = this.filtered = characters;
            this.filter.clear();
            });
}

// ... }

Displaying Data

Data Binding

We use data binding to help coordinate communication between a Component and its Template.

  • Interpolation:
  • One Way Binding: [property] = “expression”
  • Event Binding: (event) = “statement”
  • Two Way Binding: [(ngModel)] = “property”

Unidirectional data flow

Interpolation

One Way In.

<h3>Vehicle: </h3>
<div>
    <img src="">
    <a href="">Wiki</a>
    <!-- the quesetion mark makes it optional -->
    My vehicle is <span></span>
</div>

Property Binding

Using the [] to send values from the Component to the Template.

We set properties and events of DOM elements, not attributes.

We use the Metadata to tell the Component about the Template.


[target] = "expression"
bind-target = "expression"
<img [src]="vehicle.imageUrl" > <!-- element property -->
<vehicle-detail [vehicle]="currentVehicle"></vehicle-detail> <!-- component property -->
<div [ngClass]= "{selected: isSelected}">X-Wing</div> <!-- directive property -->
  • For attributes we use attr
  • Use dots for nested properties
<button [attr.aria-label]>ok</button> <!-- attribute binding -->
<div [class.isStopped="isStopped"]>Stopped</div> <!-- class property binding -->
<button [style.color]="isStopped ? 'red' : 'blue'"></button> <!-- style property binding -->

Event Binding

(target) = "statement"
on-target = "statement"
<button (click)="save()">Save</button> <!-- element event -->
<vehicle-detail (changed)="vehicleChanged()"></vehicle-detail> <!-- component event -->

One way to the Component

<input [value]="vehicle.name"
(input)="vehicle.name=$event.target.value">
<!-- (input): Input change event-->
<!-- $event: is the event message -->

Custom Events

  • EventEmitter defines a new event
  • Fire its emit method to raise event with data
@Input() vehicle: Vehicle;
@Output() onChanged: new EventEmitter<Vehicle>();
changed() { this.onChanged.emit(this.vehicle); }
<button (click)="select(character.name)"></button>
select(name: string) {
	let msg = `You selected ${name}`;
	console.log(msg);
	this.isSelected = !this.isSelected;
}
<input type="text" id="nametext" class="my-input" 
[value]="character.name" 
(input)="character.name=$event.target.value">

<img [src]="character.imageUrl" style="width:50px" 	
 [style.background]="color"
 (mouseenter)="color = '#CCC'" 
 (mouseleave)="color = '#EEE'">

Two Way Binding

[()] sends a value from Component to Template, and sends value changes in the Template to the Component.

[(ngModel)]="expression"
bindon-NgModel="expression"
<input [(ngModel)]="vehicle.name"> <!-- built-in directive -->

Built-in Directives

When Angular renders templates, it transforms the DOM according to instructions from Directives.

Angular1 Angular2 Angular2 example Alternative way
ng-class ngClass [ngClass]=”{active: isActive, color: myColor}”  
ng-style ngStyle [ngStyle]=”{color:colorPreference}” [style.color]=”colorPreference”
ng-repeat *ngFor    
ng-if *ngIf    
ng-switch *ngSwitch    
<div [ngStyle]="setStyles()"></div> <!-- ngStyle is better for setting multiple styles -->
<div [ngClass]="setClasses()"></div> <!-- the same for multiple classes -->

ngIf conditionally removes elements from the DOM.

<div *ngIf="currentVehicle">
	You selected 
</div>
<!-- # declares a local variable -->
<div *ngFor="#story of stories, #i=index">
 . 
</div>

Pipes (filters in Angular1)

Pipes allow us to transform data for display in a Template.

<p></p>
<p></p>
<p></p>
<p></p>
<p></p>
<!-- {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits} -->
<p></p>
<p></p>

Async Pipe

Using pipes we can transform values before rendering them.

Subscribes to a Promise or an Observable, returning the latest value emitted.

import { Pipe, PipeTransform } from 'angular2/core';

@Pipe({name: 'myCustomPipe'})
exports class MyCustomPipe implements PipeTransform {
	transform(value: string, args: any[]) {
		return // transformed value
	}
}

Services, Dependency Injection, and Component Lifecycle Hooks

  • Services
  • Dependency Injection
  • Component Lifecycle Hooks

Services

A service provides anything our application needs. It often shares data or functions between other Angular features.

In Angular1 we had:

  • Factories
  • Services
  • Providers
  • Constants
  • Values

But in Angular2 we do all of them with Class.

// vehicle.service.ts

@Injectable()
export class VehcileService {
	getVehicles() {
		return [
			new Vehicle(10, 'abc3d'),
			new Vehicle(12, 'ddasd2000'),
			new Vehicle(14, 'XZY200')
		];
	}
}
import { Injectable } from 'angular2/core';

// the import from another file would be:
// import { Character } from './character.service'
export class Character {
	constructor(public id: number, public name: string, public color: string);
}

@Injectable()
export class CharacterService {
	getCharacters() {
		return [
			new Character(10, 'abc3d'),
			new Character(12, 'ddasd2000'),
			new Character(14, 'XZY200')
		];
	}
}

Dependency Injection

Dependency Injection is how we provide an instance of a class to another Angular feature

Service is injected into the Component’s constructor

// vehicle.component.ts
import { VehicleService } from './vehicle.service';

@Component({
	selector: 'my-vehicles',
	templateUrl: 'app/vehicles.component.html',
	providers: [VehicleService]
})
export class VehicleListComponent {
	vehicles: Vehicle[];

	constructor(private _vehicleService: VehicleService) {
		this._vehicleService.getVehicles()
			.subscribe(vehicles => this.vehicles = vehicles);
	}
}
@Injectable()
export class VehicleService {
	constructor(private _http: Http) {}

	getVehicles() {
		return this._http.get(vehicleUrl)
			.map((response:Response) => <Vehicle[]>response.json().data);
	}
}

Register the service with the injector at the parent that contains all components that require the service. Otherwise you will get more than one instance!

Component Lifecycle Hooks

Lifecycle Hooks allow us to tap into specific moments in the application lifecycle to perform logic.

@Component({
    selector: 'story-characters',
    templateUrl: './app/characters.component.html',
    styleUrls: ['./app/characters.component.css'],
    directives: [CharacterDetailComponent],
    providers: [HTTP_PROVIDERS,CharacterService]
})
// Implements the lifecycle hook's interface OnInit
export class CharactersComponent implements OnInit {
    @Output() changed = new EventEmitter<Character>();
    @Input() storyId: number;
    characters: Character[];
    selectedCharacter: Character;

    constructor(private _characterService: CharacterService) {}

    // When the Component initializes, the ngOnInit function is executed
    ngOnInit() {
        this.characters = this._characterService.getCharacters(this.storyId);
    }

    select(selectedCharacter: Character) {
        this.selectedCharacter = selectedCharacter;
        this.changed.emit(selectedCharacter);
    }
}

Component Lifefycle

The Lifecycle Interface helps enforce the valid use of a hook.

  • ngOnChanges
  • ngOnInit (time of creation)
  • ngAfterViewInit
  • ngOnDestroy

Example

<ul>
    <li *ngFor="#character of characters" (click)="select(character)">
    
    </li>
</ul>

<button (click)="clear()">clear</button>
<ul class="messages">
	<li *ngFor="#msg of messages"></li>
</ul>

<my-character *ngIf="selectedCharacter" (onLifecycleHookFire)="log($event)" [character]="selectedCharacter"></my-character>
// character-list.component.ts

import { Component, OnInit } from 'angular2/core';
import { Character, CharacterService } from './character.service';
import { CharacterComponent} from './character.component';

@Component({
	selector: 'my-character-list',
	templateUrl: 'app/character-list.component.html',
	styles: ['li {cursor: pointer;}'],
	directives: [CharacterComponent]
})
export class CharacterListComponent implements OnInit {
	selectedCharacter: Character;
	characters: Character[] = [];
	messages: string[] = [];

	constructor(private _characterService: CharacterService) {}

	ngOnInit(){
		this.chracters = this._characterService.getCharacters();
	}

	select(character: Character) {
		this.slectedCharacter = character;
	}

	clear() {
		this.selectedCharacter = null;
	}

	log(msg: string){
		this.messages.splice(0,0,msg);
		console.log(msg);
	}
}
import { Component, EventEmitter, Input, Output, OnChanges, OnInit, AfterViewInit, OnDestgory} from 'angular2/core';
import { Character } from './character.service';

@Component({ selector: ‘my-character’, templateUrl: ‘app/character.component.html’ }) export class CharacterComponent implements OnChanges, OnInit, AfterViewInit, OnDestroy { @Input() character: Character; @Output() onLifecycleHookFire = new EventEmitter();

ngOnChanges() {
	this.onLifecycleHookFire.emit(`ngOnChanges for ${this.character.name}`);
}

ngOnInit() {
	this.onLifecycleHookFire.emit(`ngOnInit for ${this.character.name}`);
}

ngAfterViewInit() {
	this.onLifecycleHookFire.emit(`ngAFterViewInit for ${this.character.name}`);
}

ngOnDestroy() {
	console.log(`ngOnDestroy for ${this.character.name}`);
} }

Data with Http

  • HTTTP
  • Observables and Subscriptions
  • Async Pipe
  • Promises

HTTP

We use HTTP to get and save data with Promises or Observables. We isolate the HTTP calls in a shared Service.

// angular1
this.getVehicles = function() {
	return $http.get('api/vehicles')
		.then(function (response) {
			return response.data.data;
		})
		.catch(handleError);
}
// angular2
getVehicles() {
	return this._http.get('api/vehicles')
		.map((response: Response) =>
			<Vehicle[]>response.json().data
		)
		.catch(this.handleError);
}

HTTP script

  • htpp is in a seperate module
  • add th reference to http.dev.js

Step by Step

  • Add script reference to http in index.html
  • Register the Http providers
  • Call Http.get in a Service and return the mapped result
  • Subscribe to the Service’s function in the Component
<!-- angular2 -->
<script src="../node/modules/angular2/bundles/http.dev.js"></script>
import { Component } from 'angular2/core';
//  Located in module angular2/http
import { HTTP_PROVIDERS } from 'angular2/http';

import { Vehicle, VehicleService } from './vehicle/service';
import { VehicleListComponent } from './vehicle-list.component';

@Component({
	selector: 'my-app',
	template: '<my-vehicle-list></my-vehicle-list>',
	directives: [VehcileListComponent],
	providers: [
		// declaring the providers
		HTTP_PROVIDERS,
		VehicleService
	]
})
export class AppComponent {}

HTTP_PROVIDERS is an array of service providers for HTTP.

@Injectable()
export class VehicleService {
	constructor(private _http: Htpp) {}

	getVehicles() {
		// make and return the async GET call
		return this._http.get('api/vehicles.json')
			// we map the response
			.map((response: Response) => <Vehicle[]>response.json().data)
			.catch(this.handleError);
	}

	// handle the exception. always!!
	private handleError(error: Response) {
		console.error(error);
		return Observable.throw(error.json().error || 'Server error');
	}
}
// Subscribing to the Observable

constructor(private _vehicleService: VehicleService) { }	
ngOnInit() { this.getHeroes(); }
getHeroes() {
	this._vehicleService.getVehicles()
		// subscribe to the observable
		.subscribe(
			// success an failure cases
			vehicles => this.vehicles = vehicles,
			error => this.errorMessage = <any>error
		);
}

Adding Exception Handling

import { Injectable } from 'angular2/core';
import { Http, Response } from 'angular2'/http';
import { Observable } from 'rxjs/Rx';

export class Vehicle {
	constructor(public id: number, public name: string) {}

@Injectable()
export class VehicleService {
	constructor(private _http: Http) {}

	getVehicles() {
		return this._http.get('api/vehicles.json')
		.map((response: Response) => <Vehcile[]>response.json().data)
		.do(data => console.log.(data))
		.catch(this.handleError);
	}

	handleError(error: Response) {
		console.error(error);
		return Observable.throw(error.json().error || 'Server error');
	}
}
}
}
import { Component } from 'angular2/core';
import { Vehicle, VehicleService } from './vehcile.service';
import { VehicleComponent } form './vehicle.component';

@Component({
	selector: 'my-vehicle-list',
	templateUrl: 'app/vehicle-list.component.html',
	styles: ['li {cursoir: pointer;} .error {color:red;}'],
	directives: [vehicleComponent]
})
export class VehicleListComponent {
	errorMessage: string;
	vehicles: Vehicle[]:
	selectedVehicle: Vehicle;

	constructor(private _vehicleService: VehicleService) {}

	ngOnInit() { this.getHeroes(); }

	getHeroes() {
		this._vehicleService.getVehicles()
			.subcribe(
				vehicles => this.vehicles = vehicles,
				error => this.errorMessage = <any>error
			);
	}

	select(vehicle: Vehicle) {
		this.selectedVehicle = vehicle;
	}
}

RxJs

ReactiveX

RxJs (Reactive Js) implements the asynchronous observable pattern and is widely used in Angular 2.

import 'rxjs/Rx';
// for production, only import the modules you require

We do not return the response. Service does the dirty work. The consumers simply get the data.

// vehicle.service.ts
return this._http.get('api/vehicles')
	.map((response:: Response) => 
		// json() is defined by the http spec
		// data is what we defined on the server
		<Vehicle[]>response.json().data
	)
	// catch errors
	.catch(this.handleError);

We sometimes pass error messages to the consumer for presentation.

Async Pipe

The Async Pipe receives a Promise or Observable as input and subscribes to the input, eventually emitting the value(s) as changes arrive.

  • Component is simplified
  • Grab the Observable and set it to the property
// vehicle-list.component.ts

vehicles: Observable<Vehicle[]>; // property becomes observable

getHeroes() {
	this.vehicles = this._vehicleService.getVehicles(); // set the observable from the Service
}
<ul>
	<li *ngFor="#vehicle of vehicles | async">
		
	</li>
</ul>

Promises

getVehciles(value? : stgring) {
	return this._http.get('api/vehcles.json')
		.map((response: Response) => <Vehicle[]>response.json().data)
		.toPromise()
		.catch(this.handleError);
}
export class VehicleListComponent {
	errorMessage: string;
	vehicles: Promise<Vehicle[]>; // <--
	selectedVehicle: Vehicle;

	constructor(private _vehcileServie: vehicleService) {}

	// ...
}
<ul>
	<li *ngFor="#vehicle of vehicles | async" (click)="select(vehicle)"></li>
</ul>

Routing

  • Route Configuration
  • Router Outlets
  • Router Links
  • Route Parameters
  • Child Routers

Intro

Routing allows our application to navigate between different Components, passing parameters where needed

@RouteConbfig({})
<router-outlet></router-outlet>

<a [routerLink]="linkParameters"></a>
RouteParams
// $routeParams in angular1
Router
// $router in angular1
<script src="node_modules/angular2/bundles/router.dv.js"></script>

Routing in 4 steps

  1. ROUTER_PROVIDERS
  2. @RouteConfig
  3. <router-outlet>
  4. [routerLink]

RouteConfig

  • @RouteConfig accepts an array of route definitions
  • path maps the address
  • component maps to the Component and its Template
@RouteConfig([
// the name of the route must be PascalCase 
// Default route
{ path: '/', name: 'Vehicles', component: VehicleListComponent, useAsDefault: true},
// :id is a routing parameter
{ path: '/list/:id', name: 'Vehicles', component: VehicleListComponent },
{ path: '/:id', name: 'Vehicle', component: VehicleComponent }
])
export class VehiclesComponent {}
@Component({
	selector: 'story-vehicles',
	template: `<router-outlet></router-outlet>`,
	directives: [ROUTER_DIRECTIVES],
	providers: [VehicleService]
})
<nav>
	<a [routerLink]="['Dashboard']">Dashboard</a>
	<a [routerLink]="['Characters']">Characters</a>
	<a [routerLink]="['Vehicles']">Vehicles</a>
	<router-outlet></router-outlet>
</nav>

Example

// app.component.ts

import { Component } from 'angular2/core';
import { HTTP_PROVIDERS, @RouteConfig } from 'angular2/http';
import { ROUTER_PROVIDERS, ROUTER_DIRECTIVES, RouteConfig } from 'angular2/router'; // <--
import 'rxjs/Rx'; // load the full rxjs

import { CharacterListComponent } from './characters/character-list.component';
import { CharacterService } from './characters/character.service';
import { VehicleListComponent } from './vehicles/vehicle-list.component';
import { VehicleComponent } from './vehicles/vehicle.component';
import { VehicleService } from './vehicles/vehicle.service';
import { CONFIG } from './config';

@Compontent({
	selector: 'story-app',
	templateUrl: 'app/app.component.html',
	styles: [`
		nav ul {list-style-type: none;}
		nav ul li {padding: 4px; cursor: pointer; display: inline-block}
	`],
	directives: [ROUTER_DIRECTIVES],
	providers: [
		HTTP_PROVIDERS,
		ROUTER_PROVIDERS, // <--
		CharacterService,
		VehicleService
	]
})
@RouteConfig([ // <-- 
	{path: '/characters', name: 'Characters', component: CharacterListComponent, useAsDefault: true},
	{path: '/vehicles', name: 'Vehciles', component: VehicleListComponent},
	{path: '/vehicle/:id', name: 'Vehicle', component: VehicleComponent}
])
export class AppComponent{}
<div>
	<header>
		<h1>Title</h1>
		<h3>Subtitle</h3>
		<nav>
			<ul>
				<li><a [routerLink]="['Characters']">Characters</a></li>
				<li><a [routerLink]="['Vehicles']">Vehicles</a></li>
			</ul>
		</nav>
	</header>
	<main>
		<section>
			<router-outlet></router-outlet>
		</section>
	</main>
</div>
// vehicle.component.ts

import { Component, Input, OnInit } from 'angular2/core';
import { RouteParams } from 'angular2/core';
import { VehicleService, Vehicle } from './vehicle.service';

@Component({
	selector: 'story-vehicle',
	templateUrl: 'app/vehicles/vehicle.component.html'
})
export class VehicleComponent implements OnInit {
	@Input() vehicle: Vehicle;

	constructor(
		private _routeParams: RouteParams, 
		private _vehicleService: VehicleService
		) {}

	ngOnInit() {
		if( !this.vehicle) {
			let id = +this._routeParams.get('id');
			this._vehicleService.getVehicle(id)
				.subscribe((vehicle: Vehicle) => this.vehicle = vehicle);
		}
	}
}
<article>
	<h4>My Vehicles</h4>

	<ul class="vehicles">
		<li *ngFor="#vehicle of vehicles">
			<a href="" [routerLink]="['Vehicle', {id: vehicle.id}]">
			. 
			</a>
		</li>
	</ul>
</article>
<article>
	<div *ngIf="vehicle">
		<h4> details</h4>
	</div>

	<div>
		<label for="nametext">Name</label>
		<input type="text" id="nametext" [(ngModel)]="vehicle.name">
	</div>

	<div>
		<label for="typetext">Type</label>
		<input type="text" id="typetext" [(ngModel)]="vehicle.type">
	</div>
</article>

Child Routers

A Component may define routes for other Components. This creates a series of hierachical child routes.

@RouteConfig accepts an array of route definitions.

This indicates a child route.


The router can help us with lazyloading the modules.

```typescript
@RouteConfig([
	{ path: '/dashboard', name: 'Dashbaord', component: DashboardComponent, useAsDefault: true},
	{ path: '/characters/...', name: 'Characters', component: CharactersComponent },
	{ path: '/vehicles/...', name: 'Vehicles', component: VehiclesComponent }
])