build(aio): new migration

This commit is contained in:
Jesus Rodriguez 2017-04-01 01:57:13 +02:00 committed by Alex Rickabaugh
parent 309bae0a0b
commit 0e38bf9de0
147 changed files with 12713 additions and 11268 deletions

View File

@ -22,6 +22,7 @@ protractor-helpers.js
**/js-es6*/**/*.js
dist/
# special
!/*
!*.1.*
@ -62,3 +63,6 @@ dist/
# style-guide
!style-guide/src/systemjs.custom.js
# plunkers
*plnkr.no-link.html

View File

@ -15,8 +15,6 @@ export default {
// should intercept ... but doesn't in some rollup versions
if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }
// intercepts in some rollup versions
if ( warning.indexOf("The 'this' keyword is equivalent to 'undefined'") > -1 ) { return; }
// console.warn everything else
console.warn( warning.message );

View File

@ -4,29 +4,10 @@ import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';
// #docregion minimal-logger
// class used as a restricting interface (hides other public members)
export abstract class MinimalLogger {
logInfo: (msg: string) => void;
logs: string[];
}
// #enddocregion minimal-logger
/*
// Transpiles to:
// #docregion minimal-logger-transpiled
var MinimalLogger = (function () {
function MinimalLogger() {}
return MinimalLogger;
}());
exports("MinimalLogger", MinimalLogger);
// #enddocregion minimal-logger-transpiled
*/
// #docregion date-logger-service
@Injectable()
// #docregion date-logger-service-signature
export class DateLoggerService extends LoggerService implements MinimalLogger
export class DateLoggerService extends LoggerService
// #enddocregion date-logger-service-signature
{
logInfo(msg: any) { super.logInfo(stamp(msg)); }

View File

@ -0,0 +1,26 @@
// Illustrative (not used), mini-version of the actual HeroOfTheMonthComponent
// Injecting with the MinimalLogger "interface-class"
import { Component, NgModule } from '@angular/core';
import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service';
// #docregion
@Component({
selector: 'hero-of-the-month',
templateUrl: './hero-of-the-month.component.html',
// Todo: move this aliasing, `useExisting` provider to the AppModule
providers: [{ provide: MinimalLogger, useExisting: LoggerService }]
})
export class HeroOfTheMonthComponent {
logs: string[] = [];
constructor(logger: MinimalLogger) {
logger.logInfo('starting up');
}
}
// #enddocregion
// This NgModule exists only to avoid the Angular language service's "undeclared component" error
@NgModule({
declarations: [ HeroOfTheMonthComponent ]
})
class NoopModule {}

View File

@ -0,0 +1,9 @@
<h3>{{title}}</h3>
<div>Winner: <strong>{{heroOfTheMonth.name}}</strong></div>
<div>Reason for award: <strong>{{heroOfTheMonth.description}}</strong></div>
<div>Runners-up: <strong id="rups1">{{runnersUp}}</strong></div>
<p>Logs:</p>
<div id="logs">
<div *ngFor="let log of logs">{{log}}</div>
</div>

View File

@ -1,19 +1,19 @@
/* tslint:disable:one-line:check-open-brace*/
// #docplaster
// #docregion opaque-token
import { OpaqueToken } from '@angular/core';
// #docregion injection-token
import { InjectionToken } from '@angular/core';
export const TITLE = new OpaqueToken('title');
// #enddocregion opaque-token
export const TITLE = new InjectionToken<string>('title');
// #enddocregion injection-token
// #docregion hero-of-the-month
import { Component, Inject } from '@angular/core';
import { DateLoggerService,
MinimalLogger } from './date-logger.service';
import { DateLoggerService } from './date-logger.service';
import { Hero } from './hero';
import { HeroService } from './hero.service';
import { LoggerService } from './logger.service';
import { MinimalLogger } from './minimal-logger.service';
import { RUNNERS_UP,
runnersUpFactory } from './runners-up';
@ -22,28 +22,16 @@ import { RUNNERS_UP,
const someHero = new Hero(42, 'Magma', 'Had a great month!', '555-555-5555');
// #enddocregion some-hero
const template = `
<h3>{{title}}</h3>
<div>Winner: <strong>{{heroOfTheMonth.name}}</strong></div>
<div>Reason for award: <strong>{{heroOfTheMonth.description}}</strong></div>
<div>Runners-up: <strong id="rups1">{{runnersUp}}</strong></div>
<p>Logs:</p>
<div id="logs">
<div *ngFor="let log of logs">{{log}}</div>
</div>
`;
// #docregion hero-of-the-month
@Component({
selector: 'hero-of-the-month',
template: template,
templateUrl: './hero-of-the-month.component.html',
providers: [
// #docregion use-value
{ provide: Hero, useValue: someHero },
// #docregion provide-opaque-token
// #docregion provide-injection-token
{ provide: TITLE, useValue: 'Hero of the Month' },
// #enddocregion provide-opaque-token
// #enddocregion provide-injection-token
// #enddocregion use-value
// #docregion use-class
{ provide: HeroService, useClass: HeroService },
@ -52,9 +40,9 @@ const template = `
// #docregion use-existing
{ provide: MinimalLogger, useExisting: LoggerService },
// #enddocregion use-existing
// #docregion provide-opaque-token, use-factory
// #docregion provide-injection-token, use-factory
{ provide: RUNNERS_UP, useFactory: runnersUpFactory(2), deps: [Hero, HeroService] }
// #enddocregion provide-opaque-token, use-factory
// #enddocregion provide-injection-token, use-factory
]
})
export class HeroOfTheMonthComponent {

View File

@ -0,0 +1,22 @@
// #docregion
// Class used as a "narrowing" interface that exposes a minimal logger
// Other members of the actual implementation are invisible
export abstract class MinimalLogger {
logs: string[];
logInfo: (msg: string) => void;
}
// #enddocregion
/*
// Transpiles to:
// #docregion minimal-logger-transpiled
var MinimalLogger = (function () {
function MinimalLogger() {}
return MinimalLogger;
}());
exports("MinimalLogger", MinimalLogger);
// #enddocregion minimal-logger-transpiled
*/
// See http://stackoverflow.com/questions/43154832/unexpected-token-export-in-angular-app-with-systemjs-and-typescript/
export const _ = 0;

View File

@ -1,12 +1,12 @@
// #docplaster
// #docregion
import { OpaqueToken } from '@angular/core';
import { InjectionToken } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
// #docregion runners-up
export const RUNNERS_UP = new OpaqueToken('RunnersUp');
export const RUNNERS_UP = new InjectionToken<string>('RunnersUp');
// #enddocregion runners-up
// #docregion factory-synopsis

View File

@ -0,0 +1,9 @@
{
"description": "Dynamic Component Loader",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js"
],
"tags":["cookbook component"]
}

View File

@ -11,11 +11,12 @@ import { AdComponent } from './ad.component';
template: `
<div class="ad-banner">
<h3>Advertisements</h3>
<template ad-host></template>
<ng-template ad-host></ng-template>
</div>
`
// #enddocregion ad-host
})
// #docregion class
export class AdBannerComponent implements AfterViewInit, OnDestroy {
@Input() ads: AdItem[];
currentAddIndex: number = -1;
@ -53,3 +54,4 @@ export class AdBannerComponent implements AfterViewInit, OnDestroy {
}, 3000);
}
}
// #enddocregion class

View File

@ -3,7 +3,7 @@
// #docregion imports
import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
import { APP_CONFIG, AppConfig } from './app.config';
import { Logger } from './logger.service';
import { UserService } from './user.service';
// #enddocregion imports

View File

@ -1,7 +1,7 @@
// #docregion token
import { OpaqueToken } from '@angular/core';
import { InjectionToken } from '@angular/core';
export let APP_CONFIG = new OpaqueToken('app.config');
export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// #enddocregion token
// #docregion config

View File

@ -1,30 +0,0 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Homepage Hello World', function () {
beforeAll(function () {
browser.get('');
});
// Does it even launch?
let expectedLabel = 'Name:';
it(`should display the label: ${expectedLabel}`, function () {
expect(element(by.css('label')).getText()).toEqual(expectedLabel);
});
it('should display entered name', function () {
let testName = 'Bobby Joe';
let nameEle = element.all(by.css('input')).get(0);
nameEle.getAttribute('value').then(function(value: string) {
nameEle.sendKeys(testName);
let newValue = value + testName; // old input box value + new name
expect(nameEle.getAttribute('value')).toEqual(newValue);
}).then(function() {
// Check the interpolated message built from name
let helloEle = element.all(by.css('h1')).get(0);
expect(helloEle.getText()).toEqual('Hello ' + testName + '!');
});
});
});

View File

@ -1,9 +0,0 @@
{
"description": "Hello World",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1].*"
]
}

View File

@ -1,16 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HelloWorldComponent } from './hello_world';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [ HelloWorldComponent ],
bootstrap: [ HelloWorldComponent ]
})
export class AppModule { }

View File

@ -1,7 +0,0 @@
<!-- #docregion -->
<label>Name:</label>
<!-- data-bind to the input element; store value in yourName -->
<input type="text" [(ngModel)]="yourName" placeholder="Enter a name here">
<hr>
<!-- conditionally display `yourName` -->
<h1 [hidden]="!yourName">Hello {{yourName}}!</h1>

View File

@ -1,15 +0,0 @@
// #docregion
import { Component } from '@angular/core';
@Component({
// Declare the tag name in index.html to where the component attaches
selector: 'hello-world',
// Location of the template for this component
templateUrl: './hello_world.html'
})
export class HelloWorldComponent {
// Declaring the variable for binding with initial value
yourName: string = '';
}

View File

@ -1,32 +0,0 @@
<!-- #docregion -->
<!DOCTYPE html>
<html>
<head>
<title>Angular Hello World</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- 1. Load libraries -->
<!-- Polyfills -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.8.4"></script>
<script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
<script src="https://unpkg.com/typescript@2.2.1/lib/typescript.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<!-- Display the application -->
<body>
<hello-world>Loading...</hello-world>
</body>
</html>

View File

@ -1,5 +0,0 @@
// #docregion
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -1,17 +0,0 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Homepage Tabs', function () {
beforeAll(function () {
browser.get('');
});
// Does it even launch?
let expectedAppTitle = 'Tabs Demo';
it(`should display app title: ${expectedAppTitle}`, function () {
expect(element(by.css('h4')).getText()).toEqual(expectedAppTitle);
});
});

View File

@ -1,9 +0,0 @@
{
"description": "Tabs",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1].*"
]
}

View File

@ -1,17 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { DiDemoComponent } from './di_demo';
import { UiTabsComponent, UiPaneDirective } from './ui_tabs';
@NgModule({
imports: [ BrowserModule ],
declarations: [
DiDemoComponent,
UiTabsComponent,
UiPaneDirective
],
bootstrap: [ DiDemoComponent ]
})
export class AppModule { }

View File

@ -1,45 +0,0 @@
// #docregion
import { Component } from '@angular/core';
class Detail {
title: string;
text: string;
}
@Component({
selector: 'di-demo',
template: `
<h4>Tabs Demo</h4>
<ui-tabs>
<template uiPane title='Overview' active="true">
You have {{details.length}} details.
</template>
<template *ngFor="let detail of details" uiPane [title]="detail.title">
{{detail.text}} <br><br>
<button class="btn" (click)="removeDetail(detail)">Remove</button>
</template>
<template uiPane title='Summary'>
Next last ID is {{id}}.
</template>
</ui-tabs>
<hr>
<button class="btn" (click)="addDetail()">Add Detail</button>
`
})
export class DiDemoComponent {
details: Detail[] = [];
id: number = 0;
addDetail() {
this.id++;
this.details.push({
title: `Detail ${this.id}`,
text: `Some detail text for ${this.id}...`
});
}
removeDetail(detail: Detail) {
this.details = this.details.filter((d) => d !== detail);
}
}

View File

@ -1,51 +0,0 @@
// #docregion
import { Component, Directive, Input, QueryList,
ViewContainerRef, TemplateRef, ContentChildren } from '@angular/core';
@Directive({
selector: '[uiPane]'
})
export class UiPaneDirective {
@Input() title: string;
private _active: boolean = false;
constructor(public viewContainer: ViewContainerRef,
public templateRef: TemplateRef<any>) { }
@Input() set active(active: boolean) {
if (active === this._active) { return; }
this._active = active;
if (active) {
this.viewContainer.createEmbeddedView(this.templateRef);
} else {
this.viewContainer.remove(0);
}
}
get active(): boolean {
return this._active;
}
}
@Component({
selector: 'ui-tabs',
template: `
<ul class="nav nav-tabs">
<li *ngFor="let pane of panes"
(click)="select(pane)"
role="presentation" [class.active]="pane.active">
<a>{{pane.title}}</a>
</li>
</ul>
<ng-content></ng-content>
`,
styles: ['a { cursor: pointer; cursor: hand; }']
})
export class UiTabsComponent {
@ContentChildren(UiPaneDirective) panes: QueryList<UiPaneDirective>;
select(pane: UiPaneDirective) {
this.panes.toArray().forEach((p: UiPaneDirective) => p.active = p === pane);
}
}

View File

@ -1,33 +0,0 @@
<!-- #docregion -->
<!DOCTYPE html>
<html>
<head>
<title>Angular Tabs</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<!-- 1. Load libraries -->
<!-- Polyfills -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.8.4"></script>
<script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
<script src="https://unpkg.com/typescript@2.2.1/lib/typescript.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<!-- 3. Display the application -->
<body>
<di-demo class="container" style="display: block;">Loading...</di-demo>
</body>
</html>

View File

@ -1,29 +0,0 @@
<!-- #docregion -->
<!DOCTYPE html>
<html>
<head>
<title>Angular Tabs</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<!-- Display the application -->
<body>
<di-demo class="container" style="display: block">Loading...</di-demo>
</body>
</html>

View File

@ -1,17 +0,0 @@
'use strict'; // necessary for es6 output in node
import { browser, element, by } from 'protractor';
describe('Homepage Todo', function () {
beforeAll(function () {
browser.get('');
});
// Does it even launch?
let expectedAppTitle = 'Todo';
it(`should display app title: ${expectedAppTitle}`, function () {
expect(element(by.css('h2')).getText()).toEqual(expectedAppTitle);
});
});

View File

@ -1,9 +0,0 @@
{
"description": "Todo",
"basePath": "src/",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1].*"
]
}

View File

@ -1,22 +0,0 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { TodoAppComponent } from './todo_app';
import { TodoListComponent } from './todo_list';
import { TodoFormComponent } from './todo_form';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
declarations: [
TodoAppComponent,
TodoListComponent,
TodoFormComponent
],
bootstrap: [ TodoAppComponent ]
})
export class AppModule { }

View File

@ -1,6 +0,0 @@
// #docregion
// Declare an interaface for type safety
export interface Todo {
text: string;
done: boolean;
}

View File

@ -1,38 +0,0 @@
// #docregion
import { Component } from '@angular/core';
import { Todo } from './todo';
@Component({
selector: 'todo-app',
template: `
<h2>Todo</h2>
<span>{{remaining}} of {{todos.length}} remaining</span>
[ <a (click)="archive()">archive</a> ]
<todo-list [todos]="todos"></todo-list>
<todo-form (newTask)="addTask($event)"></todo-form>`,
styles: ['a { cursor: pointer; cursor: hand; }']
})
export class TodoAppComponent {
todos: Todo[] = [
{text: 'learn angular', done: true},
{text: 'build an angular app', done: false}
];
get remaining() {
return this.todos.filter(todo => !todo.done).length;
}
archive(): void {
let oldTodos = this.todos;
this.todos = [];
oldTodos.forEach(todo => {
if (!todo.done) { this.todos.push(todo); }
});
}
addTask(task: Todo) {
this.todos.push(task);
}
}

View File

@ -1,25 +0,0 @@
// #docregion
import { Component, Output, EventEmitter } from '@angular/core';
import { Todo } from './todo';
@Component({
selector: 'todo-form',
template: `
<form (ngSubmit)="addTodo()">
<input type="text" [(ngModel)]="task" size="30"
placeholder="add new todo here">
<input class="btn-primary" type="submit" value="add">
</form>`
})
export class TodoFormComponent {
@Output() newTask = new EventEmitter<Todo>();
task: string = '';
addTodo() {
if (this.task) {
this.newTask.emit({text: this.task, done: false});
}
this.task = '';
}
}

View File

@ -1,24 +0,0 @@
// #docregion
import { Component, Input } from '@angular/core';
import { Todo } from './todo';
@Component({
selector: 'todo-list',
styles: [`
.done-true {
text-decoration: line-through;
color: grey;
}`
],
template: `
<ul class="list-unstyled">
<li *ngFor="let todo of todos">
<input type="checkbox" [(ngModel)]="todo.done">
<span class="done-{{todo.done}}">{{todo.text}}</span>
</li>
</ul>`
})
export class TodoListComponent {
@Input() todos: Todo[];
}

View File

@ -1,33 +0,0 @@
<!-- #docregion -->
<!DOCTYPE html>
<html>
<head>
<title>Angular Todos</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet">
<!-- 1. Load libraries -->
<!-- Polyfills -->
<script src="https://unpkg.com/core-js/client/shim.min.js"></script>
<script src="https://unpkg.com/zone.js@0.8.4"></script>
<script src="https://unpkg.com/systemjs@0.19.39/dist/system.src.js"></script>
<script src="https://unpkg.com/typescript@2.2.1/lib/typescript.js"></script>
<!-- 2. Configure SystemJS -->
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<!-- Display the application -->
<body>
<todo-app class="container" style="display: block">Loading...</todo-app>
</body>
</html>

View File

@ -1,29 +0,0 @@
<!-- #docregion -->
<!DOCTYPE html>
<html>
<head>
<title>Angular Todos</title>
<base href="/">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.js"></script>
<script src="node_modules/systemjs/dist/system.src.js"></script>
<script src="systemjs.config.js"></script>
<script>
System.import('main.js').catch(function(err){ console.error(err); });
</script>
</head>
<!-- Display the application -->
<body>
<todo-app class="container" style="display: block">Loading...</todo-app>
</body>
</html>

View File

@ -1,5 +0,0 @@
// #docregion
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule);

View File

@ -16,8 +16,6 @@ export default {
// should intercept ... but doesn't in some rollup versions
if ( warning.code === 'THIS_IS_UNDEFINED' ) { return; }
// intercepts in some rollup versions
if ( warning.indexOf("The 'this' keyword is equivalent to 'undefined'") > -1 ) { return; }
// console.warn everything else
console.warn( warning.message );

View File

@ -0,0 +1,8 @@
aot/**/*.ts
**/*.ngfactory.ts
**/*.ngsummary.json
**/*.metadata.json
**/*.js
dist
!app/tsconfig.json
!/*.js

View File

@ -0,0 +1,62 @@
{
"name": "toh-universal",
"version": "1.0.0",
"description": "Tour-of-Heroes application with ng-universal server-side rendering",
"scripts": {
"build": "tsc -p src/",
"build:watch": "tsc -w",
"build:aot": "webpack --config webpack.config.aot.js",
"build:uni": "webpack --config webpack.config.uni.js",
"serve": "lite-server -c=bs-config.json",
"serve:aot": "lite-server -c=bs-config.aot.js",
"serve:uni": "node src/dist/server.js",
"serve:uni2": "lite-server -c bs-config.uni.js",
"prestart": "npm run build",
"start": "concurrently \"npm run build:watch\" \"npm run serve\"",
"lint": "tslint ./src/**/*.ts -t verbose",
"ngc": "ngc",
"clean": "rimraf src/dist && rimraf src/app/*.js* && rimraf src/uni/*.js* && rimraf src/main.js*"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"@angular/common": "angular/common-builds",
"@angular/compiler": "angular/compiler-builds",
"@angular/compiler-cli": "angular/compiler-cli-builds",
"@angular/core": "angular/core-builds",
"@angular/forms": "angular/forms-builds",
"@angular/http": "angular/http-builds",
"@angular/platform-browser": "angular/platform-browser-builds",
"@angular/platform-browser-dynamic": "angular/platform-browser-dynamic-builds",
"@angular/platform-server": "angular/platform-server-builds",
"@angular/router": "angular/router-builds",
"angular-in-memory-web-api": "^0.3.1",
"systemjs": "0.19.40",
"core-js": "^2.4.1",
"rxjs": "5.1.1",
"zone.js": "^0.7.7"
},
"devDependencies": {
"concurrently": "^3.2.0",
"lite-server": "^2.2.2",
"typescript": "~2.1.6",
"canonical-path": "0.0.2",
"tslint": "^3.15.1",
"lodash": "^4.16.4",
"rimraf": "^2.5.4",
"@types/node": "^6.0.46",
"@ngtools/webpack": "^1.2.11",
"@types/express": "^4.0.35",
"raw-loader": "^0.5.1",
"webpack": "^2.2.1"
},
"repository": {}
}

View File

@ -0,0 +1,19 @@
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
const routes: Routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent }
];
@NgModule({
imports: [ RouterModule.forRoot(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}

View File

@ -0,0 +1,29 @@
/* #docregion */
h1 {
font-size: 1.2em;
color: #999;
margin-bottom: 0;
}
h2 {
font-size: 2em;
margin-top: 0;
padding-top: 0;
}
nav a {
padding: 5px 10px;
text-decoration: none;
margin-top: 10px;
display: inline-block;
background-color: #eee;
border-radius: 4px;
}
nav a:visited, a:link {
color: #607D8B;
}
nav a:hover {
color: #039be5;
background-color: #CFD8DC;
}
nav a.active {
color: #039be5;
}

View File

@ -0,0 +1,19 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'my-app',
template: `
<h1>{{title}}</h1>
<nav>
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
</nav>
<router-outlet></router-outlet>
`,
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tour of Heroes';
}

View File

@ -0,0 +1,54 @@
// #docplaster
// #docregion
// #docregion v1, v2
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';
// #enddocregion v1
// Imports for loading & configuring the in-memory web api
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryDataService } from './in-memory-data.service';
// #docregion v1
import { AppComponent } from './app.component';
import { DashboardComponent } from './dashboard.component';
import { HeroesComponent } from './heroes.component';
import { HeroDetailComponent } from './hero-detail.component';
import { HeroService } from './hero.service';
// #enddocregion v1, v2
import { HeroSearchComponent } from './hero-search.component';
// #docregion v1, v2
@NgModule({
imports: [
BrowserModule.withServerTransition({
appId: 'toh-universal'
}),
FormsModule,
HttpModule,
// #enddocregion v1
// #docregion in-mem-web-api
InMemoryWebApiModule.forRoot(InMemoryDataService),
// #enddocregion in-mem-web-api
// #docregion v1
AppRoutingModule
],
// #docregion search
declarations: [
AppComponent,
DashboardComponent,
HeroDetailComponent,
HeroesComponent,
// #enddocregion v1, v2
HeroSearchComponent
// #docregion v1, v2
],
// #enddocregion search
providers: [ HeroService ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -0,0 +1,62 @@
/* #docregion */
[class*='col-'] {
float: left;
padding-right: 20px;
padding-bottom: 20px;
}
[class*='col-']:last-of-type {
padding-right: 0;
}
a {
text-decoration: none;
}
*, *:after, *:before {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
h3 {
text-align: center; margin-bottom: 0;
}
h4 {
position: relative;
}
.grid {
margin: 0;
}
.col-1-4 {
width: 25%;
}
.module {
padding: 20px;
text-align: center;
color: #eee;
max-height: 120px;
min-width: 120px;
background-color: #607D8B;
border-radius: 2px;
}
.module:hover {
background-color: #EEE;
cursor: pointer;
color: #607d8b;
}
.grid-pad {
padding: 10px 0;
}
.grid-pad > [class*='col-']:last-of-type {
padding-right: 20px;
}
@media (max-width: 600px) {
.module {
font-size: 10px;
max-height: 75px; }
}
@media (max-width: 1024px) {
.grid {
margin: 0;
}
.module {
min-width: 60px;
}
}

View File

@ -0,0 +1,10 @@
<!-- #docregion -->
<h3>Top Heroes</h3>
<div class="grid grid-pad">
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
<div class="module hero">
<h4>{{hero.name}}</h4>
</div>
</a>
</div>
<hero-search></hero-search>

View File

@ -0,0 +1,22 @@
// #docregion , search
import { Component, OnInit } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'my-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: [ './dashboard.component.css' ]
})
// #enddocregion search
export class DashboardComponent implements OnInit {
heroes: Hero[] = [];
constructor(private heroService: HeroService) { }
ngOnInit(): void {
this.heroService.getHeroes()
.then(heroes => this.heroes = heroes.slice(1, 5));
}
}

View File

@ -0,0 +1,30 @@
/* #docregion */
label {
display: inline-block;
width: 3em;
margin: .5em 0;
color: #607D8B;
font-weight: bold;
}
input {
height: 2em;
font-size: 1em;
padding-left: .4em;
}
button {
margin-top: 20px;
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer; cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
button:disabled {
background-color: #eee;
color: #ccc;
cursor: auto;
}

View File

@ -0,0 +1,14 @@
<!-- #docregion -->
<div *ngIf="hero">
<h2>{{hero.name}} details!</h2>
<div>
<label>id: </label>{{hero.id}}</div>
<div>
<label>name: </label>
<input [(ngModel)]="hero.name" placeholder="name" />
</div>
<button (click)="goBack()">Back</button>
<!-- #docregion save -->
<button (click)="save()">Save</button>
<!-- #enddocregion save -->
</div>

View File

@ -0,0 +1,40 @@
// #docregion
import 'rxjs/add/operator/switchMap';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Params } from '@angular/router';
import { Location } from '@angular/common';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'my-hero-detail',
templateUrl: './hero-detail.component.html',
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
hero: Hero;
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private location: Location
) {}
ngOnInit(): void {
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
.subscribe(hero => this.hero = hero);
}
// #docregion save
save(): void {
this.heroService.update(this.hero)
.then(() => this.goBack());
}
// #enddocregion save
goBack(): void {
this.location.back();
}
}

View File

@ -0,0 +1,21 @@
/* #docregion */
.search-result{
border-bottom: 1px solid gray;
border-left: 1px solid gray;
border-right: 1px solid gray;
width:195px;
height: 16px;
padding: 5px;
background-color: white;
cursor: pointer;
}
.search-result:hover {
color: #eee;
background-color: #607D8B;
}
#search-box{
width: 200px;
height: 20px;
}

View File

@ -0,0 +1,11 @@
<!-- #docregion -->
<div id="search-component">
<h4>Hero Search</h4>
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
<div>
<div *ngFor="let hero of heroes | async"
(click)="gotoDetail(hero)" class="search-result" >
{{hero.name}}
</div>
</div>
</div>

View File

@ -0,0 +1,69 @@
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
// #docregion rxjs-imports
import { Observable } from 'rxjs/Observable';
import { Subject } from 'rxjs/Subject';
// Observable class extensions
import 'rxjs/add/observable/of';
// Observable operators
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/debounceTime';
import 'rxjs/add/operator/distinctUntilChanged';
// #enddocregion rxjs-imports
import { HeroSearchService } from './hero-search.service';
import { Hero } from './hero';
@Component({
selector: 'hero-search',
templateUrl: './hero-search.component.html',
styleUrls: [ './hero-search.component.css' ],
providers: [HeroSearchService]
})
export class HeroSearchComponent implements OnInit {
// #docregion search
heroes: Observable<Hero[]>;
// #enddocregion search
// #docregion searchTerms
private searchTerms = new Subject<string>();
// #enddocregion searchTerms
constructor(
private heroSearchService: HeroSearchService,
private router: Router) {}
// #docregion searchTerms
// Push a search term into the observable stream.
search(term: string): void {
this.searchTerms.next(term);
}
// #enddocregion searchTerms
// #docregion search
ngOnInit(): void {
this.heroes = this.searchTerms
.debounceTime(300) // wait 300ms after each keystroke before considering the term
.distinctUntilChanged() // ignore if next search term is same as previous
.switchMap(term => term // switch to new observable each time the term changes
// return the http search observable
? this.heroSearchService.search(term)
// or the observable of empty heroes if there was no search term
: Observable.of<Hero[]>([]))
.catch(error => {
// TODO: add real error handling
console.log(error);
return Observable.of<Hero[]>([]);
});
}
// #enddocregion search
gotoDetail(hero: Hero): void {
let link = ['/detail', hero.id];
this.router.navigate(link);
}
}

View File

@ -0,0 +1,20 @@
// #docregion
import { Injectable } from '@angular/core';
import { Http } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import { Hero } from './hero';
@Injectable()
export class HeroSearchService {
constructor(private http: Http) {}
search(term: string): Observable<Hero[]> {
return this.http
.get(`app/heroes/?name=${term}`)
.map(response => response.json().data as Hero[]);
}
}

View File

@ -0,0 +1,87 @@
// #docplaster
// #docregion , imports
import { Injectable } from '@angular/core';
import { Headers, Http } from '@angular/http';
// #docregion rxjs
import 'rxjs/add/operator/toPromise';
// #enddocregion rxjs
import { Hero } from './hero';
// #enddocregion imports
@Injectable()
export class HeroService {
// #docregion update
private headers = new Headers({'Content-Type': 'application/json'});
// #enddocregion update
// #docregion getHeroes
private heroesUrl = 'api/heroes'; // URL to web api
constructor(private http: Http) { }
getHeroes(): Promise<Hero[]> {
return this.http.get(this.heroesUrl)
// #docregion to-promise
.toPromise()
// #enddocregion to-promise
// #docregion to-data
.then(response => response.json().data as Hero[])
// #enddocregion to-data
// #docregion catch
.catch(this.handleError);
// #enddocregion catch
}
// #enddocregion getHeroes
// #docregion getHero
getHero(id: number): Promise<Hero> {
const url = `${this.heroesUrl}/${id}`;
return this.http.get(url)
.toPromise()
.then(response => response.json().data as Hero)
.catch(this.handleError);
}
// #enddocregion getHero
// #docregion delete
delete(id: number): Promise<void> {
const url = `${this.heroesUrl}/${id}`;
return this.http.delete(url, {headers: this.headers})
.toPromise()
.then(() => null)
.catch(this.handleError);
}
// #enddocregion delete
// #docregion create
create(name: string): Promise<Hero> {
return this.http
.post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
.toPromise()
.then(res => res.json().data)
.catch(this.handleError);
}
// #enddocregion create
// #docregion update
update(hero: Hero): Promise<Hero> {
const url = `${this.heroesUrl}/${hero.id}`;
return this.http
.put(url, JSON.stringify(hero), {headers: this.headers})
.toPromise()
.then(() => hero)
.catch(this.handleError);
}
// #enddocregion update
// #docregion getHeroes, handleError
private handleError(error: any): Promise<any> {
console.error('An error occurred', error); // for demo purposes only
return Promise.reject(error.message || error);
}
// #enddocregion getHeroes, handleError
}

View File

@ -0,0 +1,4 @@
export class Hero {
id: number;
name: string;
}

View File

@ -0,0 +1,68 @@
/* #docregion */
.selected {
background-color: #CFD8DC !important;
color: white;
}
.heroes {
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;
position: relative;
left: 0;
background-color: #EEE;
margin: .5em;
padding: .3em 0;
height: 1.6em;
border-radius: 4px;
}
.heroes li:hover {
color: #607D8B;
background-color: #DDD;
left: .1em;
}
.heroes li.selected:hover {
background-color: #BBD8DC !important;
color: white;
}
.heroes .text {
position: relative;
top: -3px;
}
.heroes .badge {
display: inline-block;
font-size: small;
color: white;
padding: 0.8em 0.7em 0 0.7em;
background-color: #607D8B;
line-height: 1em;
position: relative;
left: -1px;
top: -4px;
height: 1.8em;
margin-right: .8em;
border-radius: 4px 0 0 4px;
}
button {
font-family: Arial;
background-color: #eee;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
cursor: hand;
}
button:hover {
background-color: #cfd8dc;
}
/* #docregion additions */
button.delete {
float:right;
margin-top: 2px;
margin-right: .8em;
background-color: gray !important;
color:white;
}

View File

@ -0,0 +1,29 @@
<!-- #docregion -->
<h2>My Heroes</h2>
<!-- #docregion add -->
<div>
<label>Hero name:</label> <input #heroName />
<button (click)="add(heroName.value); heroName.value=''">
Add
</button>
</div>
<!-- #enddocregion add -->
<ul class="heroes">
<!-- #docregion li-element -->
<li *ngFor="let hero of heroes" (click)="onSelect(hero)"
[class.selected]="hero === selectedHero">
<span class="badge">{{hero.id}}</span>
<span>{{hero.name}}</span>
<!-- #docregion delete -->
<button class="delete"
(click)="delete(hero); $event.stopPropagation()">x</button>
<!-- #enddocregion delete -->
</li>
<!-- #enddocregion li-element -->
</ul>
<div *ngIf="selectedHero">
<h2>
{{selectedHero.name | uppercase}} is my hero
</h2>
<button (click)="gotoDetail()">View Details</button>
</div>

View File

@ -0,0 +1,61 @@
// #docregion
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'my-heroes',
templateUrl: './heroes.component.html',
styleUrls: [ './heroes.component.css' ]
})
export class HeroesComponent implements OnInit {
heroes: Hero[];
selectedHero: Hero;
constructor(
private heroService: HeroService,
private router: Router) { }
getHeroes(): void {
this.heroService
.getHeroes()
.then(heroes => this.heroes = heroes);
}
// #docregion add
add(name: string): void {
name = name.trim();
if (!name) { return; }
this.heroService.create(name)
.then(hero => {
this.heroes.push(hero);
this.selectedHero = null;
});
}
// #enddocregion add
// #docregion delete
delete(hero: Hero): void {
this.heroService
.delete(hero.id)
.then(() => {
this.heroes = this.heroes.filter(h => h !== hero);
if (this.selectedHero === hero) { this.selectedHero = null; }
});
}
// #enddocregion delete
ngOnInit(): void {
this.getHeroes();
}
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
gotoDetail(): void {
this.router.navigate(['/detail', this.selectedHero.id]);
}
}

View File

@ -0,0 +1,19 @@
// #docregion , init
import { InMemoryDbService } from 'angular-in-memory-web-api';
export class InMemoryDataService implements InMemoryDbService {
createDb() {
let heroes = [
{id: 11, name: 'Mr. Nice'},
{id: 12, name: 'Narco'},
{id: 13, name: 'Bombasto'},
{id: 14, name: 'Celeritas'},
{id: 15, name: 'Magneta'},
{id: 16, name: 'RubberMan'},
{id: 17, name: 'Dynama'},
{id: 18, name: 'Dr IQ'},
{id: 19, name: 'Magma'},
{id: 20, name: 'Tornado'}
];
return {heroes};
}
}

View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<base href="/">
<title>Angular Tour of Heroes</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
</head>
<body>
<my-app>Loading...</my-app>
</body>
<script src="node_modules/core-js/client/shim.min.js"></script>
<script src="node_modules/zone.js/dist/zone.min.js"></script>
<script src="dist/build.js"></script>
</html>

View File

@ -2,10 +2,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Angular Hello World</title>
<base href="/">
<meta charset="UTF-8">
<title>Angular Universal Tour of Heroes</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- Polyfills -->
@ -20,9 +20,7 @@
</script>
</head>
<!-- Display the application -->
<body>
<hello-world class="container" style="display: block">Loading...</hello-world>
<my-app>Loading...</my-app>
</body>
</html>

View File

@ -0,0 +1,5 @@
// #docregion
import { platformBrowser } from '@angular/platform-browser';
import { AppModuleNgFactory } from '../aot/src/app/app.module.ngfactory';
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);

View File

@ -0,0 +1,21 @@
import { NgModule } from '@angular/core';
import { APP_BASE_HREF } from '@angular/common';
import { ServerModule } from '@angular/platform-server';
import { AppComponent } from '../app/app.component';
import { AppModule } from '../app/app.module';
@NgModule({
imports: [
ServerModule,
AppModule
],
bootstrap: [
AppComponent
],
providers: [
{provide: APP_BASE_HREF, useValue: '/'}
// { provide: NgModuleFactoryLoader, useClass: ServerRouterLoader }
]
})
export class AppServerModule {
}

View File

@ -0,0 +1,40 @@
import 'zone.js/dist/zone-node';
import { enableProdMode } from '@angular/core';
// import { AppServerModule } from './app.server';
import { AppServerModuleNgFactory } from '../../aot/src/uni/app.server.ngfactory';
import * as express from 'express';
import { ngUniversalEngine } from './universal-engine';
enableProdMode();
const server = express();
// set our angular engine as the handler for html files, so it will be used to render them.
server.engine('html', ngUniversalEngine({
bootstrap: [AppServerModuleNgFactory]
}));
// set default view directory
server.set('views', 'src');
// handle requests for routes in the app. ngExpressEngine does the rendering.
server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req, res) => {
res.render('index-aot.html', {req});
});
// handle requests for static files
server.get(['/*.js', '/*.css'], (req, res, next) => {
let fileName: string = req.originalUrl;
console.log(fileName);
let root = fileName.startsWith('/node_modules/') ? '.' : 'src';
res.sendFile(fileName, { root: root }, function (err) {
if (err) {
next(err);
}
});
});
// start the server
server.listen(3200, () => {
console.log('listening on port 3200...');
});

View File

@ -0,0 +1,38 @@
/**
* Express/Connect middleware for rendering pages using Angular Universal
*/
import * as fs from 'fs';
import { renderModuleFactory } from '@angular/platform-server';
const templateCache = {}; // cache for page templates
const outputCache = {}; // cache for rendered pages
export function ngUniversalEngine(setupOptions: any) {
return function (filePath: string, options: { req: Request }, callback: (err: Error, html: string) => void) {
let url: string = options.req.url;
let html: string = outputCache[url];
if (html) {
// return already-built page for this url
console.log('from cache: ' + url);
callback(null, html);
return;
}
console.log('building: ' + url);
if (!templateCache[filePath]) {
let file = fs.readFileSync(filePath);
templateCache[filePath] = file.toString();
}
// render the page via angular platform-server
let appModuleFactory = setupOptions.bootstrap[0];
renderModuleFactory(appModuleFactory, {
document: templateCache[filePath],
url: url
}).then(str => {
outputCache[url] = str;
callback(null, str);
});
};
}

View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": ["es2015", "dom"],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"node_modules/@types/"
]
},
"files": [
"src/app/app.module.ts",
"src/main-aot.ts"
],
"angularCompilerOptions": {
"genDir": "aot",
"entryModule": "./src/app/app.module#AppModule",
"skipMetadataEmit" : true
}
}

View File

@ -0,0 +1,27 @@
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": ["es2015", "dom"],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"../../node_modules/@types/"
]
},
"files": [
"src/uni/app.server.ts",
"src/uni/server-aot.ts"
],
"angularCompilerOptions": {
"genDir": "aot",
"entryModule": "./src/app/app.module#AppModule",
"skipMetadataEmit" : true
}
}

View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"lib": [ "es2015", "dom" ],
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"typeRoots": [
"../../../node_modules/@types/"
]
},
"compileOnSave": true,
"exclude": [
"node_modules/*"
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,8 @@ Animations
A guide to Angular's animation system.
@description
Motion is an important aspect in the design of modern web applications. Good
user interfaces transition smoothly between states with engaging animations
that call attention where it's needed. Well-designed animations can make a UI not only
@ -17,6 +19,8 @@ animation logic with the rest of your application code, for ease of control.
~~~ {.alert.is-helpful}
Angular animations are built on top of the standard [Web Animations API](https://w3c.github.io/web-animations/)
and run natively on [browsers that support it](http://caniuse.com/#feat=web-animation).
@ -28,6 +32,8 @@ add it to your page.
~~~
# Contents
* [Example: Transitioning between two states](guide/animations#example-transitioning-between-states).
@ -44,6 +50,8 @@ add it to your page.
~~~ {.l-sub-section}
The examples in this page are available as a <live-example></live-example>.
@ -53,12 +61,16 @@ The examples in this page are available as a <live-example></live-example>.
{@a example-transitioning-between-states}
## Quickstart example: Transitioning between two states
<figure>
<img src="assets/images/devguide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px"> </img>
<img src="assets/images/devguide/animations/animation_basic_click.gif" alt="A simple transition animation" align="right" style="width:220px;margin-left:20px"></img>
</figure>
You can build a simple animation that transitions an element between two states
driven by a model attribute.
@ -66,22 +78,24 @@ Animations are defined inside `@Component` metadata. Before you can add animatio
to import a few animation-specific imports and functions:
<code-example path="animations/src/app/app.module.ts" region="animations-module" linenums="false">
<code-example path="animations/src/app/app.module.ts" region="animations-module" title="app.module.ts (@NgModule imports excerpt)" linenums="false">
</code-example>
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" linenums="false">
<code-example path="animations/src/app/hero-list-basic.component.ts" region="imports" title="hero-list-basic.component.ts" linenums="false">
</code-example>
With these, you can define an *animation trigger* called `heroState` in the component
metadata. It uses animations to transition between two states: `active` and `inactive`. When a
hero is active, the element appears in a slightly larger size and lighter color.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-basic.component.ts" region="animationdef" title="hero-list-basic.component.ts (@Component excerpt)" linenums="false">
</code-example>
@ -89,20 +103,26 @@ hero is active, the element appears in a slightly larger size and lighter color.
~~~ {.alert.is-helpful}
In this example, you are defining animation styles (color and transform) inline in the
animation metadata.
~~~
Now, using the `[@triggerName]` syntax, attach the animation that you just defined to
one or more elements in the component's template.
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" linenums="false">
<code-example path="animations/src/app/hero-list-basic.component.ts" region="template" title="hero-list-basic.component.ts (excerpt)" linenums="false">
</code-example>
Here, the animation trigger applies to every element repeated by an `ngFor`. Each of
the repeated elements animates independently. The value of the
attribute is bound to the expression `hero.state` and is always either `active` or `inactive`.
@ -111,10 +131,12 @@ With this setup, an animated transition appears whenever a hero object changes s
Here's the full component implementation:
<code-example path="animations/src/app/hero-list-basic.component.ts">
<code-example path="animations/src/app/hero-list-basic.component.ts" title="hero-list-basic.component.ts">
</code-example>
## States and transitions
Angular animations are defined as logical **states** and **transitions**
@ -129,10 +151,12 @@ component's template.
You can define *styles* for each animation state:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="states" linenums="false">
<code-example path="animations/src/app/hero-list-basic.component.ts" region="states" title="src/app/hero-list-basic.component.ts" linenums="false">
</code-example>
These `state` definitions specify the *end styles* of each state.
They are applied to the element once it has transitioned to that state, and stay
*as long as it remains in that state*. In effect, you're defining what styles the element has in different states.
@ -141,32 +165,38 @@ After you define states, you can define *transitions* between the states. Each t
controls the timing of switching between one set of styles and the next:
<code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" linenums="false">
<code-example path="animations/src/app/hero-list-basic.component.ts" region="transitions" title="src/app/hero-list-basic.component.ts" linenums="false">
</code-example>
<figure class='image-display'>
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations you define states and transitions between states" width="400"> </img>
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active.png" alt="In Angular animations you define states and transitions between states" width="400"></img>
</figure>
If several transitions have the same timing configuration, you can combine
them into the same `transition` definition:
<code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" linenums="false">
<code-example path="animations/src/app/hero-list-combined-transitions.component.ts" region="transitions" title="src/app/hero-list-combined-transitions.component.ts" linenums="false">
</code-example>
When both directions of a transition have the same timing, as in the previous
example, you can use the shorthand syntax `<=>`:
<code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" linenums="false">
<code-example path="animations/src/app/hero-list-twoway.component.ts" region="transitions" title="src/app/hero-list-twoway.component.ts" linenums="false">
</code-example>
You can also apply a style during an animation but not keep it around
after the animation finishes. You can define such styles inline, in the `transition`. In this example,
the element receives one set of styles immediately and is then animated to the next.
@ -174,10 +204,12 @@ When the transition finishes, none of these styles are kept because they're not
defined in a `state`.
<code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" linenums="false">
<code-example path="animations/src/app/hero-list-inline-styles.component.ts" region="transitions" title="src/app/hero-list-inline-styles.component.ts" linenums="false">
</code-example>
### The wildcard state `*`
The `*` ("wildcard") state matches *any* animation state. This is useful for defining styles and
@ -188,9 +220,11 @@ transitions that apply regardless of which state the animation is in. For exampl
<figure class='image-display'>
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400"> </img>
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active_wildcards.png" alt="The wildcard state can be used to match many different transitions at once" width="400"></img>
</figure>
### The `void` state
The special state called `void` can apply to any animation. It applies
@ -203,17 +237,21 @@ regardless of what state it was in before it left.
<figure class='image-display'>
<img src="assets/images/devguide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400"> </img>
<img src="assets/images/devguide/animations/ng_animate_transitions_void_in.png" alt="The void state can be used for enter and leave transitions" width="400"></img>
</figure>
The wildcard state `*` also matches `void`.
## Example: Entering and leaving
<figure>
<img src="assets/images/devguide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"> </img>
<img src="assets/images/devguide/animations/animation_enter_leave.gif" alt="Enter and leave animations" align="right" style="width:250px;"></img>
</figure>
Using the `void` and `*` states you can define transitions that animate the
entering and leaving of elements:
@ -223,10 +261,12 @@ entering and leaving of elements:
For example, in the `animations` array below there are two transitions that use
the `void => *` and `* => void` syntax to animate the element in and out of the view.
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-enter-leave.component.ts" region="animationdef" title="hero-list-enter-leave.component.ts (excerpt)" linenums="false">
</code-example>
Note that in this case the styles are applied to the void state directly in the
transition definitions, and not in a separate `state(void)` definition. Thus, the transforms
are different on enter and leave: the element enters from the left
@ -235,11 +275,13 @@ and leaves to the right.
~~~ {.l-sub-section}
These two common animations have their own aliases:
<code-example language="typescript">
transition(':enter', [ ... ]); // void => *
transition(':leave', [ ... ]); // * => void
transition(':leave', [ ... ]); // * => void
</code-example>
@ -247,12 +289,16 @@ These two common animations have their own aliases:
~~~
## Example: Entering and leaving from different states
<figure>
<img src="assets/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"> </img>
<img src="assets/images/devguide/animations/animation_enter_leave_states.gif" alt="Enter and leave animations combined with state animations" align="right" style="width:200px"></img>
</figure>
You can also combine this animation with the earlier state transition animation by
using the hero state as the animation state. This lets you configure
different transitions for entering and leaving based on what the state of the hero
@ -267,15 +313,17 @@ This gives you fine-grained control over each transition:
<figure class='image-display'>
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400"> </img>
<img src="assets/images/devguide/animations/ng_animate_transitions_inactive_active_void.png" alt="This example transitions between active, inactive, and void states" width="400"></img>
</figure>
<code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-enter-leave-states.component.ts" region="animationdef" title="hero-list-enter-leave.component.ts (excerpt)" linenums="false">
</code-example>
## Animatable properties and units
Since Angular's animation support builds on top of Web Animations, you can animate any property
@ -298,9 +346,11 @@ If you don't provide a unit when specifying dimension, Angular assumes the defau
## Automatic property calculation
<figure>
<img src="assets/images/devguide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px"> </img>
<img src="assets/images/devguide/animations/animation_auto.gif" alt="Animation with automated height calculation" align="right" style="width:220px;margin-left:20px"></img>
</figure>
Sometimes you don't know the value of a dimensional style property until runtime.
For example, elements often have widths and heights that
depend on their content and the screen size. These properties are often tricky
@ -313,10 +363,12 @@ In this example, the leave animation takes whatever height the element has befor
leaves and animates from that height to zero:
<code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-auto.component.ts" region="animationdef" title="src/app/hero-list-auto.component.ts" linenums="false">
</code-example>
## Animation timing
There are three timing properties you can tune for every animated transition:
@ -353,9 +405,11 @@ and the delay (or as the *second* value when there is no delay):
<figure>
<img src="assets/images/devguide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"> </img>
<img src="assets/images/devguide/animations/animation_timings.gif" alt="Animations with specific timings" align="right" style="width:220px;margin-left:20px"></img>
</figure>
### Example
Here are a couple of custom timings in action. Both enter and leave last for
@ -364,16 +418,20 @@ slight delay of 10 milliseconds as specified in `'0.2s 10 ease-out'`:
<code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-timings.component.ts" region="animationdef" title="hero-list-timings.component.ts (excerpt)" linenums="false">
</code-example>
## Multi-step animations with keyframes
<figure>
<img src="assets/images/devguide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px"> </img>
<img src="assets/images/devguide/animations/animation_multistep.gif" alt="Animations with some bounce implemented with keyframes" align="right" style="width:220px;margin-left:20px"></img>
</figure>
Animation *keyframes* go beyond a simple transition to a more intricate animation
that goes through one or more intermediate styles when transitioning between two sets of styles.
@ -385,10 +443,12 @@ This example adds some "bounce" to the enter and leave animations with
keyframes:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="animationdef" title="hero-list-multistep.component.ts (excerpt)" linenums="false">
</code-example>
Note that the offsets are *not* defined in terms of absolute time. They are relative
measures from zero to one. The final timeline of the animation is based on the combination
of keyframe offsets, duration, delay, and easing.
@ -396,12 +456,16 @@ of keyframe offsets, duration, delay, and easing.
Defining offsets for keyframes is optional. If you omit them, offsets with even
spacing are automatically assigned. For example, three keyframes without predefined
offsets receive offsets `0`, `0.5`, and `1`.
## Parallel animation groups
<figure>
<img src="assets/images/devguide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px"> </img>
<img src="assets/images/devguide/animations/animation_groups.gif" alt="Parallel animations with different timings, implemented with groups" align="right" style="width:220px;margin-left:20px"></img>
</figure>
You've seen how to animate multiple style properties at the same time:
just put all of them into the same `style()` definition.
@ -414,11 +478,15 @@ enter and leave allows for two different timing configurations. Both
are applied to the same element in parallel, but run independently of each other:
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" linenums="false">
<code-example path="animations/src/app/hero-list-groups.component.ts" region="animationdef" title="hero-list-groups.component.ts (excerpt)" linenums="false">
</code-example>
One group animates the element transform and width; the other group animates the opacity.
## Animation callbacks
A callback is fired when an animation is started and also when it is done.
@ -427,10 +495,12 @@ In the keyframes example, you have a `trigger` called `@flyInOut`. You can hook
those callbacks like this:
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" linenums="false">
<code-example path="animations/src/app/hero-list-multistep.component.ts" region="template" title="hero-list-multistep.component.ts (excerpt)" linenums="false">
</code-example>
The callbacks receive an `AnimationEvent` that contains contains useful properties such as
`fromState`, `toState` and `totalTime`.

View File

@ -5,37 +5,48 @@ Ahead-of-Time Compilation
Learn how to use ahead-of-time compilation.
@description
This cookbook describes how to radically improve performance by compiling _ahead-of-time_ (AOT)
during a build process.
{@a toc}
# Contents
- [Overview](guide/overview)
- [Ahead-of-time (AOT) vs just-in-time (JIT)](guide/aot-compiler#aot-jit)
- [Why do AOT compilation?](guide/aot-compiler#why-aot)
- [Compile with AOT](guide/aot-compiler#compile)
- [Bootstrap](guide/aot-compiler#bootstrap)
- [Tree shaking](guide/aot-compiler#tree-shaking)
- [Rollup](guide/aot-compiler#rollup)
- [Rollup Plugins](guide/aot-compiler#rollup-plugins)
- [Run Rollup](guide/aot-compiler#run-rollup)
- [Load the bundle](guide/aot-compiler#load)
- [Serve the app](guide/aot-compiler#serve)
- [AOT QuickStart source code](guide/aot-compiler#source-code)
- [Workflow and convenience script](guide/aot-compiler#workflow)
- [Develop JIT along with AOT](guide/aot-compiler#run-jit)
- [Tour of Heroes](guide/aot-compiler#toh)
- [JIT in development, AOT in production](guide/aot-compiler#jit-dev-aot-prod)
- [Tree shaking](guide/aot-compiler#shaking)
- [Running the application](guide/aot-compiler#running-app)
- [Inspect the Bundle](guide/aot-compiler#inspect-bundle)
* [Overview](guide/overview)
* [Ahead-of-time (AOT) vs just-in-time (JIT)](guide/aot-compiler#aot-jit)
* [Why do AOT compilation?](guide/aot-compiler#why-aot)
* [Compile with AOT](guide/aot-compiler#compile)
* [Bootstrap](guide/aot-compiler#bootstrap)
* [Tree shaking](guide/aot-compiler#tree-shaking)
*[Rollup](guide/aot-compiler#rollup)
*[Rollup Plugins](guide/aot-compiler#rollup-plugins)
*[Run Rollup](guide/aot-compiler#run-rollup)
* [Load the bundle](guide/aot-compiler#load)
* [Serve the app](guide/aot-compiler#serve)
* [AOT QuickStart source code](guide/aot-compiler#source-code)
* [Workflow and convenience script](guide/aot-compiler#workflow)
*[Develop JIT along with AOT](guide/aot-compiler#run-jit)
* [Tour of Heroes](guide/aot-compiler#toh)
*[JIT in development, AOT in production](guide/aot-compiler#jit-dev-aot-prod)
*[Tree shaking](guide/aot-compiler#shaking)
*[Running the application](guide/aot-compiler#running-app)
*[Inspect the Bundle](guide/aot-compiler#inspect-bundle)
{@a overview}
## Overview
An Angular application consists largely of components and their HTML templates.
@ -44,10 +55,14 @@ the components and templates must be converted to executable JavaScript by the _
~~~ {.l-sub-section}
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo" target="_blank">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016.
~~~
You can compile the app in the browser, at runtime, as the application loads, using the **_just-in-time_ (JIT) compiler**.
This is the standard development approach shown throughout the documentation.
It's great but it has shortcomings.
@ -68,6 +83,8 @@ by compiling at build time.
{@a aot-jit}
## _Ahead-of-time_ (AOT) vs _just-in-time_ (JIT)
There is actually only one Angular compiler. The difference between AOT and JIT is a matter of timing and tooling.
@ -75,6 +92,8 @@ With AOT, the compiler runs once at build time using one set of libraries;
with JIT it runs every time for every user at runtime using a different set of libraries.
{@a why-aot}
## Why do AOT compilation?
*Faster rendering*
@ -108,6 +127,8 @@ there are fewer opportunities for injection attacks.
{@a compile}
## Compile with AOT
Preparing for offline compilation takes a few simple steps.
@ -127,12 +148,16 @@ A few minor changes to the lone `app.component` lead to these two class and HTML
</code-tabs>
Install a few new npm dependencies with the following command:
<code-example language="none" class="code-shell">
npm install @angular/compiler-cli @angular/platform-server --save
</code-example>
You will run the `ngc` compiler provided in the `@angular/compiler-cli` npm package
instead of the TypeScript compiler (`tsc`).
@ -143,10 +168,12 @@ Copy the original `src/tsconfig.json` to a file called `tsconfig-aot.json` on th
then modify it as follows.
<code-example path="cb-aot-compiler/tsconfig-aot.json" linenums="false">
<code-example path="cb-aot-compiler/tsconfig-aot.json" title="tsconfig-aot.json" linenums="false">
</code-example>
The `compilerOptions` section is unchanged except for one property.
**Set the `module` to `es2015`**.
This is important as explained later in the [Tree Shaking](guide/aot-compiler#tree-shaking) section.
@ -157,6 +184,8 @@ to store the compiled output files in a new `aot` folder.
The `"skipMetadataEmit" : true` property prevents the compiler from generating metadata files with the compiled application.
Metadata files are not necessary when targeting TypeScript files, so there is no reason to include them.
***Component-relative template URLS***
The AOT compiler requires that `@Component` URLS for external templates and CSS files be _component-relative_.
@ -164,6 +193,8 @@ That means that the value of `@Component.templateUrl` is a URL value _relative_
For example, an `'app.component.html'` URL means that the template file is a sibling of its companion `app.component.ts` file.
While JIT app URLs are more flexible, stick with _component-relative_ URLs for compatibility with AOT compilation.
***Compiling the application***
Initiate AOT compilation from the command line using the previously installed `ngc` compiler by executing:
@ -176,6 +207,8 @@ Initiate AOT compilation from the command line using the previously installed `n
~~~ {.l-sub-section}
Windows users should surround the `ngc` command in double quotes:
<code-example format='.'>
@ -186,6 +219,8 @@ Windows users should surround the `ngc` command in double quotes:
~~~
`ngc` expects the `-p` switch to point to a `tsconfig.json` file or a folder containing a `tsconfig.json` file.
After `ngc` completes, look for a collection of _NgFactory_ files in the `aot` folder.
@ -198,6 +233,8 @@ Note that the original component class is still referenced internally by the gen
~~~ {.l-sub-section}
The curious can open `aot/app.component.ngfactory.ts` to see the original Angular template syntax
compiled to TypeScript, its intermediate form.
@ -209,8 +246,12 @@ AOT compilation reveals them as separate, physical files.
~~~ {.alert.is-important}
Do not edit the _NgFactories_! Re-compilation replaces these files and all edits will be lost.
@ -220,6 +261,8 @@ Do not edit the _NgFactories_! Re-compilation replaces these files and all edits
{@a bootstrap}
## Bootstrap
The AOT approach changes application bootstrapping.
@ -248,10 +291,14 @@ Here is AOT bootstrap in `main.ts` next to the original JIT version:
</code-tabs>
Be sure to [recompile](guide/aot-compiler#compiling-aot) with `ngc`!
{@a tree-shaking}
## Tree shaking
AOT compilation sets the stage for further optimization through a process called _tree shaking_.
@ -272,6 +319,8 @@ which in turn makes more of the application "tree shakable".
{@a rollup}
### Rollup
This cookbook illustrates a tree shaking utility called _Rollup_.
@ -283,27 +332,35 @@ Rollup can only tree shake `ES2015` modules which have `import` and `export` sta
~~~ {.l-sub-section}
Recall that `tsconfig-aot.json` is configured to produce `ES2015` modules.
It's not important that the code itself be written with `ES2015` syntax such as `class` and `const`.
What matters is that the code uses ES `import` and `export` statements rather than `require` statements.
~~~
In the terminal window, install the Rollup dependencies with this command:
<code-example language="none" class="code-shell">
npm install rollup rollup-plugin-node-resolve rollup-plugin-commonjs rollup-plugin-uglify --save-dev
</code-example>
Next, create a configuration file (`rollup-config.js`)
in the project root directory to tell Rollup how to process the application.
The cookbook configuration file looks like this.
<code-example path="cb-aot-compiler/rollup-config.js" linenums="false">
<code-example path="cb-aot-compiler/rollup-config.js" title="rollup-config.js" linenums="false">
</code-example>
This config file tells Rollup that the app entry point is `src/app/main.js` .
The `dest` attribute tells Rollup to create a bundle called `build.js` in the `dist` folder.
It overrides the default `onwarn` method in order to skip annoying messages about the AOT compiler's use of the `this` keyword.
@ -312,6 +369,8 @@ The next section covers the plugins in more depth.
{@a rollup-plugins}
### Rollup Plugins
Optional plugins filter and transform the Rollup inputs and output.
@ -331,10 +390,12 @@ in the final bundle. Using it is straigthforward. Add the following to
the `plugins` array in `rollup-config.js`:
<code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" linenums="false">
<code-example path="cb-aot-compiler/rollup-config.js" region="commonjs" title="rollup-config.js (CommonJs to ES2015 Plugin)" linenums="false">
</code-example>
*Minification*
Rollup tree shaking reduces code size considerably. Minification makes it smaller still.
@ -342,7 +403,7 @@ This cookbook relies on the _uglify_ Rollup plugin to minify and mangle the code
Add the following to the `plugins` array:
<code-example path="cb-aot-compiler/rollup-config.js" region="uglify" linenums="false">
<code-example path="cb-aot-compiler/rollup-config.js" region="uglify" title="rollup-config.js (CommonJs to ES2015 Plugin)" linenums="false">
</code-example>
@ -350,6 +411,8 @@ Add the following to the `plugins` array:
~~~ {.l-sub-section}
In a production setting, you would also enable gzip on the web server to compress
the code into an even smaller package going over the wire.
@ -359,6 +422,8 @@ the code into an even smaller package going over the wire.
{@a run-rollup}
### Run Rollup
Execute the Rollup process with this command:
@ -370,6 +435,8 @@ Execute the Rollup process with this command:
~~~ {.l-sub-section}
Windows users should surround the `rollup` command in double quotes:
<code-example language="none" class="code-shell">
@ -382,8 +449,12 @@ Windows users should surround the `rollup` command in double quotes:
{@a load}
## Load the bundle
Loading the generated application bundle does not require a module loader like SystemJS.
@ -391,7 +462,7 @@ Remove the scripts that concern SystemJS.
Instead, load the bundle file using a single `<script>` tag **_after_** the `</body>` tag:
<code-example path="cb-aot-compiler/src/index.html" region="bundle" linenums="false">
<code-example path="cb-aot-compiler/src/index.html" region="bundle" title="index.html (load bundle)" linenums="false">
</code-example>
@ -399,6 +470,8 @@ Instead, load the bundle file using a single `<script>` tag **_after_** the `</b
{@a serve}
## Serve the app
You'll need a web server to host the application.
@ -408,11 +481,15 @@ Use the same `lite-server` employed elsewhere in the documentation:
npm run lite
</code-example>
The server starts, launches a browser, and the app should appear.
{@a source-code}
## AOT QuickStart source code
Here's the pertinent source code:
@ -449,12 +526,16 @@ Here's the pertinent source code:
{@a workflow}
## Workflow and convenience script
You'll rebuild the AOT version of the application every time you make a change.
Those _npm_ commands are long and difficult to remember.
Add the following _npm_ convenience script to the `package.json` so you can compile and rollup in one command.Open a terminal window and try it.
Add the following _npm_ convenience script to the `package.json` so you can compile and rollup in one command.
Open a terminal window and try it.
<code-example language="none" class="code-shell">
npm run build:aot
@ -464,6 +545,8 @@ Add the following _npm_ convenience script to the `package.json` so you can comp
{@a run-jit}
### Develop JIT along with AOT
AOT compilation and rollup together take several seconds.
@ -474,18 +557,24 @@ The same source code can be built both ways. Here's one way to do that.
* Delete the script at the bottom of `index-jit.html` that loads `bundle.js`
* Restore the SystemJS scripts like this:
<code-example path="cb-aot-compiler/src/index-jit.html" region="jit" linenums="false">
<code-example path="cb-aot-compiler/src/index-jit.html" region="jit" title="src/index-jit.html (SystemJS scripts)" linenums="false">
</code-example>
Notice the slight change to the `system.import` which now specifies `src/app/main-jit`.
That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap).
Open a _different_ terminal window and enter `npm start`.
<code-example language="none" class="code-shell">
npm start
</code-example>
That compiles the app with JIT and launches the server.
The server loads `index.html` which is still the AOT version, which you can confirm in the browser console.
Change the address bar to `index-jit.html` and it loads the JIT version.
@ -504,6 +593,8 @@ Now you can develop JIT and AOT, side-by-side.
{@a toh}
## Tour of Heroes
The sample above is a trivial variation of the QuickStart application.
@ -512,6 +603,8 @@ to an app with more substance, the [_Tour of Heroes_](tutorial/toh-pt6) applicat
{@a jit-dev-aot-prod}
### JIT in development, AOT in production
Today AOT compilation and tree shaking take more time than is practical for development. That will change soon.
@ -538,6 +631,8 @@ Here they are for comparison:
</code-tabs>
The JIT version relies on `SystemJS` to load individual modules.
Its scripts appear in its `index.html`.
@ -563,6 +658,8 @@ are evident in these `main` files which can and should reside in the same folder
</code-tabs>
***TypeScript configuration***
JIT-compiled applications transpile to `commonjs` modules.
@ -594,6 +691,8 @@ You'll need separate TypeScript configuration files such as these:
@Types and node modules
</header>
In the file structure of _this particular sample project_,
the `node_modules` folder happens to be two levels up from the project root.
Therefore, `"typeRoots"` must be set to `"../../node_modules/@types/"`.
@ -607,23 +706,29 @@ Edit your `tsconfig-aot.json` to fit your project's file structure.
{@a shaking}
### Tree shaking
Rollup does the tree shaking as before.
<code-example path="toh-6/rollup-config.js" linenums="false">
<code-example path="toh-6/rollup-config.js" title="rollup-config.js" linenums="false">
</code-example>
{@a running-app}
### Running the application
~~~ {.alert.is-important}
The general audience instructions for running the AOT build of the Tour of Heroes app are not ready.
The following instructions presuppose that you have cloned the
@ -634,6 +739,8 @@ The _Tour of Heroes_ source code is in the `public/docs/_examples/toh-6/ts` fold
~~~
Run the JIT-compiled app with `npm start` as for all other JIT examples.
Compiling with AOT presupposes certain supporting files, most of them discussed above.
@ -658,7 +765,11 @@ Compiling with AOT presupposes certain supporting files, most of them discussed
</code-tabs>
Extend the `scripts` section of the `package.json` with these npm scripts:Copy the AOT distribution files into the `/aot` folder with the node script:
Extend the `scripts` section of the `package.json` with these npm scripts:
Copy the AOT distribution files into the `/aot` folder with the node script:
<code-example language="none" class="code-shell">
node copy-dist-files
@ -668,10 +779,14 @@ Extend the `scripts` section of the `package.json` with these npm scripts:Copy t
~~~ {.l-sub-section}
You won't do that again until there are updates to `zone.js` or the `core-js` shim for old browsers.
~~~
Now AOT-compile the app and launch it with the `lite-server`:
<code-example language="none" class="code-shell">
@ -682,6 +797,8 @@ Now AOT-compile the app and launch it with the `lite-server`:
{@a inspect-bundle}
### Inspect the Bundle
It's fascinating to see what the generated JavaScript bundle looks like after Rollup.
@ -695,6 +812,8 @@ Install it:
npm install source-map-explorer --save-dev
</code-example>
Run the following command to generate the map.
@ -703,12 +822,14 @@ Run the following command to generate the map.
</code-example>
The `source-map-explorer` analyzes the source map generated with the bundle and draws a map of all dependencies,
showing exactly which application and Angular modules and classes are included in the bundle.
Here's the map for _Tour of Heroes_.
<a href="assets/images/cookbooks/aot-compiler/toh6-bundle.png" target="_blank" title="View larger image">
<figure class='image-display'>
<img src="assets/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"> </img>
<img src="assets/images/cookbooks/aot-compiler/toh6-bundle.png" alt="TOH-6-bundle"></img>
</figure>
</a>

View File

@ -5,6 +5,8 @@ AppModule: the root module
Tell Angular how to construct and bootstrap the app in the root "AppModule".
@description
An Angular module class describes how the application parts fit together.
Every application has at least one Angular module, the _root_ module
that you [bootstrap](guide/appmodule#main) to launch the application.
@ -14,12 +16,14 @@ The [setup](guide/setup) instructions produce a new project with the following m
You'll evolve this module as your application grows.
<code-example path="setup/src/app/app.module.ts" linenums="false">
<code-example path="setup/src/app/app.module.ts" title="src/app/app.module.ts" linenums="false">
</code-example>
After the `import` statements, you come to a class adorned with the
**`@NgModule`** [_decorator_](guide/glossary).
**`@NgModule`** [_decorator_](guide/glossary#decorator '"Decorator" explained').
The `@NgModule` decorator identifies `AppModule` as an Angular module class (also called an `NgModule` class).
`@NgModule` takes a _metadata_ object that tells Angular how to compile and launch the application.
@ -33,6 +37,8 @@ All you need to know at the moment is a few basics about these three properties.
{@a imports}
### The _imports_ array
Angular modules are a way to consolidate features that belong together into discrete units.
@ -50,6 +56,8 @@ Other guide and cookbook pages will tell you when you need to add additional mod
~~~ {.alert.is-important}
**Only `NgModule` classes** go in the `imports` array. Do not put any other kind of class in `imports`.
@ -59,6 +67,8 @@ Other guide and cookbook pages will tell you when you need to add additional mod
~~~ {.l-sub-section}
The `import` statements at the top of the file and the Angular module's `imports` array
are unrelated and have completely different jobs.
@ -76,6 +86,8 @@ that the application needs to function properly.
{@a declarations}
### The _declarations_ array
You tell Angular which components belong to the `AppModule` by listing it in the module's `declarations` array.
@ -91,6 +103,8 @@ that you must also add to the `declarations` array.
~~~ {.alert.is-important}
**Only _declarables_** &mdash; _components_, _directives_ and _pipes_ &mdash; belong in the `declarations` array.
Do not put any other kind of class in `declarations`; _not_ `NgModule` classes, _not_ service classes, _not_ model classes.
@ -100,6 +114,8 @@ Do not put any other kind of class in `declarations`; _not_ `NgModule` classes,
{@a bootstrap-array}
### The _bootstrap_ array
You launch the application by [_bootstrapping_](guide/appmodule#main) the root `AppModule`.
@ -124,6 +140,8 @@ Which brings us to the _bootstrapping_ process itself.
</l-main-section>
## Bootstrap in _main.ts_
There are many ways to bootstrap an application.
@ -135,10 +153,12 @@ and you'll run it in a browser. You can learn about other options later.
The recommended place to bootstrap a JIT-compiled browser application is in a separate file
in the `src` folder named `src/main.ts`
<code-example path="setup/src/main.ts" linenums="false">
<code-example path="setup/src/main.ts" title="src/main.ts" linenums="false">
</code-example>
This code creates a browser platform for dynamic (JIT) compilation and
bootstraps the `AppModule` described above.
@ -149,10 +169,12 @@ creates an instance of the component and inserts it within the element tag ident
The `AppComponent` selector &mdash; here and in most documentation samples &mdash; is `my-app`
so Angular looks for a `<my-app>` tag in the `index.html` like this one ...
<code-example path="setup/src/index.html" region="my-app" linenums="false">
<code-example path="setup/src/index.html" region="my-app" title="setup/src/index.html" linenums="false">
</code-example>
... and displays the `AppComponent` there.
This file is very stable. Once you've set it up, you may never change it again.
@ -162,6 +184,8 @@ This file is very stable. Once you've set it up, you may never change it again.
</l-main-section>
## More about Angular Modules
Your initial app has only a single module, the _root_ module.

View File

@ -6,10 +6,14 @@ The basic building blocks of Angular applications.
@description
Angular is a framework for building client applications in HTML and
either JavaScript or a language like TypeScript that compiles to JavaScript.
The framework consists of several libraries, some of them core and some optional.
You write Angular applications by composing HTML *templates* with Angularized markup,
writing *component* classes to manage those templates, adding application logic in *services*,
and boxing components and services in *modules*.
@ -23,9 +27,11 @@ You'll learn the details in the pages that follow. For now, focus on the big pic
<figure>
<img src="assets/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"> </img>
<img src="assets/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700"></img>
</figure>
The architecture diagram identifies the eight main building blocks of an Angular application:
* [Modules](guide/architecture#modules)
@ -53,17 +59,23 @@ Learn these building blocks, and you're on your way.
~~~
## Modules
<figure>
<img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"></img>
</figure>
Angular apps are modular and Angular has its own modularity system called _Angular modules_ or _NgModules_.
_Angular modules_ are a big deal.
This page introduces modules; the [Angular modules](guide/ngmodule) page covers them in depth.
<br class="l-clear-both"><br>Every Angular app has at least one Angular module class, [the _root module_](guide/appmodule),
<br class="l-clear-both"><br>
Every Angular app has at least one Angular module class, [the _root module_](guide/appmodule "AppModule: the root module"),
conventionally named `AppModule`.
While the _root module_ may be the only module in a small application, most apps have many more
@ -74,6 +86,8 @@ An Angular module, whether a _root_ or _feature_, is a class with an `@NgModule`
~~~ {.l-sub-section}
Decorators are functions that modify JavaScript classes.
Angular has many decorators that attach metadata to classes so that it knows
what those classes mean and how they should work.
@ -82,6 +96,8 @@ Learn more</a> about decorators on the web.
~~~
`NgModule` is a decorator function that takes a single metadata object whose properties describe the module.
The most important properties are:
* `declarations` - the _view classes_ that belong to this module.
@ -99,7 +115,7 @@ that hosts all other app views. Only the _root module_ should set this `bootstra
Here's a simple root module:
<code-example path="architecture/src/app/mini-app.ts" region="module" linenums="false">
<code-example path="architecture/src/app/mini-app.ts" region="module" title="src/app/app.module.ts" linenums="false">
</code-example>
@ -107,18 +123,24 @@ Here's a simple root module:
~~~ {.l-sub-section}
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root module has no reason to _export_ anything because other components don't need to _import_ the root module.
~~~
Launch an application by _bootstrapping_ its root module.
During development you're likely to bootstrap the `AppModule` in a `main.ts` file like this one.
<code-example path="architecture/src/main.ts" linenums="false">
<code-example path="architecture/src/main.ts" title="src/main.ts" linenums="false">
</code-example>
### Angular modules vs. JavaScript modules
The Angular module &mdash; a class decorated with `@NgModule` &mdash; is a fundamental feature of Angular.
@ -145,18 +167,26 @@ Other JavaScript modules use *import statements* to access public objects from o
~~~ {.l-sub-section}
<a href="http://exploringjs.com/es6/ch_modules.html" target="_blank">Learn more about the JavaScript module system on the web.</a>
~~~
These are two different and _complementary_ module systems. Use them both to write your apps.
### Angular libraries
<figure>
<img src="assets/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px"></img>
</figure>
Angular ships as a collection of JavaScript modules. You can think of them as library modules.
Each Angular library name begins with the `@angular` prefix.
@ -170,18 +200,24 @@ For example, import Angular's `Component` decorator from the `@angular/core` lib
</code-example>
You also import Angular _modules_ from Angular _libraries_ using JavaScript import statements:
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false">
</code-example>
In the example of the simple root module above, the application module needs material from within that `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false">
</code-example>
In this way you're using both the Angular and JavaScript module systems _together_.
It's easy to confuse the two systems because they share the common vocabulary of "imports" and "exports".
@ -190,6 +226,8 @@ Hang in there. The confusion yields to clarity with time and experience.
~~~ {.l-sub-section}
Learn more from the [Angular modules](guide/ngmodule) page.
@ -197,18 +235,20 @@ Learn more from the [Angular modules](guide/ngmodule) page.
<div class='l-hr'>
---
</div>
## Components
<figure>
<img src="assets/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px"></img>
</figure>
A _component_ controls a patch of screen called a *view*.
For example, the following views are controlled by components:
@ -220,7 +260,7 @@ For example, the following views are controlled by components:
You define a component's application logic&mdash;what it does to support the view&mdash;inside a class.
The class interacts with the view through an API of properties and methods.
<a id="component-code"></a>
{@a component-code}
For example, this `HeroListComponent` has a `heroes` property that returns an array of heroes
that it acquires from a service.
`HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
@ -230,21 +270,25 @@ that it acquires from a service.
</code-example>
Angular creates, updates, and destroys components as the user moves through the application.
Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()` declared above.
<div class='l-hr'>
---
</div>
## Templates
<figure>
<img src="assets/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px"></img>
</figure>
You define a component's view with its companion **template**. A template is a form of HTML
that tells Angular how to render the component.
@ -252,10 +296,12 @@ A template looks like regular HTML, except for a few differences. Here is a
template for our `HeroListComponent`:
<code-example path="architecture/src/app/hero-list.component.html">
<code-example path="architecture/src/app/hero-list.component.html" title="src/app/hero-list.component.html">
</code-example>
Although this template uses typical HTML elements like `<h2>` and `<p>`, it also has some differences. Code like `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>` uses Angular's [template syntax](guide/template-syntax).
@ -268,25 +314,31 @@ The `HeroDetailComponent` is a **child** of the `HeroListComponent`.
<figure>
<img src="assets/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px"></img>
</figure>
Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom components mix seamlessly with native HTML in the same layouts.
<br class="l-clear-both">
<div class='l-hr'>
---
</div>
## Metadata
<figure>
<img src="assets/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px"></img>
</figure>
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
<br class="l-clear-both">[Looking back at the code](guide/architecture#component-code) for `HeroListComponent`, you can see that it's just a class.
<br class="l-clear-both">
[Looking back at the code](guide/architecture#component-code) for `HeroListComponent`, you can see that it's just a class.
There is no evidence of a framework, no "Angular" in it at all.
In fact, `HeroListComponent` really is *just a class*. It's not a component until you *tell Angular about it*.
@ -301,39 +353,51 @@ Here's some metadata for `HeroListComponent`:
</code-example>
Here is the `@Component` decorator, which identifies the class
immediately below it as a component class.
The `@Component` decorator takes a required configuration object with the
information Angular needs to create and present the component and its view.
Here are a few of the most useful `@Component` configuration options:
- `selector`: CSS selector that tells Angular to create and insert an instance of this component
* `selector`: CSS selector that tells Angular to create and insert an instance of this component
where it finds a `<hero-list>` tag in *parent* HTML.
For example, if an app's HTML contains `<hero-list></hero-list>`, then
Angular inserts an instance of the `HeroListComponent` view between those tags.
- `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates).
- `providers`: array of **dependency injection providers** for services that the component requires.
* `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates).
* `providers`: array of **dependency injection providers** for services that the component requires.
This is one way to tell Angular that the component's constructor requires a `HeroService`
so it can get the list of heroes to display.
<figure>
<img src="assets/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px"></img>
</figure>
The metadata in the `@Component` tells Angular where to get the major building blocks you specify for the component.
The template, metadata, and component together describe a view.
Apply other metadata decorators in a similar fashion to guide Angular behavior.
`@Injectable`, `@Input`, and `@Output` are a few of the more popular decorators.<br class="l-clear-both">The architectural takeaway is that you must add metadata to your code
`@Injectable`, `@Input`, and `@Output` are a few of the more popular decorators.<br class="l-clear-both">
The architectural takeaway is that you must add metadata to your code
so that Angular knows what to do.
<div class='l-hr'>
---
</div>
## Data binding
@ -342,27 +406,33 @@ into actions and value updates. Writing such push/pull logic by hand is tedious,
read as any experienced jQuery programmer can attest.
<figure>
<img src="assets/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"> </img>
<img src="assets/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px"></img>
</figure>
Angular supports **data binding**,
a mechanism for coordinating parts of a template with parts of a component.
Add binding markup to the template HTML to tell Angular how to connect both sides.
As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms:
As the diagram shows, there are four forms of data binding syntax. Each form has a direction &mdash; to the DOM, from the DOM, or in both directions.<br class="l-clear-both">
The `HeroListComponent` [example](guide/architecture#templates) template has three forms:
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding">
</code-example>
* The `{{hero.name}}` [*interpolation*](guide/displaying-data)
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
displays the component's `hero.name` property value within the `<li>` element.
* The `[hero]` [*property binding*](guide/template-syntax) passes the value of `selectedHero` from
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of `selectedHero` from
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* The `(click)` [*event binding*](guide/user-input) calls the component's `selectHero` method when the user clicks a hero's name.
* The `(click)` [*event binding*](guide/user-input#click) calls the component's `selectHero` method when the user clicks a hero's name.
**Two-way data binding** is an important fourth form
that combines property and event binding in a single notation, using the `ngModel` directive.
@ -373,6 +443,8 @@ Here's an example from the `HeroDetailComponent` template:
</code-example>
In two-way binding, a data property value flows to the input box from the component as with property binding.
The user's changes also flow back to the component, resetting the property to the latest value,
as with event binding.
@ -382,29 +454,35 @@ from the root of the application component tree through all child components.
<figure>
<img src="assets/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"></img>
</figure>
Data binding plays an important role in communication
between a template and its component.<br class="l-clear-both">
<figure>
<img src="assets/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px"></img>
</figure>
Data binding is also important for communication between parent and child components.<br class="l-clear-both">
<div class='l-hr'>
---
</div>
## Directives
<figure>
<img src="assets/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px"></img>
</figure>
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM
according to the instructions given by **directives**.
@ -416,11 +494,15 @@ a `@Component` decorator is actually a `@Directive` decorator extended with temp
~~~ {.l-sub-section}
While **a component is technically a directive**,
components are so distinctive and central to Angular applications that this architectural overview separates components from directives.
~~~
Two *other* kinds of directives exist: _structural_ and _attribute_ directives.
They tend to appear within an element tag as attributes do,
@ -435,8 +517,12 @@ The [example template](guide/architecture#templates) uses two built-in structura
</code-example>
* [`*ngFor`](guide/displaying-data) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data) includes the `HeroDetail` component only if a selected hero exists.
* [`*ngFor`](guide/displaying-data#ngFor) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) includes the `HeroDetail` component only if a selected hero exists.
**Attribute** directives alter the appearance or behavior of an existing element.
In templates they look like regular HTML attributes, hence the name.
@ -450,31 +536,38 @@ by setting its display value property and responding to change events.
</code-example>
Angular has a few more directives that either alter the layout structure
(for example, [ngSwitch](guide/template-syntax))
(for example, [ngSwitch](guide/template-syntax#ngSwitch))
or modify aspects of DOM elements and components
(for example, [ngStyle](guide/template-syntax) and [ngClass](guide/template-syntax)).
(for example, [ngStyle](guide/template-syntax#ngStyle) and [ngClass](guide/template-syntax#ngClass)).
Of course, you can also write your own directives. Components such as
`HeroListComponent` are one kind of custom directive.
<!-- PENDING: link to where to learn more about other kinds! -->
<div class='l-hr'>
---
</div>
## Services
<figure>
<img src="assets/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px"></img>
</figure>
_Service_ is a broad category encompassing any value, function, or feature that your application needs.
Almost anything can be a service.
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.<br class="l-clear-both">Examples include:
A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.<br class="l-clear-both">
Examples include:
* logging service
* data service
* message bus
@ -493,6 +586,8 @@ Here's an example of a service class that logs to the browser console:
</code-example>
Here's a `HeroService` that uses a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) to fetch heroes.
The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work.
@ -501,6 +596,8 @@ The `HeroService` depends on the `Logger` service and another `BackendService` t
</code-example>
Services are everywhere.
Component classes should be lean. They don't fetch data from the server,
@ -519,20 +616,24 @@ Angular does help you *follow* these principles by making it easy to factor your
application logic into services and make those services available to components through *dependency injection*.
<div class='l-hr'>
---
</div>
## Dependency injection
<figure>
<img src="assets/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px"></img>
</figure>
_Dependency injection_ is a way to supply a new instance of a class
with the fully-formed dependencies it requires. Most dependencies are services.
Angular uses dependency injection to provide new components with the services they need.<br class="l-clear-both">Angular can tell which services a component needs by looking at the types of its constructor parameters.
Angular uses dependency injection to provide new components with the services they need.<br class="l-clear-both">
Angular can tell which services a component needs by looking at the types of its constructor parameters.
For example, the constructor of your `HeroListComponent` needs a `HeroService`:
@ -540,6 +641,8 @@ For example, the constructor of your `HeroListComponent` needs a `HeroService`:
</code-example>
When Angular creates a component, it first asks an **injector** for
the services that the component requires.
@ -553,13 +656,17 @@ This is *dependency injection*.
The process of `HeroService` injection looks a bit like this:
<figure>
<img src="assets/images/devguide/architecture/injector-injects.png" alt="Service"> </img>
<img src="assets/images/devguide/architecture/injector-injects.png" alt="Service"></img>
</figure>
If the injector doesn't have a `HeroService`, how does it know how to make one?
In brief, you must have previously registered a **provider** of the `HeroService` with the injector.
A provider is something that can create or return a service, typically the service class itself.
You can register providers in modules or in components.
In general, add providers to the [root module](guide/architecture#module) so that
@ -570,6 +677,8 @@ the same instance of a service is available everywhere.
</code-example>
Alternatively, register at a component level in the `providers` property of the `@Component` metadata:
@ -577,6 +686,8 @@ Alternatively, register at a component level in the `providers` property of the
</code-example>
Registering at a component level means you get a new instance of the
service with each new instance of that component.
@ -596,9 +707,9 @@ Points to remember about dependency injection:
* Register *providers* with injectors.
<div class='l-hr'>
---
</div>
## Wrap up
@ -645,5 +756,7 @@ by implementing the lifecycle hook interfaces.
> [**Router**](guide/router): Navigate from page to page within the client
application and never leave the browser.
> [**Testing**](guide/testing): Run unit tests on your application parts as they interact with the Angular framework
using the _Angular Testing Platform_.

View File

@ -6,6 +6,8 @@ Attribute directives attach behavior to elements.
@description
An **Attribute** directive changes the appearance or behavior of a DOM element.
# Contents
@ -19,6 +21,8 @@ An **Attribute** directive changes the appearance or behavior of a DOM element.
Try the <live-example title="Attribute Directive example"></live-example>.
## Directives overview
There are three kinds of directives in Angular:
@ -31,14 +35,16 @@ There are three kinds of directives in Angular:
You saw a component for the first time in the [QuickStart](quickstart) guide.
*Structural Directives* change the structure of the view.
Two examples are [NgFor](guide/template-syntax) and [NgIf](guide/template-syntax).
Two examples are [NgFor](guide/template-syntax#ngFor) and [NgIf](guide/template-syntax#ngIf).
Learn about them in the [Structural Directives](guide/structural-directives) guide.
*Attribute directives* are used as attributes of elements.
The built-in [NgStyle](guide/template-syntax) directive in the
The built-in [NgStyle](guide/template-syntax#ngStyle) directive in the
[Template Syntax](guide/template-syntax) guide, for example,
can change several element styles at the same time.
## Build a simple attribute directive
An attribute directive minimally requires building a controller class annotated with
@ -55,6 +61,8 @@ when the user hovers over that element. You can apply it like this:
</code-example>
### Write the directive code
Follow the [setup](guide/setup) instructions for creating a new local project
@ -63,10 +71,12 @@ named <code>attribute-directives</code>.
Create the following source file in the indicated folder:
<code-example path="attribute-directives/src/app/highlight.directive.1.ts">
<code-example path="attribute-directives/src/app/highlight.directive.1.ts" title="src/app/highlight.directive.ts">
</code-example>
The `import` statement specifies symbols from the Angular `core`:
1. `Directive` provides the functionality of the `@Directive` decorator.
@ -76,6 +86,8 @@ so the code can access the DOM element.
Next, the `@Directive` decorator function contains the directive metadata in a configuration object
as an argument.
`@Directive` requires a CSS selector to identify
the HTML in the template that is associated with the directive.
The [CSS selector for an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
@ -86,6 +98,8 @@ Angular locates all elements in the template that have an attribute named `myHig
~~~ {.l-sub-section}
### Why not call it "highlight"?
Though *highlight* is a more concise name than *myHighlight* and would work,
@ -100,6 +114,8 @@ For a simple demo, the short prefix, `my`, helps distinguish your custom directi
~~~
After the `@Directive` metadata comes the directive's controller class,
called `HighlightDirective`, which contains the logic for the directive.
Exporting `HighlightDirective` makes it accessible to other components.
@ -110,6 +126,8 @@ into the constructor.
`ElementRef` is a service that grants direct access to the DOM element
through its `nativeElement` property.
## Apply the attribute directive
To use the new `HighlightDirective`, create a template that
@ -120,37 +138,45 @@ Put the template in its own <code>app.component.html</code>
file that looks like this:
<code-example path="attribute-directives/src/app/app.component.1.html">
<code-example path="attribute-directives/src/app/app.component.1.html" title="src/app/app.component.html">
</code-example>
Now reference this template in the `AppComponent`:
<code-example path="attribute-directives/src/app/app.component.ts">
<code-example path="attribute-directives/src/app/app.component.ts" title="src/app/app.component.ts">
</code-example>
Next, add an `import` statement to fetch the `Highlight` directive and
add that class to the `declarations` NgModule metadata. This way Angular
recognizes the directive when it encounters `myHighlight` in the template.
<code-example path="attribute-directives/src/app/app.module.ts">
<code-example path="attribute-directives/src/app/app.module.ts" title="src/app/app.module.ts">
</code-example>
Now when the app runs, the `myHighlight` directive highlights the paragraph text.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight"> </img>
<img src="assets/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight"></img>
</figure>
~~~ {.l-sub-section}
### Your directive isn't working?
Did you remember to add the directive to the `declarations` attribute of `@NgModule`?
@ -160,10 +186,12 @@ Open the console in the browser tools and look for an error like this:
<code-example format="nocode">
EXCEPTION: Template parse errors:
Can't bind to 'myHighlight' since it isn't a known property of 'p'.
Can't bind to 'myHighlight' since it isn't a known property of 'p'.
</code-example>
Angular detects that you're trying to bind to *something* but it can't find this directive
in the module's `declarations` array.
After specifying `HighlightDirective` in the `declarations` array,
@ -172,11 +200,15 @@ Angular knows it can apply the directive to components declared in this module.
~~~
To summarize, Angular found the `myHighlight` attribute on the `<p>` element.
It created an instance of the `HighlightDirective` class and
injected a reference to the `<p>` element into the directive's constructor
which sets the `<p>` element's background style to yellow.
## Respond to user-initiated events
Currently, `myHighlight` simply sets an element color.
@ -192,6 +224,8 @@ add the `Input` symbol as well because you'll need it soon.
</code-example>
Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` decorator.
@ -200,12 +234,16 @@ each adorned by the `HostListener` decorator.
</code-example>
The `@HostListener` decorator lets you subscribe to events of the DOM
element that hosts an attribute directive, the `<p>` in this case.
~~~ {.l-sub-section}
Of course you could reach into the DOM with standard JavaScript and and attach event listeners manually.
There are at least three problems with _that_ approach:
@ -216,6 +254,8 @@ There are at least three problems with _that_ approach:
~~~
The handlers delegate to a helper method that sets the color on the DOM element, `el`,
which you declare and initialize in the constructor.
@ -224,22 +264,28 @@ which you declare and initialize in the constructor.
</code-example>
Here's the updated directive in full:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts">
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" title="src/app/highlight.directive.ts">
</code-example>
Run the app and confirm that the background color appears when
the mouse hovers over the `p` and disappears as it moves out.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"> </img>
<img src="assets/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"></img>
</figure>
## Pass values into the directive with an _@Input_ data binding
Currently the highlight color is hard-coded _within_ the directive. That's inflexible.
@ -255,6 +301,8 @@ Start by adding a `highlightColor` property to the directive class like this:
{@a input}
### Binding to an _@Input_ property
Notice the `@Input` decorator. It adds metadata to the class that makes the directive's `highlightColor` property available for binding.
@ -269,6 +317,8 @@ Try it by adding the following directive binding variations to the `AppComponent
</code-example>
Add a `color` property to the `AppComponent`.
@ -276,6 +326,8 @@ Add a `color` property to the `AppComponent`.
</code-example>
Let it control the highlight color with a property binding.
@ -283,6 +335,8 @@ Let it control the highlight color with a property binding.
</code-example>
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
@ -290,6 +344,8 @@ That's good, but it would be nice to _simultaneously_ apply the directive and se
</code-example>
The `[myHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
and sets the directive's highlight color with a property binding.
You're re-using the directive's attribute selector (`[myHighlight]`) to do both jobs.
@ -302,10 +358,14 @@ You'll have to rename the directive's `highlightColor` property to `myHighlight`
</code-example>
This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn't convey the property's intent.
{@a input-alias}
### Bind to an _@Input_ alias
Fortunately you can name the directive property whatever you want _and_ **_alias it_** for binding purposes.
@ -317,6 +377,8 @@ Restore the original property name and specify the selector as the alias in the
</code-example>
_Inside_ the directive the property is known as `highlightColor`.
_Outside_ the directive, where you bind to it, it's known as `myHighlight`.
@ -327,6 +389,8 @@ You get the best of both worlds: the property name you want and the binding synt
</code-example>
Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method to use it.
If someone neglects to bind to `highlightColor`, highlight in red:
@ -335,6 +399,8 @@ If someone neglects to bind to `highlightColor`, highlight in red:
</code-example>
Here's the latest version of the directive class.
@ -342,6 +408,8 @@ Here's the latest version of the directive class.
</code-example>
## Write a harness to try it
It may be difficult to imagine how this directive actually works.
@ -355,6 +423,8 @@ Update <code>app.component.html</code> as follows:
</code-example>
Revise the `AppComponent.color` so that it has no initial value.
@ -362,14 +432,18 @@ Revise the `AppComponent.color` so that it has no initial value.
</code-example>
Here are the harness and directive in action.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"> </img>
<img src="assets/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"></img>
</figure>
## Bind to a second property
This highlight directive has a single customizable property. In a real app, it may need more.
@ -385,6 +459,8 @@ Add a second **input** property to `HighlightDirective` called `defaultColor`:
</code-example>
Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
then with the `defaultColor`, and falls back to "red" if both properties are undefined.
@ -393,6 +469,8 @@ then with the `defaultColor`, and falls back to "red" if both properties are und
</code-example>
How do you bind to a second property when you're already binding to the `myHighlight` attribute name?
As with components, you can add as many directive property bindings as you need by stringing them along in the template.
@ -404,6 +482,8 @@ and fall back to "violet" as the default color.
</code-example>
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
because you made it _public_ with the `@Input` decorator.
@ -411,18 +491,20 @@ Here's how the harness should work when you're done coding.
<figure class='image-display'>
<img src="assets/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"> </img>
<img src="assets/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"></img>
</figure>
## Summary
This page covered how to:
- [Build an **attribute directive**](guide/attribute-directives#write-directive) that modifies the behavior of an element.
- [Apply the directive](guide/attribute-directives#apply-directive) to an element in a template.
- [Respond to **events**](guide/attribute-directives#respond-to-user) that change the directive's behavior.
- [**Bind** values to the directive](guide/attribute-directives#bindings).
* [Build an **attribute directive**](guide/attribute-directives#write-directive) that modifies the behavior of an element.
* [Apply the directive](guide/attribute-directives#apply-directive) to an element in a template.
* [Respond to **events**](guide/attribute-directives#respond-to-user) that change the directive's behavior.
* [**Bind** values to the directive](guide/attribute-directives#bindings).
The final source code follows:
@ -455,8 +537,12 @@ The final source code follows:
</code-tabs>
You can also experience and download the <live-example title="Attribute Directive example"></live-example>.
### Appendix: Why add _@Input_?
In this demo, the `hightlightColor` property is an ***input*** property of
@ -467,6 +553,8 @@ the `HighlightDirective`. You've seen it applied without an alias:
</code-example>
You've seen it with an alias:
@ -474,6 +562,8 @@ You've seen it with an alias:
</code-example>
Either way, the `@Input` decorator tells Angular that this property is
_public_ and available for binding by a parent component.
Without `@Input`, Angular refuses to bind to the property.
@ -509,6 +599,8 @@ Now apply that reasoning to the following example:
</code-example>
* The `color` property in the expression on the right belongs to the template's component.
The template and its component trust each other.
The `color` property doesn't require the `@Input` decorator.

View File

@ -6,6 +6,8 @@ Browser support and polyfills guide.
@description
Angular supports most recent browsers. This includes the following specific versions:
@ -197,6 +199,8 @@ Angular supports most recent browsers. This includes the following specific vers
~~~ {.l-sub-section}
Angular's continuous integration process runs unit tests of the framework on all of these browsers for every pull request,
using <a href="https://saucelabs.com/" target="_blank">SauceLabs</a> and
<a href="https://www.browserstack.com" target="_blank">Browserstack</a>.
@ -204,6 +208,8 @@ using <a href="https://saucelabs.com/" target="_blank">SauceLabs</a> and
~~~
## Polyfills #
Angular is built on the latest standards of the web platform.
Targeting such a wide range of browsers is challenging because they do not support all features of modern browsers.
@ -211,10 +217,12 @@ Targeting such a wide range of browsers is challenging because they do not suppo
You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`)
that implement missing features in JavaScript.
<code-example path="quickstart/src/index.html" region="polyfills" linenums="false">
<code-example path="quickstart/src/index.html" region="polyfills" title="quickstart/src/index.html" linenums="false">
</code-example>
A particular browser may require at least one polyfill to run _any_ Angular application.
You may need additional polyfills for specific features.
@ -223,6 +231,8 @@ The tables below can help you determine which polyfills to load, depending on th
~~~ {.alert.is-important}
The suggested polyfills are the ones that run full Angular applications.
You may need additional polyfills to support features not covered by this list.
Note that polyfills cannot magically transform an old, slow browser into a modern, fast one.
@ -230,6 +240,8 @@ Note that polyfills cannot magically transform an old, slow browser into a moder
~~~
### Mandatory polyfills ##
These are the polyfills required to run an Angular application on each supported browser:
@ -267,6 +279,8 @@ These are the polyfills required to run an Angular application on each supported
</td>
<td>
[ES6](guide/browser-support#core-es6)
</td>
@ -279,6 +293,8 @@ These are the polyfills required to run an Angular application on each supported
</td>
<td>
[ES6<br>classList](guide/browser-support#classlist)
</td>
@ -287,6 +303,8 @@ These are the polyfills required to run an Angular application on each supported
</table>
### Optional browser features to polyfill ##
Some features of Angular may require additional polyfills.
@ -317,10 +335,12 @@ Here are the features which may require additional polyfills:
<tr style="vertical-align: top">
<td>
<a href="./animations.html"> Animations </a>
<a href="./animations.html">Animations</a>
</td>
<td>
[Web Animations](guide/browser-support#web-animations)
</td>
@ -333,10 +353,12 @@ Here are the features which may require additional polyfills:
<tr style="vertical-align: top">
<td>
<a href="../api/common/index/DatePipe-pipe.html" target="_blank"> Date </a> <span> , </span> <a href="../api/common/index/CurrencyPipe-pipe.html" target="_blank"> currency </a> <span> , </span> <a href="../api/common/index/DecimalPipe-pipe.html" target="_blank"> decimal </a> <span> and </span> <a href="../api/common/index/PercentPipe-pipe.html" target="_blank"> percent </a> <span> pipes </span>
<a href="../api/common/index/DatePipe-pipe.html" target="_blank">Date</a> <span>, </span> <a href="../api/common/index/CurrencyPipe-pipe.html" target="_blank">currency</a> <span>, </span> <a href="../api/common/index/DecimalPipe-pipe.html" target="_blank">decimal</a> <span> and </span> <a href="../api/common/index/PercentPipe-pipe.html" target="_blank">percent</a> <span> pipes</span>
</td>
<td>
[Intl API](guide/browser-support#intl)
</td>
@ -349,11 +371,13 @@ Here are the features which may require additional polyfills:
<tr style="vertical-align: top">
<td>
<a href="../api/common/index/NgClass-directive.html" target="_blank"> NgClass </a> <span> on SVG elements </span>
<a href="../api/common/index/NgClass-directive.html" target="_blank">NgClass</a> <span> on SVG elements</span>
</td>
<td>
[classList](guide/browser-support#classlist)
[classList](guide/browser-support#classlist)
</td>
<td>
@ -365,11 +389,13 @@ Here are the features which may require additional polyfills:
<tr style="vertical-align: top">
<td>
<a href="./server-communication.html"> Http </a> <span> when sending and receiving binary data </span>
<a href="./server-communication.html">Http</a> <span> when sending and receiving binary data</span>
</td>
<td>
[Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
[Typed&nbsp;Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
</td>
<td>
@ -380,6 +406,8 @@ Here are the features which may require additional polyfills:
</table>
### Suggested polyfills ##
Below are the polyfills which are used to test the framework itself. They are a good starting point for an application.
@ -405,7 +433,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='core-es6' href="https://github.com/zloirock/core-js" target="_blank"> ES6 </a>
<a id='core-es6' href="https://github.com/zloirock/core-js" target="_blank">ES6</a>
</td>
<td>
@ -421,7 +449,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='classlist' href="https://github.com/eligrey/classList.js" target="_blank"> classList </a>
<a id='classlist' href="https://github.com/eligrey/classList.js" target="_blank">classList</a>
</td>
<td>
@ -437,7 +465,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='intl' href="https://github.com/andyearnshaw/Intl.js" target="_blank"> Intl </a>
<a id='intl' href="https://github.com/andyearnshaw/Intl.js" target="_blank">Intl</a>
</td>
<td>
@ -453,7 +481,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='web-animations' href="https://github.com/web-animations/web-animations-js" target="_blank"> Web Animations </a>
<a id='web-animations' href="https://github.com/web-animations/web-animations-js" target="_blank">Web Animations</a>
</td>
<td>
@ -469,7 +497,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js" target="_blank"> Typed Array </a>
<a id='typedarray' href="https://github.com/inexorabletash/polyfill/blob/master/typedarray.js" target="_blank">Typed Array</a>
</td>
<td>
@ -485,7 +513,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='blob' href="https://github.com/eligrey/Blob.js" target="_blank"> Blob </a>
<a id='blob' href="https://github.com/eligrey/Blob.js" target="_blank">Blob</a>
</td>
<td>
@ -501,7 +529,7 @@ Below are the polyfills which are used to test the framework itself. They are a
<tr>
<td>
<a id='formdata' href="https://github.com/francois2metz/html5-formdata" target="_blank"> FormData </a>
<a id='formdata' href="https://github.com/francois2metz/html5-formdata" target="_blank">FormData</a>
</td>
<td>
@ -516,5 +544,7 @@ Below are the polyfills which are used to test the framework itself. They are a
</table>
\* Figures are for minified and gzipped code,
computed with the <a href="http://closure-compiler.appspot.com/home" target="_blank">closure compiler</a>.

File diff suppressed because it is too large Load Diff

View File

@ -5,6 +5,8 @@ Cookbook
A collection of recipes for common Angular application scenarios.
@description
The *Cookbook* offers answers to common implementation questions.
Each cookbook page is a collection of recipes focused on a particular Angular feature or application challenge
@ -13,11 +15,15 @@ such as data binding, cross-component interaction, and communicating with a remo
~~~ {.l-sub-section}
The cookbook is just getting started. Many more recipes are on the way.
~~~
Each cookbook links to a live sample with every recipe included.
Recipes are deliberately brief and code-centric.

View File

@ -6,6 +6,8 @@ An annotated history of recent documentation improvements.
@description
The Angular documentation is a living document with continuous improvements.
This log calls attention to recent significant changes.
@ -41,6 +43,7 @@ Read about moving your existing project to this structure in
the QuickStart repo update instructions</a>.
Notably:
* `app/main.ts` moved to `src/main.ts`.
* `app/` moved to `src/app/`.
* `index.html`, `styles.css` and `tsconfig.json` moved inside `src/`.
@ -56,15 +59,18 @@ Remember also that you can use both techniques in the same app,
choosing the approach that best fits each scenario.
## NEW: Deployment guide (2017-01-30)
The new [Deployment](guide/deployment) guide describes techniques for putting your application on a server.
It includes important advice on optimizing for production.
## Hierarchical Dependency Injection: refreshed (2017-01-13)
[Hierarchical Dependency Injection](guide/hierarchical-dependency-injection) guide is significantly revised.
Closes issue #3086.
Revised samples are clearer and cover all topics discussed.
## Miscellaneous (2017-01-05)
* [Setup](guide/setup) guide:
added (optional) instructions on how to remove _non-essential_ files.
* No longer consolidate RxJS operator imports in `rxjs-extensions` file; each file should import what it needs.
@ -72,28 +78,34 @@ added (optional) instructions on how to remove _non-essential_ files.
* [Style Guide](guide/style-guide): copy edits and revised rules.
## Router: more detail (2016-12-21)
Added more information to the [Router](guide/router) guide
including sections named outlets, wildcard routes, and preload strategies.
## HTTP: how to set default request headers (and other request options) (2016-12-14)
Added section on how to set default request headers (and other request options) to
[HTTP](guide/server-communication) guide.
[HTTP](guide/server-communication#override-default-request-options) guide.
## Testing: added component test plunkers (2016-12-02)
Added two plunkers that each test _one simple component_ so you can write a component test plunker of your own: <live-example name="setup" plnkr="quickstart-specs">one</live-example> for the QuickStart seed's `AppComponent` and <live-example name="testing" plnkr="banner-specs">another</live-example> for the Testing guide's `BannerComponent`.
Linked to these plunkers in [Testing](guide/testing) and [Setup anatomy](guide/setup-systemjs-anatomy) guides.
Linked to these plunkers in [Testing](guide/testing#live-examples) and [Setup anatomy](guide/setup-systemjs-anatomy) guides.
## Internationalization: pluralization and _select_ (2016-11-30)
The [Internationalization (i18n)](cookbook/i18n) guide explains how to handle pluralization and
translation of alternative texts with `select`.
The sample demonstrates these features too.
## Testing: karma file updates (2016-11-30)
* `karma.config` + `karma-test-shim` can handle multiple spec source paths;
see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294).
* Displays Jasmine Runner output in the karma-launched browser.
## QuickStart Rewrite (2016-11-18)
The QuickStart is completely rewritten so that it actually is quick.
It references a minimal "Hello Angular" app running in Plunker.
The new [Setup](guide/setup) page tells you how to install a local development environment
@ -101,9 +113,11 @@ by downloading (or cloning) the QuickStart github repository.
You are no longer asked to copy-and-paste code into setup files that were not explained anyway.
## Sync with Angular v.2.2.0 (2016-11-14)
Docs and code samples updated and tested with Angular v.2.2.0.
## UPDATE: NgUpgrade Guide for the AOT friendly _upgrade/static_ module (2016-11-14)
The updated [NgUpgrade Guide](guide/upgrade) guide covers the
new AOT friendly `upgrade/static` module
released in v.2.2.0, which is the recommended
@ -111,15 +125,18 @@ facility for migrating from AngularJS to Angular.
The documentation for the version prior to v.2.2.0 has been removed.
## ES6 described in "TypeScript to JavaScript" (2016-11-14)
The updated [TypeScript to JavaScript](cookbook/ts-to-js) cookbook
now explains how to write apps in ES6/7
by translating the common idioms in the TypeScript documentation examples
(and elsewhere on the web) to ES6/7 and ES5.
## Sync with Angular v.2.1.1 (2016-10-21)
Docs and code samples updated and tested with Angular v.2.1.1.
## npm _@types_ packages replace _typings_ (2016-10-20)
Documentation samples now get TypeScript type information for 3rd party libraries
from npm `@types` packages rather than with the _typings_ tooling.
The `typings.json` file is gone.
@ -129,34 +146,41 @@ The `package.json` installs `@types/angular` and several `@types/angular-...`
packages in support of upgrade; these are not needed for pure Angular development.
## "Template Syntax" explains two-way data binding syntax (2016-10-20)
Demonstrates how to two-way data bind to a custom Angular component and
re-explains `[(ngModel)]` in terms of the basic `[()]` syntax.
## BREAKING CHANGE: `in-memory-web-api` (v.0.1.11) delivered as esm umd (2016-10-19)
This change supports ES6 developers and aligns better with typical Angular libraries.
It does not affect the module's API but it does affect how you load and import it.
See the <a href="https://github.com/angular/in-memory-web-api/blob/master/CHANGELOG.md#0113-2016-10-20" target="_blank">change note</a>
in the `in-memory-web-api` repo.
## "Router" _preload_ syntax and _:enter_/_:leave_ animations (2016-10-19)
The router can lazily _preload_ modules _after_ the app starts and
_before_ the user navigates to them for improved perceived performance.
New `:enter` and `:leave` aliases make animation more natural.
## Sync with Angular v.2.1.0 (2016-10-12)
Docs and code samples updated and tested with Angular v.2.1.0.
## NEW "Ahead of time (AOT) Compilation" cookbook (2016-10-11)
The NEW [Ahead of time (AOT) Compilation](cookbook/aot-compiler) cookbook
explains what AOT compilation is and why you'd want it.
It demonstrates the basics with a QuickStart app
followed by the more advanced considerations of compiling and bundling the Tour of Heroes.
## Sync with Angular v.2.0.2 (2016-10-6)
Docs and code samples updated and tested with Angular v.2.0.2.
## "Routing and Navigation" guide with the _Router Module_ (2016-10-5)
The [Routing and Navigation](guide/router) guide now locates route configuration
in a _Routing Module_.
The _Routing Module_ replaces the previous _routing object_ involving the `ModuleWithProviders`.

View File

@ -1,4 +1,6 @@
@description
Good tools make application development quicker and easier to maintain than
if you did everything by hand.
@ -29,6 +31,8 @@ And you can also <a href="/resources/zips/cli-quickstart/cli-quickstart.zip">dow
Step 1. Set up the Development Environment
</h2>
You need to set up your development environment before you can do anything.
Install **[Node.js® and npm](https://nodejs.org/en/download/)**
@ -36,12 +40,16 @@ if they are not already on your machine.
~~~ {.l-sub-section}
**Verify that you are running at least node `6.9.x` and npm `3.x.x`**
by running `node -v` and `npm -v` in a terminal/console window.
Older versions produce errors, but newer versions are fine.
~~~
Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globally.
@ -57,7 +65,11 @@ Then **install the [Angular CLI](https://github.com/angular/angular-cli)** globa
Step 2. Create a new project
</h2>
Open a terminal window.
Generate a new project and skeleton application by running the following commands:
@ -70,6 +82,8 @@ Generate a new project and skeleton application by running the following command
~~~ {.l-sub-section}
Patience please.
It takes time to set up a new project, most of it spent installing npm packages.
@ -83,15 +97,19 @@ It takes time to set up a new project, most of it spent installing npm packages.
Step 3: Serve the application
</h2>
Go to the project directory and launch the server.
<code-example language="sh" class="code-shell">
cd my-app
ng serve --open
ng serve --open
</code-example>
The `ng serve` command launches the server, watches your files,
and rebuilds the app as you make changes to those files.
@ -102,7 +120,7 @@ Your app greets you with a message:
<figure class='image-display'>
<img src='assets/images/devguide/cli-quickstart/app-works.png' alt="The app works!"> </img>
<img src='assets/images/devguide/cli-quickstart/app-works.png' alt="The app works!"></img>
</figure>
@ -112,33 +130,43 @@ Your app greets you with a message:
Step 4: Edit your first Angular component
</h2>
The CLI created the first Angular component for you.
This is the _root component_ and it is named `app-root`.
You can find it in `./src/app/app.component.ts`.
Open the component file and change the `title` property from _app works!_ to _My First Angular App_:
<code-example path="cli-quickstart/src/app/app.component.ts" region="title" linenums="false">
<code-example path="cli-quickstart/src/app/app.component.ts" region="title" title="src/app/app.component.ts" linenums="false">
</code-example>
The browser reloads automatically with the revised title. That's nice, but it could look better.
Open `src/app/app.component.css` and give the component some style.
<code-example path="cli-quickstart/src/app/app.component.css" linenums="false">
<code-example path="cli-quickstart/src/app/app.component.css" title="src/app/app.component.css" linenums="false">
</code-example>
<figure class='image-display'>
<img src='assets/images/devguide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"> </img>
<img src='assets/images/devguide/cli-quickstart/my-first-app.png' alt="Output of QuickStart app"></img>
</figure>
Looking good!
## What's next?
That's about all you'd expect to do in a "Hello, World" app.
@ -147,6 +175,8 @@ a small application that demonstrates the great things you can build with Angula
Or you can stick around a bit longer to learn about the files in your brand new project.
## Project file review
An Angular CLI project is the foundation for both quick experiments and enterprise solutions.
@ -159,6 +189,8 @@ Whenever you want to know more about how Angular CLI works make sure to visit
Some of the generated files might be unfamiliar to you.
### The `src` folder
Your app lives in the `src` folder.
All Angular components, templates, styles, images, and anything else your app needs go here.
@ -286,9 +318,11 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
Defines the `AppComponent` along with an HTML template, CSS stylesheet, and a unit test.
It is the **root** component of what will become a tree of nested components
as the application evolves.
It is the **root** component of what will become a tree of nested components
as the application evolves.
</td>
</tr>
@ -300,9 +334,11 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
Defines `AppModule`, the [root module](guide/guide/appmodule) that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`.
Soon there will be more components to declare.
Defines `AppModule`, the [root module](guide/guide/appmodule "AppModule: the root module") that tells Angular how to assemble the application.
Right now it declares only the `AppComponent`.
Soon there will be more components to declare.
</td>
</tr>
@ -314,8 +350,10 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
A folder where you can put images and anything else to be copied wholesale
when you build your application.
when you build your application.
</td>
</tr>
@ -327,13 +365,15 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
This folder contains one file for each of your destination environments,
each exporting simple configuration variables to use in your application.
The files are replaced on-the-fly when you build your app.
You might use a different API endpoint for development than you do for production
or maybe different analytics tokens.
You might even use some mock services.
Either way, the CLI has you covered.
each exporting simple configuration variables to use in your application.
The files are replaced on-the-fly when you build your app.
You might use a different API endpoint for development than you do for production
or maybe different analytics tokens.
You might even use some mock services.
Either way, the CLI has you covered.
</td>
</tr>
@ -345,8 +385,10 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
Every site wants to look good on the bookmark bar.
Get started with your very own Angular icon.
Get started with your very own Angular icon.
</td>
</tr>
@ -358,10 +400,12 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
The main HTML page that is served when someone visits your site.
Most of the time you'll never need to edit it.
The CLI automatically adds all `js` and `css` files when building your app so you
never need to add any `<script>` or `<link>` tags here manually.
Most of the time you'll never need to edit it.
The CLI automatically adds all `js` and `css` files when building your app so you
never need to add any `&lt;script&gt;` or `&lt;link&gt;` tags here manually.
</td>
</tr>
@ -373,11 +417,13 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
The main entry point for your app.
Compiles the application with the [JIT compiler](guide/glossary)
and bootstraps the application's root module (`AppModule`) to run in the browser.
You can also use the [AOT compiler](guide/glossary)
without changing any code by passing in `--aot` to `ng build` or `ng serve`.
Compiles the application with the [JIT compiler](guide/glossary#jit)
and bootstraps the application's root module (`AppModule`) to run in the browser.
You can also use the [AOT compiler](guide/glossary#ahead-of-time-aot-compilation)
without changing any code by passing in `--aot` to `ng build` or `ng serve`.
</td>
</tr>
@ -389,10 +435,12 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
Different browsers have different levels of support of the web standards.
Polyfills help normalize those differences.
You should be pretty safe with `core-js` and `zone.js`, but be sure to check out
the [Browser Support guide](guide/guide/browser-support) for more information.
Polyfills help normalize those differences.
You should be pretty safe with `core-js` and `zone.js`, but be sure to check out
the [Browser Support guide](guide/guide/browser-support) for more information.
</td>
</tr>
@ -404,9 +452,11 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
Your global styles go here.
Most of the time you'll want to have local styles in your components for easier maintenance,
but styles that affect all of your app need to be in a central place.
Most of the time you'll want to have local styles in your components for easier maintenance,
but styles that affect all of your app need to be in a central place.
</td>
</tr>
@ -418,9 +468,11 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
This is the main entry point for your unit tests.
It has some custom configuration that might be unfamiliar, but it's not something you'll
need to edit.
It has some custom configuration that might be unfamiliar, but it's not something you'll
need to edit.
</td>
</tr>
@ -432,8 +484,10 @@ Any files outside of this folder are meant to support building your app.
</td>
<td>
TypeScript compiler configuration for the Angular app (`tsconfig.app.json`)
and for the unit tests (`tsconfig.spec.json`).
and for the unit tests (`tsconfig.spec.json`).
</td>
@ -443,6 +497,8 @@ Any files outside of this folder are meant to support building your app.
</table>
### The root folder
The `src/` folder is just one of the items inside the project's root folder.
Other files help you build, test, maintain, document, and deploy the app.
@ -554,10 +610,12 @@ These files go in the root folder next to `src/`.
</td>
<td>
Inside `e2e/` live the End-to-End tests.
They shouldn't be inside `src/` because e2e tests are really a separate app that
just so happens to test your main app.
That's also why they have their own `tsconfig.e2e.json`.
They shouldn't be inside `src/` because e2e tests are really a separate app that
just so happens to test your main app.
That's also why they have their own `tsconfig.e2e.json`.
</td>
</tr>
@ -569,8 +627,10 @@ These files go in the root folder next to `src/`.
</td>
<td>
`Node.js` creates this folder and puts all third party modules listed in
`package.json` inside of it.
`package.json` inside of it.
</td>
</tr>
@ -582,10 +642,12 @@ These files go in the root folder next to `src/`.
</td>
<td>
Configuration for Angular CLI.
In this file you can set several defaults and also configure what files are included
when your project is build.
Check out the official documentation if you want to know more.
In this file you can set several defaults and also configure what files are included
when your project is build.
Check out the official documentation if you want to know more.
</td>
</tr>
@ -597,10 +659,12 @@ These files go in the root folder next to `src/`.
</td>
<td>
Simple configuration for your editor to make sure everyone that uses your project
has the same basic configuration.
Most editors support an `.editorconfig` file.
See http://editorconfig.org for more information.
has the same basic configuration.
Most editors support an `.editorconfig` file.
See http://editorconfig.org for more information.
</td>
</tr>
@ -612,6 +676,8 @@ These files go in the root folder next to `src/`.
</td>
<td>
Git configuration to make sure autogenerated files are not commited to source control.
</td>
@ -624,8 +690,10 @@ These files go in the root folder next to `src/`.
</td>
<td>
Unit test configuration for the [Karma test runner](https://karma-runner.github.io),
used when running `ng test`.
used when running `ng test`.
</td>
</tr>
@ -637,8 +705,10 @@ These files go in the root folder next to `src/`.
</td>
<td>
`npm` configuration listing the third party packages your project uses.
You can also add your own [custom scripts](https://docs.npmjs.com/misc/scripts) here.
You can also add your own [custom scripts](https://docs.npmjs.com/misc/scripts) here.
</td>
</tr>
@ -650,8 +720,10 @@ These files go in the root folder next to `src/`.
</td>
<td>
End-to-end test configuration for [Protractor](http://www.protractortest.org/),
used when running `ng e2e`.
used when running `ng e2e`.
</td>
</tr>
@ -663,9 +735,11 @@ These files go in the root folder next to `src/`.
</td>
<td>
Basic documentation for your project, pre-filled with CLI command information.
Make sure to enhance it with project documentation so that anyone
checking out the repo can build your app!
Make sure to enhance it with project documentation so that anyone
checking out the repo can build your app!
</td>
</tr>
@ -677,6 +751,8 @@ These files go in the root folder next to `src/`.
</td>
<td>
TypeScript compiler configuration for your IDE to pick up and give you helpful tooling.
</td>
@ -689,9 +765,11 @@ These files go in the root folder next to `src/`.
</td>
<td>
Linting configuration for [TSLint](https://palantir.github.io/tslint/) together with
[Codelyzer](http://codelyzer.com/), used when running `ng lint`.
Linting helps keep your code style consistent.
[Codelyzer](http://codelyzer.com/), used when running `ng lint`.
Linting helps keep your code style consistent.
</td>
@ -703,10 +781,12 @@ These files go in the root folder next to `src/`.
~~~ {.l-sub-section}
### Next Step
If you're new to Angular, continue on the
[learning path](guide/guide/learning-angular).
[learning path](guide/guide/learning-angular "Angular learning path").
You can skip the "Setup" step since you're already using the Angular CLI setup.
~~~

View File

@ -5,28 +5,38 @@ Component Interaction
Share information between different directives and components.
@description
<a id="top"></a>This cookbook contains recipes for common component communication scenarios
in which two or more components share information.
<a id="toc"></a># Contents
{@a top}
This cookbook contains recipes for common component communication scenarios
in which two or more components share information.
{@a toc}
# Contents
* [Pass data from parent to child with input binding](guide/component-communication#parent-to-child)
* [Intercept input property changes with a setter](guide/component-communication#parent-to-child-setter)
* [Intercept input property changes with `ngOnChanges()`](guide/component-communication#parent-to-child-on-changes)
* [Parent calls an `@ViewChild()`](guide/component-communication#parent-to-view-child)
* [Parent and children communicate via a service](guide/component-communication#bidirectional-service)
- [Pass data from parent to child with input binding](guide/component-communication#parent-to-child)
- [Intercept input property changes with a setter](guide/component-communication#parent-to-child-setter)
- [Intercept input property changes with `ngOnChanges()`](guide/component-communication#parent-to-child-on-changes)
- [Parent calls an `@ViewChild()`](guide/component-communication#parent-to-view-child)
- [Parent and children communicate via a service](guide/component-communication#bidirectional-service)
**See the <live-example name="cb-component-communication"></live-example>**.
<a id="parent-to-child"></a>## Pass data from parent to child with input binding
{@a parent-to-child}
## Pass data from parent to child with input binding
`HeroChildComponent` has two ***input properties***,
typically adorned with [@Input decorations](guide/template-syntax).
typically adorned with [@Input decorations](guide/template-syntax#inputs-outputs).
<code-example path="cb-component-communication/src/app/hero-child.component.ts">
<code-example path="cb-component-communication/src/app/hero-child.component.ts" title="cb-component-communication/src/app/hero-child.component.ts">
</code-example>
The second `@Input` aliases the child component property name `masterName` as `'master'`.
The `HeroParentComponent` nests the child `HeroChildComponent` inside an `*ngFor` repeater,
@ -34,29 +44,37 @@ binding its `master` string property to the child's `master` alias,
and each iteration's `hero` instance to the child's `hero` property.
<code-example path="cb-component-communication/src/app/hero-parent.component.ts">
<code-example path="cb-component-communication/src/app/hero-parent.component.ts" title="cb-component-communication/src/app/hero-parent.component.ts">
</code-example>
The running application displays three heroes:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child"> </img>
<img src="assets/images/cookbooks/component-communication/parent-to-child.png" alt="Parent-to-child"></img>
</figure>
### Test it
E2E test that all children were instantiated and displayed as expected:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child">
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child" title="cb-component-communication/e2e-spec.ts">
</code-example>
[Back to top](guide/component-communication#top)
<a id="parent-to-child-setter"></a>## Intercept input property changes with a setter
{@a parent-to-child-setter}
## Intercept input property changes with a setter
Use an input property setter to intercept and act upon a value from the parent.
@ -64,122 +82,154 @@ The setter of the `name` input property in the child `NameChildComponent`
trims the whitespace from a name and replaces an empty value with default text.
<code-example path="cb-component-communication/src/app/name-child.component.ts">
<code-example path="cb-component-communication/src/app/name-child.component.ts" title="cb-component-communication/src/app/name-child.component.ts">
</code-example>
Here's the `NameParentComponent` demonstrating name variations including a name with all spaces:
<code-example path="cb-component-communication/src/app/name-parent.component.ts">
<code-example path="cb-component-communication/src/app/name-parent.component.ts" title="cb-component-communication/src/app/name-parent.component.ts">
</code-example>
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter"> </img>
<img src="assets/images/cookbooks/component-communication/setter.png" alt="Parent-to-child-setter"></img>
</figure>
### Test it
E2E tests of input property setter with empty and non-empty names:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter">
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-setter" title="cb-component-communication/e2e-spec.ts">
</code-example>
[Back to top](guide/component-communication#top)
<a id="parent-to-child-on-changes"></a>## Intercept input property changes with *ngOnChanges()*
{@a parent-to-child-on-changes}
## Intercept input property changes with *ngOnChanges()*
Detect and act upon changes to input property values with the `ngOnChanges()` method of the `OnChanges` lifecycle hook interface.
~~~ {.l-sub-section}
You may prefer this approach to the property setter when watching multiple, interacting input properties.
Learn about `ngOnChanges()` in the [LifeCycle Hooks](guide/lifecycle-hooks) chapter.
~~~
This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
<code-example path="cb-component-communication/src/app/version-child.component.ts">
<code-example path="cb-component-communication/src/app/version-child.component.ts" title="cb-component-communication/src/app/version-child.component.ts">
</code-example>
The `VersionParentComponent` supplies the `minor` and `major` values and binds buttons to methods that change them.
<code-example path="cb-component-communication/src/app/version-parent.component.ts">
<code-example path="cb-component-communication/src/app/version-parent.component.ts" title="cb-component-communication/src/app/version-parent.component.ts">
</code-example>
Here's the output of a button-pushing sequence:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"> </img>
<img src="assets/images/cookbooks/component-communication/parent-to-child-on-changes.gif" alt="Parent-to-child-onchanges"></img>
</figure>
### Test it
Test that ***both*** input properties are set initially and that button clicks trigger
the expected `ngOnChanges` calls and values:
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges">
<code-example path="cb-component-communication/e2e-spec.ts" region="parent-to-child-onchanges" title="cb-component-communication/e2e-spec.ts">
</code-example>
[Back to top](guide/component-communication#top)
<a id="child-to-parent"></a>## Parent listens for child event
{@a child-to-parent}
## Parent listens for child event
The child component exposes an `EventEmitter` property with which it `emits` events when something happens.
The parent binds to that event property and reacts to those events.
The child's `EventEmitter` property is an ***output property***,
typically adorned with an [@Output decoration](guide/template-syntax)
typically adorned with an [@Output decoration](guide/template-syntax#inputs-outputs)
as seen in this `VoterComponent`:
<code-example path="cb-component-communication/src/app/voter.component.ts">
<code-example path="cb-component-communication/src/app/voter.component.ts" title="cb-component-communication/src/app/voter.component.ts">
</code-example>
Clicking a button triggers emission of a `true` or `false`, the boolean *payload*.
The parent `VoteTakerComponent` binds an event handler called `onVoted()` that responds to the child event
payload `$event` and updates a counter.
<code-example path="cb-component-communication/src/app/votetaker.component.ts">
<code-example path="cb-component-communication/src/app/votetaker.component.ts" title="cb-component-communication/src/app/votetaker.component.ts">
</code-example>
The framework passes the event argument&mdash;represented by `$event`&mdash;to the handler method,
and the method processes it:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent"> </img>
<img src="assets/images/cookbooks/component-communication/child-to-parent.gif" alt="Child-to-parent"></img>
</figure>
### Test it
Test that clicking the *Agree* and *Disagree* buttons update the appropriate counters:
<code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent">
<code-example path="cb-component-communication/e2e-spec.ts" region="child-to-parent" title="cb-component-communication/e2e-spec.ts">
</code-example>
[Back to top](guide/component-communication#top)
## Parent interacts with child via *local variable*
A parent component cannot use data binding to read child properties
@ -188,22 +238,26 @@ by creating a template reference variable for the child element
and then reference that variable *within the parent template*
as seen in the following example.
<a id="countdown-timer-example"></a>
{@a countdown-timer-example}
The following is a child `CountdownTimerComponent` that repeatedly counts down to zero and launches a rocket.
It has `start` and `stop` methods that control the clock and it displays a
countdown status message in its own template.
<code-example path="cb-component-communication/src/app/countdown-timer.component.ts">
<code-example path="cb-component-communication/src/app/countdown-timer.component.ts" title="cb-component-communication/src/app/countdown-timer.component.ts">
</code-example>
The `CountdownLocalVarParentComponent` that hosts the timer component is as follows:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv">
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="lv" title="cb-component-communication/src/app/countdown-parent.component.ts">
</code-example>
The parent component cannot data bind to the child's
`start` and `stop` methods nor to its `seconds` property.
@ -218,12 +272,14 @@ Here we see the parent and child working together.
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/countdown-timer-anim.gif" alt="countdown timer"> </img>
<img src="assets/images/cookbooks/component-communication/countdown-timer-anim.gif" alt="countdown timer"></img>
</figure>
{@a countdown-tests}
### Test it
Test that the seconds displayed in the parent template
@ -231,13 +287,17 @@ match the seconds displayed in the child's status message.
Test also that clicking the *Stop* button pauses the countdown timer:
<code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests">
<code-example path="cb-component-communication/e2e-spec.ts" region="countdown-timer-tests" title="cb-component-communication/e2e-spec.ts">
</code-example>
[Back to top](guide/component-communication#top)
<a id="parent-to-view-child"></a>## Parent calls an _@ViewChild()_
{@a parent-to-view-child}
## Parent calls an _@ViewChild()_
The *local variable* approach is simple and easy. But it is limited because
the parent-child wiring must be done entirely within the parent template.
@ -256,17 +316,23 @@ The child [CountdownTimerComponent](guide/component-communication#countdown-time
~~~ {.l-sub-section}
The switch from the *local variable* to the *ViewChild* technique
is solely for the purpose of demonstration.
~~~
Here is the parent, `CountdownViewChildParentComponent`:
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc">
<code-example path="cb-component-communication/src/app/countdown-parent.component.ts" region="vc" title="cb-component-communication/src/app/countdown-parent.component.ts">
</code-example>
It takes a bit more work to get the child view into the parent component *class*.
First, you have to import references to the `ViewChild` decorator and the `AfterViewInit` lifecycle hook.
@ -293,9 +359,13 @@ Use `setTimeout()` to wait one tick and then revise the `seconds()` method so
that it takes future values from the timer component.
### Test it
Use [the same countdown timer tests](guide/component-communication#countdown-tests) as before.[Back to top](guide/component-communication#top)
Use [the same countdown timer tests](guide/component-communication#countdown-tests) as before.
<a id="bidirectional-service"></a>## Parent and children communicate via a service
[Back to top](guide/component-communication#top)
{@a bidirectional-service}
## Parent and children communicate via a service
A parent component and its children share a service whose interface enables bi-directional communication
*within the family*.
@ -306,23 +376,27 @@ Components outside this component subtree have no access to the service or their
This `MissionService` connects the `MissionControlComponent` to multiple `AstronautComponent` children.
<code-example path="cb-component-communication/src/app/mission.service.ts">
<code-example path="cb-component-communication/src/app/mission.service.ts" title="cb-component-communication/src/app/mission.service.ts">
</code-example>
The `MissionControlComponent` both provides the instance of the service that it shares with its children
(through the `providers` metadata array) and injects that instance into itself through its constructor:
<code-example path="cb-component-communication/src/app/missioncontrol.component.ts">
<code-example path="cb-component-communication/src/app/missioncontrol.component.ts" title="cb-component-communication/src/app/missioncontrol.component.ts">
</code-example>
The `AstronautComponent` also injects the service in its constructor.
Each `AstronautComponent` is a child of the `MissionControlComponent` and therefore receives its parent's service instance:
<code-example path="cb-component-communication/src/app/astronaut.component.ts">
<code-example path="cb-component-communication/src/app/astronaut.component.ts" title="cb-component-communication/src/app/astronaut.component.ts">
</code-example>
@ -330,6 +404,8 @@ Each `AstronautComponent` is a child of the `MissionControlComponent` and theref
~~~ {.l-sub-section}
Notice that this example captures the `subscription` and `unsubscribe()` when the `AstronautComponent` is destroyed.
This is a memory-leak guard step. There is no actual risk in this app because the
lifetime of a `AstronautComponent` is the same as the lifetime of the app itself.
@ -340,23 +416,29 @@ it controls the lifetime of the `MissionService`.
~~~
The *History* log demonstrates that messages travel in both directions between
the parent `MissionControlComponent` and the `AstronautComponent` children,
facilitated by the service:
<figure class='image-display'>
<img src="assets/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service"> </img>
<img src="assets/images/cookbooks/component-communication/bidirectional-service.gif" alt="bidirectional-service"></img>
</figure>
### Test it
Tests click buttons of both the parent `MissionControlComponent` and the `AstronautComponent` children
and verify that the history meets expectations:
<code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service">
<code-example path="cb-component-communication/e2e-spec.ts" region="bidirectional-service" title="cb-component-communication/e2e-spec.ts">
</code-example>
[Back to top](guide/component-communication#top)

View File

@ -6,6 +6,8 @@ Learn how to apply CSS styles to components.
@description
Angular applications are styled with standard CSS. That means you can apply
everything you know about CSS stylesheets, selectors, rules, and media queries
directly to Angular applications.
@ -26,6 +28,8 @@ This page describes how to load and apply these component styles.
You can run the <live-example></live-example> in Plunker and download the code from there.
## Using component styles
For every Angular component you write, you may define not only an HTML template,
@ -37,10 +41,12 @@ The `styles` property takes an array of strings that contain CSS code.
Usually you give it one string, as in the following example:
<code-example path="component-styles/src/app/hero-app.component.ts" linenums="false">
<code-example path="component-styles/src/app/hero-app.component.ts" title="src/app/hero-app.component.ts" linenums="false">
</code-example>
The selectors you put into a component's styles apply only within the template
of that component. The `h1` selector in the preceding example applies only to the `<h1>` tag
in the template of `HeroAppComponent`. Any `<h1>` elements elsewhere in
@ -60,6 +66,8 @@ This is a big improvement in modularity compared to how CSS traditionally works.
{@a special-selectors}
## Special selectors
Component styles have a few special *selectors* from the world of shadow DOM style scoping
@ -73,10 +81,12 @@ Use the `:host` pseudo-class selector to target styles in the element that *host
targeting elements *inside* the component's template).
<code-example path="component-styles/src/app/hero-details.component.css" region="host" linenums="false">
<code-example path="component-styles/src/app/hero-details.component.css" region="host" title="src/app/hero-details.component.css" linenums="false">
</code-example>
The `:host` selector is the only way to target the host element. You can't reach
the host element from inside the component with other selectors because it's not part of the
component's own template. The host element is in a parent component's template.
@ -87,10 +97,12 @@ including another selector inside parentheses after `:host`.
The next example targets the host element again, but only when it also has the `active` CSS class.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" linenums="false">
<code-example path="component-styles/src/app/hero-details.component.css" region="hostfunction" title="src/app/hero-details.component.css" linenums="false">
</code-example>
### :host-context
Sometimes it's useful to apply styles based on some condition *outside* of a component's view.
@ -105,10 +117,12 @@ The following example applies a `background-color` style to all `<h2>` elements
if some ancestor element has the CSS class `theme-light`.
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" linenums="false">
<code-example path="component-styles/src/app/hero-details.component.css" region="hostcontext" title="src/app/hero-details.component.css" linenums="false">
</code-example>
### /deep/
Component styles normally apply only to the HTML in the component's own template.
@ -120,15 +134,19 @@ children and content children of the component.
The following example targets all `<h3>` elements, from the host element down
through this component to all of its child elements in the DOM.
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" linenums="false">
<code-example path="component-styles/src/app/hero-details.component.css" region="deep" title="src/app/hero-details.component.css" linenums="false">
</code-example>
The `/deep/` selector also has the alias `>>>`. You can use either interchangeably.
~~~ {.alert.is-important}
Use the `/deep/` and `>>>` selectors only with *emulated* view encapsulation.
Emulated is the default and most commonly used view encapsulation. For more information, see the
[Controlling view encapsulation](guide/component-styles#view-encapsulation) section.
@ -140,9 +158,12 @@ Emulated is the default and most commonly used view encapsulation. For more info
{@a loading-styles}
## Loading styles into components
There are several ways to add styles to a component:
* By setting `styles` or `styleUrls` metadata.
* Inline in the template HTML.
* With CSS imports.
@ -155,17 +176,19 @@ You can add a `styles` array property to the `@Component` decorator.
Each string in the array (usually just one string) defines the CSS.
<code-example path="component-styles/src/app/hero-app.component.ts">
<code-example path="component-styles/src/app/hero-app.component.ts" title="src/app/hero-app.component.ts">
</code-example>
### Style URLs in metadata
You can load styles from external CSS files by adding a `styleUrls` attribute
into a component's `@Component` decorator:
<code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls">
<code-example path="component-styles/src/app/hero-details.component.ts" region="styleurls" title="src/app/hero-details.component.ts">
</code-example>
@ -173,6 +196,8 @@ into a component's `@Component` decorator:
~~~ {.alert.is-important}
The URL is relative to the *application root*, which is usually the
location of the `index.html` web page that hosts the application.
The style file URL is *not* relative to the component file.
@ -186,6 +211,8 @@ To specify a URL relative to the component file, see [Appendix 2](guide/componen
~~~ {.l-sub-section}
If you use module bundlers like Webpack, you can also use the `styles` attribute
to load styles from external files at build time. You could write:
@ -200,16 +227,20 @@ For information on loading CSS in this manner, refer to the module bundler's doc
~~~
### Template inline styles
You can embed styles directly into the HTML template by putting them
inside `<style>` tags.
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles">
<code-example path="component-styles/src/app/hero-controls.component.ts" region="inlinestyles" title="src/app/hero-controls.component.ts">
</code-example>
### Template link tags
You can also embed `<link>` tags into the component's HTML template.
@ -218,19 +249,23 @@ As with `styleUrls`, the link tag's `href` URL is relative to the
application root, not the component file.
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink">
<code-example path="component-styles/src/app/hero-team.component.ts" region="stylelink" title="src/app/hero-team.component.ts">
</code-example>
### CSS @imports
You can also import CSS files into the CSS files using the standard CSS `@import` rule.
For details, see [`@import`](https://developer.mozilla.org/en/docs/Web/CSS/@import)
on the [MDN](https://developer.mozilla.org) site.
In this case, the URL is relative to the CSS file into which you're importing.
<code-example path="component-styles/src/app/hero-details.component.css" region="import">
<code-example path="component-styles/src/app/hero-details.component.css" region="import" title="src/app/hero-details.component.css (excerpt)">
</code-example>
@ -238,6 +273,8 @@ In this case, the URL is relative to the CSS file into which you're importing.
{@a view-encapsulation}
## Controlling view encapsulation: native, emulated, and none
As discussed earlier, component CSS styles are encapsulated into the component's view and don't
@ -252,9 +289,11 @@ Choose from the following modes:
on the [MDN](https://developer.mozilla.org) site)
to attach a shadow DOM to the component's host element, and then puts the component
view inside that shadow DOM. The component's styles are included within the shadow DOM.
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
(and renaming) the CSS code to effectively scope the CSS to the component's view.
For details, see [Appendix 1](guide/component-styles#inspect-generated-css).
* `None` means that Angular does no view encapsulation.
Angular adds the CSS to the global styles.
The scoping rules, isolations, and protections discussed earlier don't apply.
@ -263,10 +302,12 @@ Choose from the following modes:
To set the components encapsulation mode, use the `encapsulation` property in the component metadata:
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" linenums="false">
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" title="src/app/quest-summary.component.ts" linenums="false">
</code-example>
`Native` view encapsulation only works on browsers that have native support
for shadow DOM (see [Shadow DOM v0](http://caniuse.com/#feat=shadowdom) on the
[Can I use](http://caniuse.com) site). The support is still limited,
@ -276,6 +317,8 @@ in most cases.
{@a inspect-generated-css}
## Appendix 1: Inspecting the CSS generated in emulated view encapsulation
When using emulated view encapsulation, Angular preprocesses
@ -288,15 +331,18 @@ attached to it:
<code-example format="">
&lt;hero-details _nghost-pmm-5>
&lt;h2 _ngcontent-pmm-5>Mister Fantastic&lt;/h2>
&lt;hero-team _ngcontent-pmm-5 _nghost-pmm-6>
&lt;h3 _ngcontent-pmm-6>Team&lt;/h3>
&lt;/hero-team>
&lt;/hero-detail>
&lt;h2 _ngcontent-pmm-5>Mister Fantastic&lt;/h2>
&lt;hero-team _ngcontent-pmm-5 _nghost-pmm-6>
&lt;h3 _ngcontent-pmm-6>Team&lt;/h3>
&lt;/hero-team>
&lt;/hero-detail>
</code-example>
There are two kinds of generated attributes:
* An element that would be a shadow DOM host in native encapsulation has a
generated `_nghost` attribute. This is typically the case for component host elements.
* An element within a component's view has a `_ngcontent` attribute
@ -309,17 +355,19 @@ by the generated component styles, which are in the `<head>` section of the DOM:
<code-example format="">
[_nghost-pmm-5] {
display: block;
border: 1px solid black;
}
display: block;
border: 1px solid black;
}
h3[_ngcontent-pmm-6] {
background-color: white;
border: 1px solid #777;
}
h3[_ngcontent-pmm-6] {
background-color: white;
border: 1px solid #777;
}
</code-example>
These styles are post-processed so that each selector is augmented
with `_nghost` or `_ngcontent` attribute selectors.
These extra selectors enable the scoping rules described in this page.
@ -327,24 +375,30 @@ These extra selectors enable the scoping rules described in this page.
{@a relative-urls}
## Appendix 2: Loading styles with relative URLs
It's common practice to split a component's code, HTML, and CSS into three separate files in the same directory:
<code-example format="nocode">
quest-summary.component.ts
quest-summary.component.html
quest-summary.component.css
quest-summary.component.html
quest-summary.component.css
</code-example>
You include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively.
Because these files are co-located with the component,
it would be nice to refer to them by name without also having to specify a path back to the root of the application.
You can use a relative URL by prefixing your filenames with `./`:
<code-example path="component-styles/src/app/quest-summary.component.ts">
<code-example path="component-styles/src/app/quest-summary.component.ts" title="src/app/quest-summary.component.ts">
</code-example>

View File

@ -6,53 +6,67 @@ Angular's dependency injection system creates and delivers dependent services "j
@description
**Dependency injection** is an important application design pattern.
Angular has its own dependency injection framework, and
you really can't build an Angular application without it.
It's used so widely that almost everyone just calls it _DI_.
This page covers what DI is, why it's so useful,
and [how to use it](guide/dependency-injection#angular-di) in an Angular app.# Contents
and [how to use it](guide/dependency-injection#angular-di) in an Angular app.
- [Why dependency injection?](guide/dependency-injection#why-di)
- [Angular dependency injection](guide/dependency-injection#angular-dependency-injection)
- [Configuring the injector](guide/dependency-injection#injector-config)
- [Registering providers in an `NgModule`](guide/dependency-injection#register-providers-ngmodule)
- [Registering providers in a component](guide/dependency-injection#register-providers-component)
- [When to use `NgModule` versus an application component](guide/dependency-injection#ngmodule-vs-comp)
- [Preparing the `HeroListComponent` for injection](guide/dependency-injection#prep-for-injection)
- [Implicit injector creation](guide/dependency-injection#di-metadata)
- [Singleton services](guide/dependency-injection#singleton-services)
- [Testing the component](guide/dependency-injection#testing-the-component)
- [When the service needs a service](guide/dependency-injection#service-needs-service)
- [Why `@Injectable()`?](guide/dependency-injection#injectable)
- [Creating and registering a logger service](guide/dependency-injection#logger-service)
- [Injector providers](guide/dependency-injection#injector-providers)
- [The `Provider` class and `provide` object literal](guide/dependency-injection#provide)
- [Alternative class providers](guide/dependency-injection#class-provider)
- [Class provider with dependencies](guide/dependency-injection#class-provider-dependencies)
- [Aliased class providers](guide/dependency-injection#aliased-class-providers)
- [Value providers](guide/dependency-injection#value-provider)
- [Factory providers](guide/dependency-injection#factory-provider)
- [Dependency injection tokens](guide/dependency-injection#dependency-injection-tokens)
- [Non-class dependencies](guide/dependency-injection#non-class-dependencies)
- [`OpaqueToken`](guide/dependency-injection#opaquetoken)
- [Optional dependencies](guide/dependency-injection#optional)
- [Summary](guide/dependency-injection#summary)
- [Appendix: Working with injectors directly](guide/dependency-injection#explicit-injector)
# Contents
* [Why dependency injection?](guide/dependency-injection#why-di)
* [Angular dependency injection](guide/dependency-injection#angular-dependency-injection)
* [Configuring the injector](guide/dependency-injection#injector-config)
* [Registering providers in an `NgModule`](guide/dependency-injection#register-providers-ngmodule)
* [Registering providers in a component](guide/dependency-injection#register-providers-component)
* [When to use `NgModule` versus an application component](guide/dependency-injection#ngmodule-vs-comp)
* [Preparing the `HeroListComponent` for injection](guide/dependency-injection#prep-for-injection)
* [Implicit injector creation](guide/dependency-injection#di-metadata)
* [Singleton services](guide/dependency-injection#singleton-services)
* [Testing the component](guide/dependency-injection#testing-the-component)
* [When the service needs a service](guide/dependency-injection#service-needs-service)
* [Why `@Injectable()`?](guide/dependency-injection#injectable)
* [Creating and registering a logger service](guide/dependency-injection#logger-service)
* [Injector providers](guide/dependency-injection#injector-providers)
* [The `Provider` class and `provide` object literal](guide/dependency-injection#provide)
* [Alternative class providers](guide/dependency-injection#class-provider)
* [Class provider with dependencies](guide/dependency-injection#class-provider-dependencies)
* [Aliased class providers](guide/dependency-injection#aliased-class-providers)
* [Value providers](guide/dependency-injection#value-provider)
* [Factory providers](guide/dependency-injection#factory-provider)
* [Dependency injection tokens](guide/dependency-injection#dependency-injection-tokens)
* [Non-class dependencies](guide/dependency-injection#non-class-dependencies)
* [`InjectionToken`](guide/dependency-injection#injection-token)
* [Optional dependencies](guide/dependency-injection#optional)
* [Summary](guide/dependency-injection#summary)
* [Appendix: Working with injectors directly](guide/dependency-injection#explicit-injector)
Run the <live-example></live-example>.
## Why dependency injection?
To understand why dependency injection is so important, consider an example without it.
Imagine writing the following code:
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car">
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car" title="src/app/car/car.ts (without DI)">
</code-example>
The `Car` class creates everything it needs inside its constructor.
What's the problem?
The problem is that the `Car` class is brittle, inflexible, and hard to test.
@ -95,7 +109,7 @@ When you can't control the dependencies, a class becomes difficult to test.
How can you make `Car` more robust, flexible, and testable?
<a id="ctor-injection"></a>
{@a ctor-injection}
That's super easy. Change the `Car` constructor to a version with DI:
@ -111,6 +125,8 @@ That's super easy. Change the `Car` constructor to a version with DI:
</code-tabs>
See what happened? The definition of the dependencies are
now in the constructor.
The `Car` class no longer creates an `engine` or `tires`.
@ -119,12 +135,16 @@ It just consumes them.
~~~ {.l-sub-section}
This example leverages TypeScript's constructor syntax for declaring
parameters and properties simultaneously.
~~~
Now you can create a car by passing the engine and tires to the constructor.
@ -132,6 +152,8 @@ Now you can create a car by passing the engine and tires to the constructor.
</code-example>
How cool is that?
The definition of the `engine` and `tire` dependencies are
decoupled from the `Car` class.
@ -143,6 +165,8 @@ Now, if someone extends the `Engine` class, that is not `Car`'s problem.
~~~ {.l-sub-section}
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
something like this:
@ -151,12 +175,16 @@ something like this:
</code-example>
The critical point is this: the `Car` class did not have to change.
You'll take care of the consumer's problem shortly.
~~~
The `Car` class is much easier to test now because you are in complete control
of its dependencies.
You can pass mocks to the constructor that do exactly what you want them to do
@ -167,6 +195,8 @@ during each test:
</code-example>
**You just learned what dependency injection is**.
It's a coding pattern in which a class receives its dependencies from external
@ -181,10 +211,12 @@ You need something that takes care of assembling these parts.
You _could_ write a giant class to do that:
<code-example path="dependency-injection/src/app/car/car-factory.ts">
<code-example path="dependency-injection/src/app/car/car-factory.ts" title="src/app/car/car-factory.ts">
</code-example>
It's not so bad now with only three creation methods.
But maintaining it will be hairy as the application grows.
This factory is going to become a huge spiderweb of
@ -200,10 +232,12 @@ You register some classes with this injector, and it figures out how to create t
When you need a `Car`, you simply ask the injector to get it for you and you're good to go.
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" linenums="false">
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" title="src/app/car/car-injector.ts" linenums="false">
</code-example>
Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
The consumer knows nothing about creating a `Car`.
You don't have a gigantic factory class to maintain.
@ -214,6 +248,8 @@ This is what a **dependency injection framework** is all about.
Now that you know what dependency injection is and appreciate its benefits,
read on to see how it is implemented in Angular.
## Angular dependency injection
Angular ships with its own dependency injection framework. This framework can also be used
@ -244,10 +280,14 @@ that from the [The Tour of Heroes](tutorial/).
</code-tabs>
The `HeroesComponent` is the root component of the *Heroes* feature area.
It governs all the child components of this area.
This stripped down version has only one child, `HeroListComponent`,
which displays a list of heroes.
Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection
defined in another file.
That may suffice in the early stages of development, but it's far from ideal.
@ -260,6 +300,8 @@ It's better to make a service that hides how the app gets hero data.
~~~ {.l-sub-section}
Given that the service is a
[separate concern](https://en.wikipedia.org/wiki/Separation_of_concerns),
consider writing the service code in its own file.
@ -268,18 +310,24 @@ See [this note](guide/dependency-injection#one-class-per-file) for details.
~~~
The following `HeroService` exposes a `getHeroes` method that returns
the same mock data as before, but none of its consumers need to know that.
<code-example path="dependency-injection/src/app/heroes/hero.service.1.ts">
<code-example path="dependency-injection/src/app/heroes/hero.service.1.ts" title="src/app/heroes/hero.service.ts">
</code-example>
~~~ {.l-sub-section}
The `@Injectable()` decorator above the service class is
covered [shortly](guide/dependency-injection#injectable).
@ -290,6 +338,8 @@ covered [shortly](guide/dependency-injection#injectable).
~~~ {.l-sub-section}
Of course, this isn't a real service.
If the app were actually getting data from a remote server, the API would have to be
asynchronous, perhaps returning a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).
@ -299,6 +349,8 @@ This is important in general, but not in this example.
~~~
A service is nothing more than a class in Angular.
It remains nothing more than a class until you register it with an Angular injector.
@ -310,6 +362,8 @@ It remains nothing more than a class until you register it with an Angular injec
{@a injector-config}
### Configuring the injector
You don't have to create an Angular injector.
@ -320,13 +374,19 @@ Angular creates an application-wide injector for you during the bootstrap proces
</code-example>
You do have to configure the injector by registering the **providers**
that create the services the application requires.
This guide explains what [providers](guide/dependency-injection#providers) are later.
You can either register a provider within an [NgModule](guide/ngmodule) or in application components.
{@a register-providers-ngmodule}
### Registering providers in an _NgModule_
Here's the `AppModule` that registers two providers, `UserService` and an `APP_CONFIG` provider,
in its `providers` array.
@ -336,24 +396,30 @@ in its `providers` array.
</code-example>
Because the `HeroService` is used _only_ within the `HeroesComponent`
and its subcomponents, the top-level `HeroesComponent` is the ideal
place to register it.
{@a register-providers-component}
### Registering providers in a component
Here's a revised `HeroesComponent` that registers the `HeroService` in its `providers` array.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full" linenums="false">
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="full" title="src/app/heroes/heroes.component.ts" linenums="false">
</code-example>
{@a ngmodule-vs-comp}
### When to use _NgModule_ versus an application component
On the one hand, a provider in an `NgModule` is registered in the root injector. That means that every provider
@ -371,8 +437,10 @@ the `HeroesComponent`.
~~~ {.l-sub-section}
Also see *"Should I add app-wide providers to the root `AppModule` or
the root `AppComponent`?"* in the [NgModule FAQ](cookbook/ngmodule-faq).
the root `AppComponent`?"* in the [NgModule FAQ](cookbook/ngmodule-faq#q-root-component-or-module).
~~~
@ -380,6 +448,8 @@ the root `AppComponent`?"* in the [NgModule FAQ](cookbook/ngmodule-faq).
{@a prep-for-injection}
### Preparing the _HeroListComponent_ for injection
The `HeroListComponent` should get heroes from the injected `HeroService`.
@ -404,15 +474,19 @@ It's a small change:
~~~ {.l-sub-section}
#### Focus on the constructor
Adding a parameter to the constructor isn't all that's happening here.
<code-example path="dependency-injection/src/app/heroes/hero-list.component.2.ts" region="ctor" linenums="false">
<code-example path="dependency-injection/src/app/heroes/hero-list.component.2.ts" region="ctor" title="src/app/heroes/hero-list.component.ts" linenums="false">
</code-example>
Note that the constructor parameter has the type `HeroService`, and that
the `HeroListComponent` class has an `@Component` decorator
(scroll up to confirm that fact).
@ -430,6 +504,8 @@ Angular injector to inject an instance of
{@a di-metadata}
### Implicit injector creation
You saw how to use an injector to create a new
@ -438,10 +514,12 @@ You _could_ create such an injector
explicitly:
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" linenums="false">
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-create-and-call" title="src/app/car/car-injector.ts" linenums="false">
</code-example>
You won't find code like that in the Tour of Heroes or any of the other
documentation samples.
You *could* write code that [explicitly creates an injector](guide/dependency-injection#explicit-injector) if you *had* to,
@ -453,6 +531,8 @@ If you let Angular do its job, you'll enjoy the benefits of automated dependency
{@a singleton-services}
### Singleton services
Dependencies are singletons within the scope of an injector.
@ -465,6 +545,8 @@ For more information, see [Hierarchical Injectors](guide/hierarchical-dependency
{@a testing-the-component}
### Testing the component
Earlier you saw that designing a class for dependency injection makes the class easier to test.
@ -474,7 +556,7 @@ For example, you can create a new `HeroListComponent` with a mock service that y
under test:
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" linenums="false">
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" title="src/app/test.component.ts" linenums="false">
</code-example>
@ -482,6 +564,8 @@ under test:
~~~ {.l-sub-section}
Learn more in [Testing](guide/testing).
@ -490,6 +574,8 @@ Learn more in [Testing](guide/testing).
{@a service-needs-service}
### When the service needs a service
The `HeroService` is very simple. It doesn't have any dependencies of its own.
@ -514,11 +600,15 @@ Here is the revision compared to the original.
</code-tabs>
The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `logger`.
You call that property within the `getHeroes()` method when anyone asks for heroes.
{@a injectable}
### Why _@Injectable()_?
**<a href="../api/core/index/Injectable-decorator.html">@Injectable()</a>** marks a class as available to an
@ -529,6 +619,8 @@ error when trying to instantiate a class that is not marked as
~~~ {.l-sub-section}
As it happens, you could have omitted `@Injectable()` from the first
version of `HeroService` because it had no injected parameters.
But you must have it now that the service has an injected dependency.
@ -548,6 +640,8 @@ in order to inject a `Logger`.
Suggestion: add @Injectable() to every service class
</header>
Consider adding `@Injectable()` to every service class, even those that don't have dependencies
and, therefore, do not technically require it. Here's why:
@ -568,6 +662,8 @@ and, therefore, do not technically require it. Here's why:
~~~
Injectors are also responsible for instantiating components
like `HeroesComponent`. So why doesn't `HeroesComponent` have
`@Injectable()`?
@ -582,6 +678,8 @@ identify a class as a target for instantiation by an injector.
~~~ {.l-sub-section}
At runtime, injectors can read class metadata in the transpiled JavaScript code
and use the constructor parameter type information
to determine what things to inject.
@ -610,6 +708,8 @@ to make the intent clear.
Always include the parentheses
</header>
Always write `@Injectable()`, not just `@Injectable`.
The application will fail mysteriously if you forget the parentheses.
@ -617,19 +717,24 @@ The application will fail mysteriously if you forget the parentheses.
~~~
## Creating and registering a logger service
Inject a logger into `HeroService` in two steps:
1. Create the logger service.
1. Register it with the application.
The logger service is quite simple:
<code-example path="dependency-injection/src/app/logger.service.ts">
<code-example path="dependency-injection/src/app/logger.service.ts" title="src/app/logger.service.ts">
</code-example>
You're likely to need the same logger service everywhere in your application,
so put it in the project's `app` folder and
register it in the `providers` array of the application module, `AppModule`.
@ -639,6 +744,8 @@ register it in the `providers` array of the application module, `AppModule`.
</code-example>
If you forget to register the logger, Angular throws an exception when it first looks for the logger:
<code-example format="nocode">
@ -646,6 +753,8 @@ If you forget to register the logger, Angular throws an exception when it first
</code-example>
That's Angular telling you that the dependency injector couldn't find the *provider* for the logger.
It needed that provider to create a `Logger` to inject into a new
`HeroService`, which it needed to
@ -653,6 +762,8 @@ create and inject into a new `HeroListComponent`.
The chain of creations started with the `Logger` provider. *Providers* are the subject of the next section.
## Injector providers
A provider *provides* the concrete, runtime version of a dependency value.
@ -664,10 +775,12 @@ You must register a service *provider* with the injector, or it won't know how t
Earlier you registered the `Logger` service in the `providers` array of the metadata for the `AppModule` like this:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger" title="src/app/providers.component.ts">
</code-example>
There are many ways to *provide* something that looks and behaves like a `Logger`.
The `Logger` class itself is an obvious and natural provider.
But it's not the only way.
@ -684,22 +797,30 @@ What matters is that the injector has a provider to go to when it needs a `Logge
</div>
### The *Provider* class and _provide_ object literal
You wrote the `providers` array like this:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-1" title="src/app/providers.component.ts">
</code-example>
This is actually a shorthand expression for a provider registration
using a _provider_ object literal with two properties:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3" title="src/app/providers.component.ts">
</code-example>
The first is the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value
and registering the provider.
@ -712,6 +833,8 @@ There are many ways to create dependency values just as there are many ways to w
</div>
### Alternative class providers
Occasionally you'll ask a different class to provide the service.
@ -719,33 +842,39 @@ The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4" title="src/app/providers.component.ts">
</code-example>
{@a class-provider-dependencies}
### Class provider with dependencies
Maybe an `EvenBetterLogger` could display the user name in the log message.
This logger gets the user from the injected `UserService`,
which is also injected at the application level.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" title="src/app/providers.component.ts" linenums="false">
</code-example>
Configure it like `BetterLogger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" title="src/app/providers.component.ts" linenums="false">
</code-example>
{@a aliased-class-providers}
### Aliased class providers
Suppose an old component depends upon an `OldLogger` class.
@ -763,10 +892,12 @@ You certainly do not want two different `NewLogger` instances in your app.
Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" title="src/app/providers.component.ts" linenums="false">
</code-example>
The solution: alias with the `useExisting` option.
@ -777,14 +908,20 @@ The solution: alias with the `useExisting` option.
{@a value-provider}
### Value providers
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" title="src/app/providers.component.ts" linenums="false">
</code-example>
Then you register a provider with the `useValue` option,
which makes this object play the logger role.
@ -793,15 +930,19 @@ which makes this object play the logger role.
</code-example>
See more `useValue` examples in the
[Non-class dependencies](guide/dependency-injection#non-class-dependencies) and
[OpaqueToken](guide/dependency-injection#opaquetoken) sections.
[InjectionToken](guide/dependency-injection#injection-token) sections.
<div id='factory-provider'>
</div>
### Factory providers
Sometimes you need to create the dependent value dynamically,
@ -824,30 +965,36 @@ as when you log in a different user.
Unlike `EvenBetterLogger`, you can't inject the `UserService` into the `HeroService`.
The `HeroService` won't have direct access to the user information to decide
who is authorized and who is not.
Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" linenums="false">
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" title="src/app/heroes/hero.service.ts (excerpt)" linenums="false">
</code-example>
You can inject the `Logger`, but you can't inject the boolean `isAuthorized`.
You'll have to take over the creation of new instances of this `HeroService` with a factory provider.
A factory provider needs a factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" linenums="false">
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
Although the `HeroService` has no access to the `UserService`, the factory function does.
You inject both the `Logger` and the `UserService` into the factory provider
and let the injector pass them along to the factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" linenums="false">
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
@ -855,6 +1002,8 @@ and let the injector pass them along to the factory function:
~~~ {.l-sub-section}
The `useFactory` field tells Angular that the provider is a factory function
whose implementation is the `heroServiceFactory`.
@ -865,6 +1014,8 @@ The injector resolves these tokens and injects the corresponding services into t
~~~
Notice that you captured the factory provider in an exported variable, `heroServiceProvider`.
This extra step makes the factory provider reusable.
You can register the `HeroService` with this variable wherever you need it.
@ -887,6 +1038,8 @@ Here you see the new and the old implementation side-by-side:
</code-tabs>
## Dependency injection tokens
When you register a provider with an injector, you associate that provider with a dependency injection token.
@ -898,48 +1051,58 @@ the class *type* served as its own lookup key.
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" linenums="false">
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" title="src/app/injector.component.ts" linenums="false">
</code-example>
You have similar good fortune when you write a constructor that requires an injected class-based dependency.
When you define a constructor parameter with the `HeroService` class type,
Angular knows to inject the
service associated with that `HeroService` class token:
<code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature">
<code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature" title="src/app/heroes/hero-list.component.ts">
</code-example>
This is especially convenient when you consider that most dependency values are provided by classes.
{@a non-class-dependencies}
### Non-class dependencies
<p>
What if the dependency value isn't a class? Sometimes the thing you want to inject is a
span string, function, or object.
span string, function, or object.
</p>
<p>
Applications often define configuration objects with lots of small facts
(like the title of the application or the address of a web API endpoint)
but these configuration objects aren't always instances of a class.
They can be object literals such as this one:
(like the title of the application or the address of a web API endpoint)
but these configuration objects aren't always instances of a class.
They can be object literals such as this one:
</p>
<code-example path="dependency-injection/src/app/app.config.ts" region="config" linenums="false">
<code-example path="dependency-injection/src/app/app.config.ts" region="config" title="src/app/app-config.ts (excerpt)" linenums="false">
</code-example>
What if you'd like to make this configuration object available for injection?
You know you can register an object with a [value provider](guide/dependency-injection#value-provider).
But what should you use as the token?
You don't have a class to serve as a token.
There is no `AppConfig` class.
@ -947,21 +1110,25 @@ There is no `AppConfig` class.
~~~ {.l-sub-section}
### TypeScript interfaces aren't valid tokens
The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, you
cannot use a TypeScript interface as a token:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" title="src/app/providers.component.ts" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" title="src/app/providers.component.ts" linenums="false">
</code-example>
That seems strange if you're used to dependency injection in strongly typed languages, where
an interface is the preferred dependency lookup key.
@ -974,30 +1141,39 @@ There is no interface type information left for Angular to find at runtime.
{@a opaquetoken}
### _OpaqueToken_
{@a injection-token}
### _InjectionToken_
One solution to choosing a provider token for non-class dependencies is
to define and use an <a href="../api/core/index/OpaqueToken-class.html"><b>OpaqueToken</b></a>.
The definition looks like this:
to define and use an <a href="../api/core/index/InjectionToken-class.html"><b>InjectionToken</b></a>.
The definition of such a token looks like this:
<code-example path="dependency-injection/src/app/app.config.ts" region="token" linenums="false">
<code-example path="dependency-injection/src/app/app.config.ts" region="token" title="src/app/app.config.ts" linenums="false">
</code-example>
Register the dependency provider using the `OpaqueToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
Register the dependency provider using the `InjectionToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" title="src/app/providers.component.ts" linenums="false">
</code-example>
Now you can inject the configuration object into any constructor that needs it, with
the help of an `@Inject` decorator:
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" linenums="false">
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" title="src/app/app.component.ts" linenums="false">
</code-example>
@ -1005,12 +1181,16 @@ the help of an `@Inject` decorator:
~~~ {.l-sub-section}
Although the `AppConfig` interface plays no role in dependency injection,
it supports typing of the configuration object within the class.
~~~
Aternatively, you can provide and inject the configuration object in an ngModule like `AppModule`.
+makeExcerpt('src/app/app.module.ts','ngmodule-providers')
@ -1020,6 +1200,8 @@ Aternatively, you can provide and inject the configuration object in an ngModule
</div>
## Optional dependencies
The `HeroService` *requires* a `Logger`, but what if it could get by without
@ -1038,10 +1220,14 @@ constructor argument with `@Optional()`:
</code-example>
When using `@Optional()`, your code must be prepared for a null value. If you
don't register a `logger` somewhere up the line, the injector will set the
value of `logger` to null.
## Summary
You learned the basics of Angular dependency injection in this page.
@ -1054,16 +1240,20 @@ You can learn more about its advanced features, beginning with its support for
nested injectors, in
[Hierarchical Dependency Injection](guide/hierarchical-dependency-injection).
## Appendix: Working with injectors directly
Developers rarely work directly with an injector, but
here's an `InjectorComponent` that does.
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector">
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector" title="src/app/injector.component.ts">
</code-example>
An `Injector` is itself an injectable service.
In this example, Angular injects the component's own `Injector` into the component's constructor.
@ -1079,6 +1269,8 @@ is not found. Angular can't find the service if it's not registered with this or
~~~ {.l-sub-section}
The technique is an example of the
[service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
@ -1096,6 +1288,8 @@ must acquire services generically and dynamically.
~~~
## Appendix: Why have one class per file
Having multiple classes in the same file is confusing and best avoided.
@ -1110,6 +1304,8 @@ you'll get a runtime null reference error.
~~~ {.l-sub-section}
You actually can define the component first with the help of the `forwardRef()` method as explained
in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
But why flirt with trouble?

View File

@ -5,10 +5,14 @@ Deployment
Learn how to deploy your Angular app.
@description
This page describes tools and techniques for deploy and optimize your Angular application.
{@a toc}
## Table of contents
* [Overview](guide/deployment#overview)
@ -30,6 +34,8 @@ This page describes tools and techniques for deploy and optimize your Angular ap
{@a overview}
## Overview
This guide describes techniques for preparing and deploying an Angular application to a server running remotely.
@ -39,10 +45,10 @@ The techniques progress from _easy but suboptimal_ to _more optimal and more inv
* [_Ahead of Time_ compilation (AOT)](guide/deployment#aot "AOT Compilation") is the first of
[several optimization strategies](guide/deployment#optimize).
You'll also want to read the [detailed instructions in the AOT Cookbook](cookbook/aot-compiler).
You'll also want to read the [detailed instructions in the AOT Cookbook](cookbook/aot-compiler "AOT Cookbook").
* [Webpack](guide/deployment#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT.
The Angular [webpack guide](guide/webpack) can get you started and
The Angular [webpack guide](guide/webpack "Webpack: an introduction") can get you started and
_this_ page provides additional optimization advice, but you'll probably have to learn more about webpack on your own.
* The [Angular configuration](guide/deployment#angular-configuration "Angular configuration") section calls attention to
@ -54,6 +60,8 @@ server-side changes that may be necessary, _no matter how you deploy the applica
{@a dev-deploy}
## Simplest deployment possible
The simplest way to deploy the app is to publish it to a web server
@ -82,6 +90,8 @@ That's the simplest deployment you can do.
~~~ {.alert.is-helpful}
This is _not_ a production deployment. It's not optimized and it won't be fast for users.
It might be good enough for sharing your progress and ideas internally with managers, teammates, and other stakeholders.
Be sure to read about [optimizing for production](guide/deployment#optimize "Optimizing for production") below.
@ -93,6 +103,8 @@ Be sure to read about [optimizing for production](guide/deployment#optimize "Opt
{@a node-modules}
### Load npm package files from the web (SystemJS)
The `node_modules` folder of _npm packages_ contains much more code
@ -113,6 +125,8 @@ with versions that load from the web. It might look like this.
</code-example>
(2) Replace the `systemjs.config.js` script with a script that
loads `systemjs.config.server.js`.
@ -120,6 +134,8 @@ loads `systemjs.config.server.js`.
</code-example>
(3) Add `systemjs.config.server.js` (shown in the code sample below) to the `src/` folder.
This alternative version configures _SystemJS_ to load _UMD_ versions of Angular
(and other third-party packages) from the web.
@ -134,6 +150,8 @@ Notice the `paths` key:
</code-example>
In the standard SystemJS config, the `npm` path points to the `node_modules/`.
In this server config, it points to
<a href="https://unpkg.com/" target="_blank" title="unpkg.com">https://unpkg.com</a>,
@ -183,9 +201,11 @@ The following trivial router sample app shows these changes.
</code-tabs>
Practice with this sample before attempting these techniques on your application.
1. Follow the [setup instructions](guide/setup) for creating a new project
1. Follow the [setup instructions](guide/setup "Angular QuickStart setup") for creating a new project
named <code>simple-deployment</code>.
1. Add the "Simple deployment" sample files shown above.
@ -205,6 +225,8 @@ When you have that working, try the same process on your application.
{@a optimize}
## Optimize for production
Although deploying directly from the development environment works, it's far from optimal.
@ -226,14 +248,14 @@ Does it matter? That depends upon business and technical factors you must evalua
If it _does_ matter, there are tools and techniques to reduce the number of requests and the size of responses.
- Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
- Bundling: concatenates modules into a single file (bundle).
- Inlining: pulls template html and css into the components.
- Minification: removes excess whitespace, comments, and optional tokens.
- Uglification: rewrites code to use short, cryptic variable and function names.
- Dead code elimination: removes unreferenced modules and unused code.
- Pruned libraries: drop unused libraries and pare others down to the features you need.
- Performance measurement: focus on optimizations that make a measurable difference.
* Ahead-of-Time (AOT) Compilation: pre-compiles Angular component templates.
* Bundling: concatenates modules into a single file (bundle).
* Inlining: pulls template html and css into the components.
* Minification: removes excess whitespace, comments, and optional tokens.
* Uglification: rewrites code to use short, cryptic variable and function names.
* Dead code elimination: removes unreferenced modules and unused code.
* Pruned libraries: drop unused libraries and pare others down to the features you need.
* Performance measurement: focus on optimizations that make a measurable difference.
Each tool does something different.
They work best in combination and are mutually reinforcing.
@ -244,28 +266,33 @@ building for production is a single step.
{@a aot}
### Ahead-of-Time (AOT) compilation
The Angular _Ahead-of-Time_ compiler pre-compiles application components and their templates
during the build process.
Apps compiled with AOT launch faster for several reasons.
* Application components execute immediately, without client-side compilation.
* Templates are embedded as code within their components so there is no client-side request for template files.
* You don't download the Angular compiler, which is pretty big on its own.
* The compiler discards unused Angular directives that a tree-shaking tool can then exclude.
Learn more about AOT Compilation in the [AOT Cookbook](cookbook/aot-compiler)
Learn more about AOT Compilation in the [AOT Cookbook](cookbook/aot-compiler "AOT Cookbook")
which describes running the AOT compiler from the command line
and using [_rollup_](guide/deployment#rollup) for bundling, minification, uglification and tree shaking.
{@a webpack}
### Webpack (and AOT)
<a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another
great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application.
The "[Webpack: an introduction](guide/webpack)" guide will get you started
The "[Webpack: an introduction](guide/webpack "Webpack: an introduction")" guide will get you started
using webpack with Angular.
Consider configuring _Webpack_ with the official
@ -277,6 +304,8 @@ and performs AOT compilation &mdash; without any changes to the source code.
{@a rollup}
### Dead code elimination with _rollup_
Any code that you don't call is _dead code_.
@ -294,6 +323,8 @@ this post</a> by rollup-creator, Rich Harris.
{@a prune}
### Pruned libraries
Don't count on automation to remove all dead code.
@ -307,6 +338,8 @@ Other libraries let you import features _a la carte_.
{@a measure}
### Measure performance first
You can make better decisions about what to optimize and how when you have a clear and accurate understanding of
@ -325,15 +358,19 @@ that can also help verify that your deployment was successful.
{@a angular-configuration}
## Angular configuration
Angular configuration can make the difference between whether the app launches quickly or doesn't load at all.
{@a base-tag}
### The `base` tag
The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!#base-href)
The HTML [_&lt;base href="..."/&gt;_](https://angular.io/docs/ts/latest/guide/router.html#!)
specifies a base path for resolving relative URLs to assets such as images, scripts, and style sheets.
For example, given the `<base href="/my/app/">`, the browser resolves a URL such as `some/place/foo.jpg`
into a server request for `my/app/some/place/foo.jpg`.
@ -342,10 +379,14 @@ During navigation, the Angular router uses the _base href_ as the base path to c
~~~ {.l-sub-section}
See also the [*APP_BASE_HREF*](api/common/index/APP_BASE_HREF-let) alternative.
See also the [*APP_BASE_HREF*](api/common/index/APP_BASE_HREF-let "API: APP_BASE_HREF") alternative.
~~~
In development, you typically start the server in the folder that holds `index.html`.
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
@ -358,6 +399,8 @@ for the missing files. Look at where it _tried_ to find those files and adjust t
{@a enable-prod-mode}
### Enable production mode
Angular apps run in development mode by default, as you can see by the following message on the browser
@ -368,26 +411,30 @@ console:
Angular 2 is running in the development mode. Call enableProdMode() to enable the production mode.
</code-example>
Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles.
To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`.
<code-example path="deployment/src/main.ts" region="enableProdMode" linenums="false">
<code-example path="deployment/src/main.ts" region="enableProdMode" title="src/main.ts (enableProdMode)" linenums="false">
</code-example>
{@a lazy-loading}
### Lazy loading
You can dramatically reduce launch time by only loading the application modules that
absolutely must be present when the app starts.
Configure the Angular Router to defer loading of all other modules (and their associated code), either by
[waiting until the app has launched](guide/router)
or by [_lazy loading_](guide/router)
[waiting until the app has launched](guide/router#preloading "Preloading")
or by [_lazy loading_](guide/router#asynchronous-routing "Lazy loading")
them on demand.
#### Don't eagerly import something from a lazy loaded module
@ -411,12 +458,16 @@ automatically recognizes lazy loaded `NgModules` and creates separate bundles fo
{@a server-configuration}
## Server configuration
This section covers changes you may have make to the server or to files deployed to the server.
{@a fallback}
### Routed apps must fallback to `index.html`
Angular apps are perfect candidates for serving with a simple static HTML server.
@ -428,6 +479,8 @@ to return the application's host page (`index.html`) when asked for a file that
{@a deep-link}
A routed application should support "deep links".
A _deep link_ is a URL that specifies a path to a component inside the app.
For example, `http://www.mysite.com/heroes/42` is a _deep link_ to the hero detail page
@ -453,41 +506,46 @@ The list is by no means exhaustive, but should provide you with a good starting
#### Development servers
- [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
* [Lite-Server](https://github.com/johnpapa/lite-server): the default dev server installed with the
[Quickstart repo](https://github.com/angular/quickstart) is pre-configured to fallback to `index.html`.
- [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
* [Webpack-Dev-Server](https://github.com/webpack/webpack-dev-server): setup the
`historyApiFallback` entry in the dev server options as follows:
<code-example>
historyApiFallback: {
disableDotRule: true,
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
}
disableDotRule: true,
htmlAcceptHeaders: ['text/html', 'application/xhtml+xml']
}
</code-example>
#### Production servers
- [Apache](https://httpd.apache.org/): add a
* [Apache](https://httpd.apache.org/): add a
[rewrite rule](http://httpd.apache.org/docs/current/mod/mod_rewrite.html)
to the `.htaccess` file as show
[here](https://ngmilk.rocks/2015/03/09/angularjs-html5-mode-or-pretty-urls-on-apache-using-htaccess/):
<code-example format=".">
RewriteEngine On
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If an existing asset or directory is requested go to it as it is
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -f [OR]
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} -d
RewriteRule ^ - [L]
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
# If the requested resource doesn't exist, use index.html
RewriteRule ^ /index.html
</code-example>
- [NGinx](http://nginx.org/): use `try_files`, as described in
* [NGinx](http://nginx.org/): use `try_files`, as described in
[Front Controller Pattern Web Apps](https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/#front-controller-pattern-web-apps),
modified to serve `index.html`:
@ -497,28 +555,32 @@ modified to serve `index.html`:
</code-example>
- [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
* [IIS](https://www.iis.net/): add a rewrite rule to `web.config`, similar to the one shown
[here](http://stackoverflow.com/a/26152011/2116927):
<code-example format="." escape="html">
<system.webServer>
<rewrite>
<rules>
<rule name="Angular Routes" stopProcessing="true">
<match url=".*" />
<conditions logicalGrouping="MatchAll">
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
</conditions>
<action type="Rewrite" url="/" />
</rule>
</rules>
</rewrite>
</system.webServer>
<code-example format='.'>
&lt;system.webServer&gt;
&lt;rewrite&gt;
&lt;rules&gt;
&lt;rule name="Angular Routes" stopProcessing="true"&gt;
&lt;match url=".*" /&gt;
&lt;conditions logicalGrouping="MatchAll"&gt;
&lt;add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" /&gt;
&lt;add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" /&gt;
&lt;/conditions&gt;
&lt;action type="Rewrite" url="/src/" /&gt;
&lt;/rule&gt;
&lt;/rules&gt;
&lt;/rewrite&gt;
&lt;/system.webServer&gt;
</code-example>
- [GitHub Pages](https://pages.github.com/): you can't
* [GitHub Pages](https://pages.github.com/): you can't
[directly configure](https://github.com/isaacs/github/issues/408)
the GitHub Pages server, but you can add a 404 page.
Copy `index.html` into `404.html`.
@ -528,15 +590,15 @@ It's also a good idea to
and to
[create a `.nojekyll` file](https://www.bennadel.com/blog/3181-including-node-modules-and-vendors-folders-in-your-github-pages-site.htm)
- [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
* [Firebase hosting](https://firebase.google.com/docs/hosting/): add a
[rewrite rule](https://firebase.google.com/docs/hosting/url-redirects-rewrites#section-rewrites).
<code-example format=".">
"rewrites": [ {
"source": "**",
"destination": "/index.html"
} ]
"source": "**",
"destination": "/index.html"
} ]
</code-example>
@ -544,6 +606,8 @@ and to
{@a cors}
### Requesting services from a different server (CORS)
Angular developers may encounter a
@ -560,6 +624,8 @@ Read about how to enable CORS for specific servers at
{@a next-steps}
## Next steps
If you want to go beyond the [simple _copy-deploy_](guide/deployment#dev-deploy "Simplest deployment possible") approach,
read the [AOT Cookbook](cookbook/aot-compiler) next.
read the [AOT Cookbook](cookbook/aot-compiler "AOT Cookbook") next.

View File

@ -6,6 +6,8 @@ Property binding helps show app data in the UI.
@description
You can display data by binding controls in an HTML template to properties of an Angular component.
In this page, you'll create a component with a list of heroes.
@ -16,9 +18,11 @@ The final UI looks like this:
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/final.png" alt="Final UI"> </img>
<img src="assets/images/devguide/displaying-data/final.png" alt="Final UI"></img>
</figure>
# Contents
* [Showing component properties with interpolation](guide/displaying-data#interpolation).
@ -28,6 +32,8 @@ The final UI looks like this:
~~~ {.l-sub-section}
The <live-example></live-example> demonstrates all of the syntax and code
snippets described in this page.
@ -35,6 +41,8 @@ snippets described in this page.
~~~
## Showing component properties with interpolation
The easiest way to display a component property
is to bind the property name through interpolation.
@ -49,10 +57,12 @@ changing the template and the body of the component.
When you're done, it should look like this:
<code-example path="displaying-data/src/app/app.component.1.ts">
<code-example path="displaying-data/src/app/app.component.1.ts" title="src/app/app.component.ts">
</code-example>
You added two properties to the formerly empty component: `title` and `myHero`.
The revised template displays the two component properties using double curly brace
@ -67,6 +77,8 @@ interpolation:
~~~ {.l-sub-section}
The template is a multi-line string within ECMAScript 2015 backticks (<code>\`</code>).
The backtick (<code>\`</code>)&mdash;which is *not* the same character as a single
quote (`'`)&mdash;allows you to compose a string over several lines, which makes the
@ -75,6 +87,8 @@ HTML more readable.
~~~
Angular automatically pulls the value of the `title` and `myHero` properties from the component and
inserts those values into the browser. Angular updates the display
when these properties change.
@ -82,12 +96,16 @@ when these properties change.
~~~ {.l-sub-section}
More precisely, the redisplay occurs after some kind of asynchronous event related to
the view, such as a keystroke, a timer completion, or a response to an HTTP request.
~~~
Notice that you don't call **new** to create an instance of the `AppComponent` class.
Angular is creating an instance for you. How?
@ -99,6 +117,8 @@ That element is a placeholder in the body of your `index.html` file:
</code-example>
When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angular looks for a `<my-app>`
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
inside the `<my-app>` tag.
@ -106,10 +126,14 @@ inside the `<my-app>` tag.
Now run the app. It should display the title and hero name:
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"> </img>
<img src="assets/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero"></img>
</figure>
The next few sections review some of the coding choices in the app.
## Template inline or template file?
You can store your component's template in one of two places.
@ -123,6 +147,8 @@ Here the app uses inline HTML because the template is small and the demo
is simpler without the additional HTML file.
In either style, the template data bindings have the same access to the component's properties.
## Constructor or variable initialization?
Although this example uses variable assignment to initialize the components, you can instead declare and initialize the properties using a constructor:
@ -132,8 +158,12 @@ Although this example uses variable assignment to initialize the components, you
</code-example>
This app uses more terse "variable assignment" style simply for brevity.
## Showing an array property with ***ngFor**
To display a list of heroes, begin by adding an array of hero names to the component and redefine `myHero` to be the first name in the array.
@ -143,6 +173,8 @@ To display a list of heroes, begin by adding an array of hero names to the compo
</code-example>
Now use the Angular `ngFor` directive in the template to display
each item in the `heroes` list.
@ -151,6 +183,8 @@ each item in the `heroes` list.
</code-example>
This UI uses the HTML unordered list with `<ul>` and `<li>` tags. The `*ngFor`
in the `<li>` element is the Angular "repeater" directive.
It marks that `<li>` element (and its children) as the "repeater template":
@ -164,15 +198,19 @@ It marks that `<li>` element (and its children) as the "repeater template":
~~~ {.alert.is-important}
Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax.
For more information, see the [Template Syntax](guide/template-syntax) page.
For more information, see the [Template Syntax](guide/template-syntax#ngFor) page.
~~~
Notice the `hero` in the `ngFor` double-quoted instruction;
it is an example of a template input variable. Read
more about template input variables in the [microsyntax](guide/template-syntax) section of
more about template input variables in the [microsyntax](guide/template-syntax#microsyntax) section of
the [Template Syntax](guide/template-syntax) page.
Angular duplicates the `<li>` for each item in the list, setting the `hero` variable
@ -182,19 +220,25 @@ context for the interpolation in the double curly braces.
~~~ {.l-sub-section}
In this case, `ngFor` is displaying an array, but `ngFor` can
repeat items for any [iterable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) object.
~~~
Now the heroes appear in an unordered list.
<figure class='image-display'>
<img src="assets/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"> </img>
<img src="assets/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"></img>
</figure>
## Creating a class for the data
The app's code defines the data directly inside the component, which isn't best practice.
@ -213,6 +257,8 @@ Create a new file in the `app` folder called `hero.ts` with the following code:
</code-example>
You've defined a class with a constructor and two properties: `id` and `name`.
It might not look like the class has properties, but it does.
@ -225,11 +271,16 @@ Consider the first parameter:
</code-example>
That brief syntax does a lot:
* Declares a constructor parameter and its type.
* Declares a public property of the same name.
* Initializes that property with the corresponding argument when creating an instance of the class.
## Using the Hero class
After importing the `Hero` class, the `AppComponent.heroes` property can return a _typed_ array
@ -240,6 +291,8 @@ of `Hero` objects:
</code-example>
Next, update the template.
At the moment it displays the hero's `id` and `name`.
Fix that to display only the hero's `name` property.
@ -249,8 +302,12 @@ Fix that to display only the hero's `name` property.
</code-example>
The display looks the same, but the code is clearer.
## Conditional display with NgIf
Sometimes an app needs to display a view or a portion of a view only under specific circumstances.
@ -269,39 +326,50 @@ To see it in action, add the following paragraph at the bottom of the template:
~~~ {.alert.is-important}
Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax.
Read more about `ngIf` and `*` in the [ngIf section](guide/template-syntax) of the [Template Syntax](guide/template-syntax) page.
Read more about `ngIf` and `*` in the [ngIf section](guide/template-syntax#ngIf) of the [Template Syntax](guide/template-syntax) page.
~~~
The template expression inside the double quotes,
`*ngIf="heros.length > 3"`, looks and behaves much like TypeScript.
When the component's list of heroes has more than three items, Angular adds the paragraph
to the DOM and the message appears. If there are three or fewer items, Angular omits the
paragraph, so no message appears. For more information,
see the [template expressions](guide/template-syntax) section of the
see the [template expressions](guide/template-syntax#template-expressions) section of the
[Template Syntax](guide/template-syntax) page.
~~~ {.alert.is-helpful}
Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM. That improves performance, especially in larger projects when conditionally including or excluding
big chunks of HTML with many data bindings.
~~~
Try it out. Because the array has four items, the message should appear.
Go back into <code>app.component.ts"</code> and delete or comment out one of the elements from the hero array.
The browser should refresh automatically and the message should disappear.
## Summary
Now you know how to use:
- **Interpolation** with double curly braces to display a component property.
- **ngFor** to display an array of items.
- A TypeScript class to shape the **model data** for your component and display properties of that model.
- **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
* **Interpolation** with double curly braces to display a component property.
* **ngFor** to display an array of items.
* A TypeScript class to shape the **model data** for your component and display properties of that model.
* **ngIf** to conditionally display a chunk of HTML based on a boolean expression.
Here's the final code:

View File

@ -5,114 +5,188 @@ Dynamic Component Loader
Load components dynamically.
@description
Component templates are not always fixed. An application may need to load new components at runtime.
In this cookbook we show how to use `ComponentFactoryResolver` to add components dynamically.
This cookbook shows you how to use `ComponentFactoryResolver` to add components dynamically.
<a id="toc"></a>## Table of contents
{@a toc}
[Dynamic Component Loading](guide/dynamic-component-loader#dynamic-loading)
# Contents
[Where to load the component](guide/dynamic-component-loader#where-to-load)
* [Dynamic component loading](guide/dynamic-component-loader#dynamic-loading)
* [The directive](guide/dynamic-component-loader#directive)
* [Loading components](guide/dynamic-component-loader#loading-components)
[Loading components](guide/dynamic-component-loader#loading-components)
* [Resolving Components](guide/dynamic-component-loader#resolving-components)
* [Selector References](guide/dynamic-component-loader#selector-references)
<a id="dynamic-loading"></a>## Dynamic Component Loading
* [A common _AdComponent_ interface](guide/dynamic-component-loader#common-interface)
* [Final ad banner](guide/dynamic-component-loader#final-ad-banner)
See the <live-example name="cb-dynamic-component-loader"></live-example>
of the code in this cookbook.
{@a dynamic-loading}
## Dynamic component loading
The following example shows how to build a dynamic ad banner.
The hero agency is planning an ad campaign with several different ads cycling through the banner.
The hero agency is planning an ad campaign with several different
ads cycling through the banner. New ad components are added
frequently by several different teams. This makes it impractical
to use a template with a static component structure.
New ad components are added frequently by several different teams. This makes it impractical to use a template with a static component structure.
Instead, you need a way to load a new component without a fixed
reference to the component in the ad banner's template.
Instead we need a way to load a new component without a fixed reference to the component in the ad banner's template.
Angular comes with its own API for loading components dynamically. In the following sections you will learn how to use it.
Angular comes with its own API for loading components dynamically.
<a id="where-to-load"></a>## Where to load the component
{@a directive}
Before components can be added we have to define an anchor point to mark where components can be inserted dynamically.
## The directive
The ad banner uses a helper directive called `AdDirective` to mark valid insertion points in the template.
Before you can add components you have to define an anchor point
to tell Angular where to insert components.
The ad banner uses a helper directive called `AdDirective` to
mark valid insertion points in the template.
<code-example path="cb-dynamic-component-loader/src/app/ad.directive.ts" linenums="false">
<code-example path="cb-dynamic-component-loader/src/app/ad.directive.ts" title="src/app/ad.directive.ts" linenums="false">
</code-example>
`AdDirective` injects `ViewContainerRef` to gain access to the view container of the element that will become the host of the dynamically added component.
<a id="loading-components"></a>## Loading components
The next step is to implement the ad banner. Most of the implementation is in `AdBannerComponent`.
We start by adding a `template` element with the `AdDirective` directive applied.
<code-tabs>
`AdDirective` injects `ViewContainerRef` to gain access to the view
container of the element that will host the dynamically added component.
<code-pane title="ad-banner.component.ts" path="cb-dynamic-component-loader/src/app/ad-banner.component.ts">
In the `@Directive` decorator, notice the selector name, `ad-host`;
that's what you use to apply the directive to the element.
The next section shows you how.
</code-pane>
{@a loading-components}
<code-pane title="ad.service.ts" path="cb-dynamic-component-loader/src/app/ad.service.ts">
## Loading components
</code-pane>
Most of the ad banner implementation is in `ad-banner.component.ts`.
To keep things simple in this example, the HTML is in the `@Component`
decorator's `template` property as a template string.
<code-pane title="ad-item.ts" path="cb-dynamic-component-loader/src/app/ad-item.ts">
</code-pane>
<code-pane title="app.module.ts" path="cb-dynamic-component-loader/src/app/app.module.ts">
</code-pane>
<code-pane title="app.component" path="cb-dynamic-component-loader/src/app/app.component.ts">
</code-pane>
</code-tabs>
The `template` element decorated with the `ad-host` directive marks where dynamically loaded components will be added.
Using a `template` element is recommended since it doesn't render any additional output.
The `<ng-template>` element is where you apply the directive you just made.
To apply the `AdDirective`, recall the selector from `ad.directive.ts`,
`ad-host`. Apply that to `<ng-template>` without the square brackets. Now Angular knows
where to dynamically load components.
<code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" linenums="false">
<code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="ad-host" title="src/app/ad-banner.component.ts (template)" linenums="false">
</code-example>
### Resolving Components
`AdBanner` takes an array of `AdItem` objects as input. `AdItem` objects specify the type of component to load and any data to bind to the component.
The ad components making up the ad campaign are returned from `AdService`.
Passing an array of components to `AdBannerComponent` allows for a dynamic list of ads without static elements in the template.
`AdBannerComponent` cycles through the array of `AdItems` and loads the corresponding components on an interval. Every 3 seconds a new component is loaded.
`ComponentFactoryResolver` is used to resolve a `ComponentFactory` for each specific component. The component factory is need to create an instance of the component.
`ComponentFactories` are generated by the Angular compiler.
Generally the compiler will generate a component factory for any component referenced in a template.
With dynamically loaded components there are no selector references in the templates since components are loaded at runtime. In order to ensure that the compiler will still generate a factory, dynamically loaded components have to be added to their `NgModule`'s `entryComponents` array.
<code-example path="cb-dynamic-component-loader/src/app/app.module.ts" region="entry-components" linenums="false">
The `<ng-template>` element is a good choice for dynamic components
because it doesn't render any additional output.
{@a resolving-components}
### Resolving components
Take a closer look at the methods in `ad-banner.component.ts`.
`AdBannerComponent` takes an array of `AdItem` objects as input,
which ultimately comes from `AdService`. `AdItem` objects specify
the type of component to load and any data to bind to the
component.`AdService` returns the actual ads making up the ad campaign.
Passing an array of components to `AdBannerComponent` allows for a
dynamic list of ads without static elements in the template.
With its `getAds()` method, `AdBannerComponent` cycles through the array of `AdItems`
and loads a new component every 3 seconds by calling `loadComponent()`.
<code-example path="cb-dynamic-component-loader/src/app/ad-banner.component.ts" region="class" title="src/app/ad-banner.component.ts (excerpt)" linenums="false">
</code-example>
Components are added to the template by calling `createComponent` on the `ViewContainerRef` reference.
`createComponent` returns a reference to the loaded component. The component reference can be used to pass input data or call methods to interact with the component.
In the Ad banner, all components implement a common `AdComponent` interface to standardize the api for passing data to the components.
The `loadComponent()` method is doing a lot of the heavy lifting here.
Take it step by step. First, it picks an ad.
Two sample components and the `AdComponent` interface are shown below:
~~~ {.l-sub-section}
**How _loadComponent()_ chooses an ad**
The `loadComponent()` method chooses an ad using some math.
First, it sets the `currentAddIndex` by taking whatever it
currently is plus one, dividing that by the length of the `AdItem` array, and
using the _remainder_ as the new `currentAddIndex` value. Then, it uses that
value to select an `adItem` from the array.
~~~
After `loadComponent()` selects an ad, it uses `ComponentFactoryResolver`
to resolve a `ComponentFactory` for each specific component.
The `ComponentFactory` then creates an instance of each component.
Next, you're targeting the `viewContainerRef` that
exists on this specific instance of the component. How do you know it's
this specific instance? Because it's referring to `adHost` and `adHost` is the
directive you set up earlier to tell Angular where to insert dynamic components.
As you may recall, `AdDirective` injects `ViewContainerRef` into its constructor.
This is how the directive accesses the element that you want to use to host the dynamic component.
To add the component to the template, you call `createComponent()` on `ViewContainerRef`.
The `createComponent()` method returns a reference to the loaded component.
Use that reference to interact with the component by assigning to its properties or calling its methods.
{@a selector-references}
#### Selector references
Generally, the Angular compiler generates a `ComponentFactory`
for any component referenced in a template. However, there are
no selector references in the templates for
dynamically loaded components since they load at runtime.
To ensure that the compiler still generates a factory,
add dynamically loaded components to the `NgModule`'s `entryComponents` array:
<code-example path="cb-dynamic-component-loader/src/app/app.module.ts" region="entry-components" title="src/app/app.module.ts (entry components)" linenums="false">
</code-example>
{@a common-interface}
### A common _AdComponent_ interface
In the ad banner, all components implement a common `AdComponent` interface to
standardize the API for passing data to the components.
Here are two sample components and the `AdComponent` interface for reference:
<code-tabs>
@ -131,9 +205,18 @@ Two sample components and the `AdComponent` interface are shown below:
</code-tabs>
The final ad banner looks like this:
{@a final-ad-baner}
### Final ad banner
The final ad banner looks like this:
<figure class='image-display'>
<img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"> </img>
<img src="assets/images/cookbooks/dynamic-component-loader/ads.gif" alt="Ads"></img>
</figure>
See the <live-example name="cb-dynamic-component-loader"></live-example>.

View File

@ -5,6 +5,8 @@ Dynamic Forms
Render dynamic forms with FormGroup.
@description
We can't always justify the cost and time to build handcrafted forms,
especially if we'll need a great number of them, they're similar to each other, and they change frequently
to meet rapidly changing business and regulatory requirements.
@ -19,7 +21,9 @@ All such greatness has humble beginnings.
In our example we use a dynamic form to build an online application experience for heroes seeking employment.
The agency is constantly tinkering with the application process.
We can create the forms on the fly *without changing our application code*.
<a id="toc"></a>## Table of contents
{@a toc}
## Table of contents
[Bootstrap](guide/dynamic-form#bootstrap)
@ -30,9 +34,13 @@ We can create the forms on the fly *without changing our application code*.
[Questionnaire Metadata](guide/dynamic-form#questionnaire-metadata)
[Dynamic Template](guide/dynamic-form#dynamic-template)
**See the <live-example name="cb-dynamic-form"></live-example>**.
<a id="bootstrap"></a>## Bootstrap
{@a bootstrap}
## Bootstrap
We start by creating an `NgModule` called `AppModule`.
@ -56,7 +64,9 @@ We bootstrap our `AppModule` in main.ts.
</code-tabs>
<a id="object-model"></a>## Question Model
{@a object-model}
## Question Model
The next step is to define an object model that can describe all scenarios needed by the form functionality.
The hero application process involves a form with a lot of questions.
@ -65,37 +75,47 @@ The "question" is the most fundamental object in the model.
We have created `QuestionBase` as the most fundamental question class.
<code-example path="cb-dynamic-form/src/app/question-base.ts">
<code-example path="cb-dynamic-form/src/app/question-base.ts" title="src/app/question-base.ts">
</code-example>
From this base we derived two new classes in `TextboxQuestion` and `DropdownQuestion` that represent Textbox and Dropdown questions.
The idea is that the form will be bound to specific question types and render the appropriate controls dynamically.
`TextboxQuestion` supports multiple html5 types like text, email, url etc via the `type` property.
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" linenums="false">
<code-example path="cb-dynamic-form/src/app/question-textbox.ts" title="src/app/question-textbox.ts" linenums="false">
</code-example>
`DropdownQuestion` presents a list of choices in a select box.
<code-example path="cb-dynamic-form/src/app/question-dropdown.ts" linenums="false">
<code-example path="cb-dynamic-form/src/app/question-dropdown.ts" title="src/app/question-dropdown.ts" linenums="false">
</code-example>
Next we have defined `QuestionControlService`, a simple service for transforming our questions to a `FormGroup`.
In a nutshell, the form group consumes the metadata from the question model and allows us to specify default values and validation rules.
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" linenums="false">
<code-example path="cb-dynamic-form/src/app/question-control.service.ts" title="src/app/question-control.service.ts" linenums="false">
</code-example>
<a id="form-component"></a>## Question form components
{@a form-component}
## Question form components
Now that we have defined the complete model we are ready to create components to represent the dynamic form.
`DynamicFormComponent` is the entry point and the main container for the form.
<code-tabs>
@ -110,6 +130,8 @@ Now that we have defined the complete model we are ready to create components to
</code-tabs>
It presents a list of questions, each question bound to a `<df-question>` component element.
The `<df-question>` tag matches the `DynamicFormQuestionComponent`,
the component responsible for rendering the details of each _individual_ question based on values in the data-bound question object.
@ -127,6 +149,8 @@ the component responsible for rendering the details of each _individual_ questio
</code-tabs>
Notice this component can present any type of question in our model.
We only have two types of questions at this point but we can imagine many more.
The `ngSwitch` determines which type of question to display.
@ -135,7 +159,11 @@ In both components we're relying on Angular's **formGroup** to connect the temp
underlying control objects, populated from the question model with display and validation rules.
`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`.
<a id="questionnaire-metadata"></a>## Questionnaire data`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
{@a questionnaire-metadata}
## Questionnaire data
`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
The set of questions we have defined for the job application is returned from the `QuestionService`.
In a real app we'd retrieve these questions from storage.
@ -144,18 +172,22 @@ underlying control objects, populated from the question model with display and v
Questionnaire maintenance is a simple matter of adding, updating, and removing objects from the `questions` array.
<code-example path="cb-dynamic-form/src/app/question.service.ts">
<code-example path="cb-dynamic-form/src/app/question.service.ts" title="src/app/question.service.ts">
</code-example>
Finally, we display an instance of the form in the `AppComponent` shell.
<code-example path="cb-dynamic-form/src/app/app.component.ts">
<code-example path="cb-dynamic-form/src/app/app.component.ts" title="app.component.ts">
</code-example>
<a id="dynamic-template"></a>## Dynamic Template
{@a dynamic-template}
## Dynamic Template
Although in this example we're modelling a job application for heroes, there are no references to any specific hero question
outside the objects returned by `QuestionService`.
@ -169,10 +201,14 @@ The *Save* button is disabled until the form is in a valid state.
When the form is valid, we can click *Save* and the app renders the current form values as JSON.
This proves that any user input is bound back to the data model.
Saving and retrieving the data is an exercise for another time.
The final form looks like this:
<figure class='image-display'>
<img src="assets/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> </img>
<img src="assets/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form"></img>
</figure>
[Back to top](guide/dynamic-form#top)

View File

@ -8,6 +8,8 @@ Validate user's form entries.
{@a top}
Improve overall data quality by validating user input for accuracy and completeness.
This cookbook shows how to validate user input in the UI and display useful validation messages
@ -15,6 +17,8 @@ using first the template-driven forms and then the reactive forms approach.
~~~ {.l-sub-section}
Read more about these choices in the [Forms](guide/forms)
and the [Reactive Forms](guide/reactive-forms) guides.
@ -24,25 +28,36 @@ and the [Reactive Forms](guide/reactive-forms) guides.
{@a toc}
## Contents
* [Simple template-driven forms](guide/form-validation#template1)
* [Template-driven forms with validation messages in code](guide/form-validation#template2)
- [Component Class](guide/form-validation#component-class)
- [The benefits of messages in code](guide/form-validation#improvement)
- [`FormModule` and template-driven forms](guide/form-validation#formmodule)
* [Component Class](guide/form-validation#component-class)
* [The benefits of messages in code](guide/form-validation#improvement)
* [`FormModule` and template-driven forms](guide/form-validation#formmodule)
* [Reactive forms with validation in code](guide/form-validation#reactive)
- [Switch to the `ReactiveFormsModule`](guide/form-validation#reactive-forms-module)
- [Component template](guide/form-validation#reactive-component-template)
- [Component class](guide/form-validation#reactive-component-class)
- [`FormBuilder` declaration](guide/form-validation#formbuilder)
- [Committing hero value changes](guide/form-validation#committing-changes)
* [Switch to the `ReactiveFormsModule`](guide/form-validation#reactive-forms-module)
* [Component template](guide/form-validation#reactive-component-template)
* [Component class](guide/form-validation#reactive-component-class)
* [`FormBuilder` declaration](guide/form-validation#formbuilder)
* [Committing hero value changes](guide/form-validation#committing-changes)
* [Custom validation](guide/form-validation#custom-validation)
- [Custom validation directive](guide/form-validation#custom-validation-directive)
* [Custom validation directive](guide/form-validation#custom-validation-directive)
* [Testing considerations](guide/form-validation#testing)
{@a live-example}
**Try the live example to see and download the full cookbook source code.**
<live-example name="cb-form-validation" embedded=true img="cookbooks/form-validation/plunker.png">
@ -53,6 +68,8 @@ and the [Reactive Forms](guide/reactive-forms) guides.
{@a template1}
## Simple template-driven forms
In the template-driven approach, you arrange
@ -72,27 +89,30 @@ In this first template validation example,
notice the HTML that reads the control state and updates the display appropriately.
Here's an excerpt from the template HTML for a single input control bound to the hero name:
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="name-with-error-msg" title="template/hero-form-template1.component.html (Hero name)" linenums="false">
</code-example>
Note the following:
- The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
- The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
Note the following:
* The `<input>` element carries the HTML validation attributes: `required`, `minlength`, and `maxlength`.
* The `name` attribute of the input is set to `"name"` so Angular can track this input element and associate it
with an Angular form control called `name` in its internal control model.
- The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
* The `[(ngModel)]` directive allows two-way data binding between the input box to the `hero.name` property.
- The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
* The template variable (`#name`) has the value `"ngModel"` (always `ngModel`).
This gives you a reference to the Angular `NgModel` directive
associated with this control that you can use _in the template_
to check for control states such as `valid` and `dirty`.
- The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs` but only if there are "name" errors and
the control is either `dirty` or `touched`.
- Each nested `<div>` can present a custom message for one of the possible validation errors.
* Each nested `<div>` can present a custom message for one of the possible validation errors.
There are messages for `required`, `minlength`, and `maxlength`.
The full template repeats this kind of layout for each data entry control on the form.
@ -102,6 +122,8 @@ The full template repeats this kind of layout for each data entry control on the
~~~ {.l-sub-section}
#### Why check _dirty_ and _touched_?
The app shouldn't show errors for a new hero before the user has had a chance to edit the value.
@ -111,14 +133,18 @@ Learn about `dirty` and `touched` in the [Forms](guide/forms) guide.
~~~
The component class manages the hero model used in the data binding
as well as other code to support the view.
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.ts" region="class">
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.ts" region="class" title="template/hero-form-template1.component.ts (class)">
</code-example>
Use this template-driven validation technique when working with static forms with simple, standard validation rules.
Here are the complete files for the first version of `HeroFormTemplateCompononent` in the template-driven approach:
@ -140,6 +166,8 @@ Here are the complete files for the first version of `HeroFormTemplateCompononen
{@a template2}
## Template-driven forms with validation messages in code
While the layout is straightforward,
@ -171,20 +199,25 @@ Here's the hero name again, excerpted from the revised template
</code-tabs>
The `<input>` element HTML is almost the same. There are noteworthy differences:
- The hard-code error message `<divs>` are gone.
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
The `<input>` element HTML is almost the same. There are noteworthy differences:
* The hard-code error message `<divs>` are gone.
* There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
It invalidates the control if the user enters "bob" in the name `<input>`([try it](guide/form-validation#live-example)).
See the [custom validation](guide/form-validation#custom-validation) section later in this cookbook for more information
on custom validation directives.
- The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
* The `#name` template variable is gone because the app no longer refers to the Angular control for this element.
- Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
* Binding to the new `formErrors.name` property is sufficent to display all name validation error messages.
{@a component-class}
### Component class
The original component code for Template 1 stayed the same; however,
Template 2 requires some changes in the component. This section covers the code
@ -196,36 +229,42 @@ The first step is to acquire the form control that Angular created from the temp
Look back at the top of the component template at the
`#heroForm` template variable in the `<form>` element:
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="form-tag" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template1.component.html" region="form-tag" title="template/hero-form-template1.component.html (form tag)" linenums="false">
</code-example>
The `heroForm` variable is a reference to the control model that Angular derived from the template.
Tell Angular to inject that model into the component class's `currentForm` property using a `@ViewChild` query:
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="view-child" title="template/hero-form-template2.component.ts (heroForm)" linenums="false">
</code-example>
Some observations:
- Angular `@ViewChild` queries for a template variable when you pass it
* Angular `@ViewChild` queries for a template variable when you pass it
the name of that variable as a string (`'heroForm'` in this case).
- The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
* The `heroForm` object changes several times during the life of the component, most notably when you add a new hero.
Periodically inspecting it reveals these changes.
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks)
* Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks#afterview)
when anything changes in the view.
That's the right time to see if there's a new `heroForm` object.
- When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
* When there _is_ a new `heroForm` model, `formChanged()` subscribes to its `valueChanges` _Observable_ property.
The `onValueChanged` handler looks for validation errors after every keystroke.
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="handler" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="handler" title="template/hero-form-template2.component.ts (handler)" linenums="false">
</code-example>
The `onValueChanged` handler interprets user data entry.
The `data` object passed into the handler contains the current element values.
The handler ignores them. Instead, it iterates over the fields of the component's `formErrors` object.
@ -235,22 +274,26 @@ Only two hero properties have validation rules, `name` and `power`.
The messages are empty strings when the hero data are valid.
For each field, the `onValueChanged` handler does the following:
- Clears the prior error message, if any.
- Acquires the field's corresponding Angular form control.
- If such a control exists _and_ it's been changed ("dirty")
* Clears the prior error message, if any.
* Acquires the field's corresponding Angular form control.
* If such a control exists _and_ it's been changed ("dirty")
_and_ it's invalid, the handler composes a consolidated error message for all of the control's errors.
Next, the component needs some error messages of course&mdash;a set for each validated property with
one message per validation rule:
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="messages" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.ts" region="messages" title="template/hero-form-template2.component.ts (messages)" linenums="false">
</code-example>
Now every time the user makes a change, the `onValueChanged` handler checks for validation errors and produces messages accordingly.
{@a improvement}
### The benefits of messages in code
Clearly the template got substantially smaller while the component code got substantially larger.
@ -276,6 +319,8 @@ In short, there are more opportunities to improve message handling now that text
{@a formmodule}
### _FormModule_ and template-driven forms
Angular has two different forms modules&mdash;`FormsModule` and
@ -287,7 +332,7 @@ You've been reviewing the "Template-driven" approach which requires the `FormsMo
Here's how you imported it in the `HeroFormTemplateModule`.
<code-example path="cb-form-validation/src/app/template/hero-form-template.module.ts" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template.module.ts" title="template/hero-form-template.module.ts" linenums="false">
</code-example>
@ -295,6 +340,8 @@ Here's how you imported it in the `HeroFormTemplateModule`.
~~~ {.l-sub-section}
This guide hasn't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
form template in this cookbook.
@ -307,6 +354,8 @@ They're not germane to the validation story. Look at the [live example](guide/fo
{@a reactive}
## Reactive forms with validation in code
In the template-driven approach, you markup the template with form elements, validation attributes,
@ -321,6 +370,7 @@ At runtime, Angular binds the template elements to your control model based on y
This approach requires a bit more effort. *You have to write the control model and manage it*.
This allows you to do the following:
* Add, change, and remove validation functions on the fly.
* Manipulate the control model dynamically from within the component.
* [Test](guide/form-validation#testing) validation and control logic with isolated unit tests.
@ -329,19 +379,25 @@ The following cookbook sample re-writes the hero form in _reactive forms_ style.
{@a reactive-forms-module}
### Switch to the _ReactiveFormsModule_
The reactive forms classes and directives come from the Angular `ReactiveFormsModule`, not the `FormsModule`.
The application module for the reactive forms feature in this sample looks like this:
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.module.ts" linenums="false">
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.module.ts" title="src/app/reactive/hero-form-reactive.module.ts" linenums="false">
</code-example>
The reactive forms feature module and component are in the `src/app/reactive` folder.
Focus on the `HeroFormReactiveComponent` there, starting with its template.
{@a reactive-component-template}
### Component template
Begin by changing the `<form>` tag so that it binds the Angular `formGroup` directive in the template
@ -349,10 +405,12 @@ to the `heroForm` property in the component class.
The `heroForm` is the control model that the component class builds and maintains.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="form-tag" linenums="false">
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" region="form-tag" title="cb-form-validation/src/app/reactive/hero-form-reactive.component.html" linenums="false">
</code-example>
Next, modify the template HTML elements to match the _reactive forms_ style.
Here is the "name" portion of the template again, revised for reactive forms and compared with the template-driven version:
@ -368,16 +426,20 @@ Here is the "name" portion of the template again, revised for reactive forms and
</code-tabs>
Key changes are:
- The validation attributes are gone (except `required`) because
* The validation attributes are gone (except `required`) because
validating happens in code.
- `required` remains, not for validation purposes (that's in the code),
* `required` remains, not for validation purposes (that's in the code),
but rather for css styling and accessibility.
~~~ {.l-sub-section}
A future version of reactive forms will add the `required` HTML validation attribute to the DOM element
(and perhaps the `aria-required` attribute) when the control has the `required` validator function.
@ -387,16 +449,20 @@ to the control model, as you'll see below.
~~~
- The `formControlName` replaces the `name` attribute; it serves the same
* The `formControlName` replaces the `name` attribute; it serves the same
purpose of correlating the input with the Angular form control.
- The two-way `[(ngModel)]` binding is gone.
* The two-way `[(ngModel)]` binding is gone.
The reactive approach does not use data binding to move data into and out of the form controls.
That's all in code.
~~~ {.l-sub-section}
The retreat from data binding is a principle of the reactive paradigm rather than a technical limitation.
~~~
@ -404,6 +470,8 @@ The retreat from data binding is a principle of the reactive paradigm rather tha
{@a reactive-component-class}
### Component class
The component class is now responsible for defining and managing the form control model.
@ -426,24 +494,32 @@ Here's the section of code devoted to that process, paired with the template-dri
</code-tabs>
- Inject `FormBuilder` in a constructor.
- Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](guide/lifecycle-hooks)
* Inject `FormBuilder` in a constructor.
* Call a `buildForm` method in the `ngOnInit` [lifecycle hook method](guide/lifecycle-hooks#hooks-overview)
because that's when you'll have the hero data. Call it again in the `addHero` method.
~~~ {.l-sub-section}
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.
~~~
- The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
* The `buildForm` method uses the `FormBuilder`, `fb`, to declare the form control model.
Then it attaches the same `onValueChanged` handler (there's a one line difference)
to the form's `valueChanges` event and calls it immediately
to set error messages for the new control model.
{@a formbuilder}
#### _FormBuilder_ declaration
The `FormBuilder` declaration object specifies the three controls of the sample's hero form.
@ -460,7 +536,9 @@ discussed in a separate [section below](guide/form-validation#custom-validation)
~~~ {.l-sub-section}
Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms) section of Reactive Forms guide.
Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms#formbuilder) section of Reactive Forms guide.
~~~
@ -468,6 +546,8 @@ Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reacti
{@a committing-changes}
#### Committing hero value changes
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
@ -475,12 +555,13 @@ Reactive forms do not use data binding to update data model properties.
The developer decides _when and how_ to update the data model from control values.
This sample updates the model twice:
1. When the user submits the form.
1. When the user adds a new hero.
The `onSubmit()` method simply replaces the `hero` object with the combined values of the form:
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="on-submit" linenums="false">
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="on-submit" title="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" linenums="false">
</code-example>
@ -488,17 +569,23 @@ The `onSubmit()` method simply replaces the `hero` object with the combined valu
~~~ {.l-sub-section}
This example is lucky in that the `heroForm.value` properties _just happen_ to
correspond _exactly_ to the hero data object properties.
~~~
The `addHero()` method discards pending changes and creates a brand new `hero` model object.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" linenums="false">
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="add-hero" title="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" linenums="false">
</code-example>
Then it calls `buildForm()` again which replaces the previous `heroForm` control model with a new one.
The `<form>` tag's `[formGroup]` binding refreshes the page with the new control model.
@ -524,6 +611,8 @@ Here's the complete reactive component file, compared to the two template-driven
~~~ {.l-sub-section}
Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves,
and to compare all of the files in this cookbook sample.
@ -534,6 +623,8 @@ and to compare all of the files in this cookbook sample.
{@a custom-validation}
## Custom validation
This cookbook sample has a custom `forbiddenNamevalidator()` function that's applied to both the
template-driven and the reactive form controls. It's in the `src/app/shared` folder
@ -541,10 +632,12 @@ and declared in the `SharedModule`.
Here's the `forbiddenNamevalidator()` function:
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" linenums="false">
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" title="shared/forbidden-name.directive.ts (forbiddenNameValidator)" linenums="false">
</code-example>
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name
and returns a validator function.
@ -560,40 +653,52 @@ and whose value is an arbitrary dictionary of values that you could insert into
{@a custom-validation-directive}
### Custom validation directive
In the reactive forms component, the `'name'` control's validator function list
has a `forbiddenNameValidator` at the bottom.
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" linenums="false">
<code-example path="cb-form-validation/src/app/reactive/hero-form-reactive.component.ts" region="name-validators" title="reactive/hero-form-reactive.component.ts (name validators)" linenums="false">
</code-example>
In the _template-driven_ example, the `<input>` has the selector (`forbiddenName`)
of a custom _attribute directive_, which rejects "bob".
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-input" linenums="false">
<code-example path="cb-form-validation/src/app/template/hero-form-template2.component.html" region="name-input" title="template/hero-form-template2.component.html (name input)" linenums="false">
</code-example>
The corresponding `ForbiddenValidatorDirective` is a wrapper around the `forbiddenNameValidator`.
Angular `forms` recognizes the directive's role in the validation process because the directive registers itself
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validation directives.
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" linenums="false">
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" title="shared/forbidden-name.directive.ts (providers)" linenums="false">
</code-example>
Here is the rest of the directive to help you get an idea of how it all comes together:
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive">
<code-example path="cb-form-validation/src/app/shared/forbidden-name.directive.ts" region="directive" title="shared/forbidden-name.directive.ts (directive)">
</code-example>
~~~ {.l-sub-section}
If you are familiar with Angular validations, you may have noticed
that the custom validation directive is instantiated with `useExisting`
rather than `useClass`. The registered validator must be _this instance_ of
@ -611,8 +716,12 @@ This time, when you type “bob”, there's no "bob" error message.
~~~ {.l-sub-section}
For more information on attaching behavior to elements,
see [Attribute Directives](guide/attribute-directives).
@ -623,6 +732,8 @@ see [Attribute Directives](guide/attribute-directives).
{@a testing}
## Testing Considerations
You can write _isolated unit tests_ of validation and control logic in _Reactive Forms_.

View File

@ -5,6 +5,8 @@ Forms
A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors.
@description
Forms are the mainstay of business applications.
You use forms to log in, submit a help request, place an order, book a flight,
schedule a meeting, and perform countless other data-entry tasks.
@ -18,15 +20,17 @@ which you'll learn about on this page.
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
- Build an Angular form with a component and template.
- Use `ngModel` to create two-way data bindings for reading and writing input-control values.
- Track state changes and the validity of form controls.
- Provide visual feedback using special CSS classes that track the state of the controls.
- Display validation errors to users and enable/disable form controls.
- Share information across HTML elements using template reference variables.
* Build an Angular form with a component and template.
* Use `ngModel` to create two-way data bindings for reading and writing input-control values.
* Track state changes and the validity of form controls.
* Provide visual feedback using special CSS classes that track the state of the controls.
* Display validation errors to users and enable/disable form controls.
* Share information across HTML elements using template reference variables.
You can run the <live-example></live-example> in Plunker and download the code from there.
## Template-driven forms
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
@ -35,12 +39,16 @@ the form-specific directives and techniques described in this page.
~~~ {.l-sub-section}
You can also use a reactive (or model-driven) approach to build forms.
However, this page focuses on template-driven forms.
~~~
You can build almost any form with an Angular template&mdash;login forms, contact forms, and pretty much any business form.
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
@ -52,9 +60,11 @@ You'll learn to build a template-driven form that looks like this:
<figure class='image-display'>
<img src="assets/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form"> </img>
<img src="assets/images/devguide/forms/hero-form-1.png" width="400px" alt="Clean Form"></img>
</figure>
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
@ -64,19 +74,25 @@ If you delete the hero name, the form displays a validation error in an attentio
<figure class='image-display'>
<img src="assets/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"> </img>
<img src="assets/images/devguide/forms/hero-form-2.png" width="400px" alt="Invalid, Name Required"></img>
</figure>
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
~~~ {.l-sub-section}
You can customize the colors and location of the "required" bar with standard CSS.
~~~
You'll build this form in small steps:
1. Create the `Hero` model class.
@ -88,6 +104,8 @@ You'll build this form in small steps:
1. Show and hide validation-error messages.
1. Handle form submission with *ngSubmit*.
1. Disable the forms *Submit* button until the form is valid.
## Setup
Follow the [setup](guide/setup) instructions for creating a new project
@ -105,10 +123,12 @@ and one optional field (`alterEgo`).
In the `app` directory, create the following file with the given content:
<code-example path="forms/src/app/hero.ts">
<code-example path="forms/src/app/hero.ts" title="src/app/hero.ts">
</code-example>
It's an anemic model with few requirements and no behavior. Perfect for the demo.
The TypeScript compiler generates a public field for each `public` constructor parameter and
@ -124,6 +144,8 @@ You can create a new hero like this:
</code-example>
## Create a form component
An Angular form has two parts: an HTML-based _template_ and a component _class_
@ -137,21 +159,25 @@ Create the following file with the given content:
</code-example>
Theres nothing special about this component, nothing form-specific,
nothing to distinguish it from any component you've written before.
Understanding this component requires only the Angular concepts covered in previous pages.
- The code imports the Angular core library and the `Hero` model you just created.
- The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
- The `templateUrl` property points to a separate file for the template HTML.
- You defined dummy data for `model` and `powers`, as befits a demo.
* The code imports the Angular core library and the `Hero` model you just created.
* The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
* The `templateUrl` property points to a separate file for the template HTML.
* You defined dummy data for `model` and `powers`, as befits a demo.
Down the road, you can inject a data service to get and save real data
or perhaps expose these properties as inputs and outputs
(see [Input and output properties](guide/template-syntax) on the
(see [Input and output properties](guide/template-syntax#inputs-outputs) on the
[Template Syntax](guide/template-syntax) page) for binding to a
parent component. This is not a concern now and these future changes won't affect the form.
- You added a `diagnostic` property to return a JSON representation of the model.
* You added a `diagnostic` property to return a JSON representation of the model.
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
### Why the separate template file?
@ -167,6 +193,8 @@ so it's usually best to put the HTML template in a separate file.
You'll write that template file in a moment. First,
revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
## Revise *app.module.ts*
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
@ -177,14 +205,18 @@ Because template-driven forms are in their own module, you need to add the `Form
Replace the contents of the "QuickStart" version with the following:
<code-example path="forms/src/app/app.module.ts">
<code-example path="forms/src/app/app.module.ts" title="src/app/app.module.ts">
</code-example>
~~~ {.l-sub-section}
There are three changes:
1. You import `FormsModule` and the new `HeroFormComponent`.
@ -202,6 +234,8 @@ the `HeroFormComponent` component visible throughout this module.
~~~ {.alert.is-important}
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
If you wrote it and it should belong to this module, _do_ declare it in the `declarations` array.
@ -209,6 +243,8 @@ If you wrote it and it should belong to this module, _do_ declare it in th
~~~
## Revise *app.component.ts*
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
@ -216,14 +252,18 @@ If you wrote it and it should belong to this module, _do_ declare it in th
Replace the contents of the "QuickStart" version with the following:
<code-example path="forms/src/app/app.component.ts">
<code-example path="forms/src/app/app.component.ts" title="src/app/app.component.ts">
</code-example>
~~~ {.l-sub-section}
There are only two changes.
The `template` is simply the new element tag identified by the component's `selector` property.
This displays the hero form when the application component is loaded.
@ -233,15 +273,19 @@ You've also dropped the `name` field from the class body.
~~~
## Create an initial HTML form template
Create the template file with the following contents:
<code-example path="forms/src/app/hero-form.component.html" region="start">
<code-example path="forms/src/app/hero-form.component.html" region="start" title="src/app/hero-form.component.html">
</code-example>
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
opening them up for user input in input boxes.
@ -265,12 +309,16 @@ Bootstrap gives the form a little style.
Angular forms don't require a style library
</header>
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
the styles of any external library. Angular apps can use any CSS library or none at all.
~~~
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
@ -279,6 +327,8 @@ To add the stylesheet, open `index.html` and add the following link to the `<hea
</code-example>
## Add powers with _*ngFor_
The hero must choose one superpower from a fixed list of agency-approved powers.
@ -295,19 +345,25 @@ Add the following HTML *immediately below* the *Alter Ego* group:
</code-example>
This code repeats the `<option>` tag for each power in the list of powers.
The `pow` template input variable is a different power in each iteration;
you display its name using the interpolation syntax.
## Two-way data binding with _ngModel_
Running the app right now would be disappointing.
<figure class='image-display'>
<img src="assets/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"> </img>
<img src="assets/images/devguide/forms/hero-form-3.png" width="400px" alt="Early form with no binding"></img>
</figure>
You don't see hero data because you're not binding to the `Hero` yet.
You know how to do that from earlier pages.
[Displaying Data](guide/displaying-data) teaches property binding.
@ -331,6 +387,8 @@ Find the `<input>` tag for *Name* and update it like this:
~~~ {.l-sub-section}
You added a diagnostic interpolation after the input tag
so you can see what you're doing.
You left yourself a note to throw it away when you're done.
@ -338,6 +396,8 @@ You left yourself a note to throw it away when you're done.
~~~
Focus on the binding syntax: `[(ngModel)]="..."`.
If you ran the app now and started typing in the *Name* input box,
@ -347,23 +407,29 @@ At some point it might look like this:
<figure class='image-display'>
<img src="assets/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"> </img>
<img src="assets/images/devguide/forms/ng-model-in-action.png" width="400px" alt="ngModel in action"></img>
</figure>
The diagnostic is evidence that values really are flowing from the input box to the model and
back again.
~~~ {.l-sub-section}
That's *two-way data binding*.
For more information, see
[Two-way binding with NgModel](guide/template-syntax) on the
[Two-way binding with NgModel](guide/template-syntax#ngModel) on the
the [Template Syntax](guide/template-syntax) page.
~~~
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
@ -371,6 +437,8 @@ Defining a `name` attribute is a requirement when using `[(ngModel)]` in combina
~~~ {.l-sub-section}
Internally, Angular creates `FormControl` instances and
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
Each `FormControl` is registered under the name you assigned to the `name` attribute.
@ -379,6 +447,8 @@ Read more in [The NgForm directive](guide/forms#ngForm), later in this page.
~~~
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
You'll ditch the input box binding message
and add a new binding (at the top) to the component's `diagnostic` property.
@ -395,25 +465,33 @@ After revision, the core of the form should look like this:
~~~ {.l-sub-section}
- Each input element has an `id` property that is used by the `label` element's `for` attribute
* Each input element has an `id` property that is used by the `label` element's `for` attribute
to match the label to its input control.
- Each input element has a `name` property that is required by Angular forms to register the control with the form.
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
~~~
If you run the app now and change every hero model property, the form might display like this:
<figure class='image-display'>
<img src="assets/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"> </img>
<img src="assets/images/devguide/forms/ng-model-in-action-2.png" width="400px" alt="ngModel in action"></img>
</figure>
The diagnostic near the top of the form
confirms that all of your changes are reflected in the model.
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
## Track control state and validity with _ngModel_
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
@ -491,7 +569,9 @@ You can leverage those class names to change the appearance of the control.
</table>
Temporarily add a [template reference variable](guide/template-syntax) named `spy`
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
@ -499,6 +579,8 @@ to the _Name_ `<input>` tag and use it to display the input's CSS classes.
</code-example>
Now run the app and look at the _Name_ input box.
Follow these steps *precisely*:
@ -511,22 +593,28 @@ The actions and effects are as follows:
<figure class='image-display'>
<img src="assets/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition"> </img>
<img src="assets/images/devguide/forms/control-state-transitions-anim.gif" alt="Control State Transition"></img>
</figure>
You should see the following transitions and class names:
<figure class='image-display'>
<img src="assets/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"> </img>
<img src="assets/images/devguide/forms/ng-control-class-changes.png" width="500px" alt="Control state transitions"></img>
</figure>
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
strong visual signal when the values are invalid. You also want to mark required fields.
To create such visual feedback, add definitions for the `ng-*` CSS classes.
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
## Add custom CSS for visual feedback
You can mark required fields and invalid data at the same time with a colored bar
@ -534,17 +622,21 @@ on the left of the input box:
<figure class='image-display'>
<img src="assets/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"> </img>
<img src="assets/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form"></img>
</figure>
You achieve this effect by adding these class definitions to a new `forms.css` file
that you add to the project as a sibling to `index.html`:
<code-example path="forms/src/forms.css">
<code-example path="forms/src/forms.css" title="src/forms.css">
</code-example>
Update the `<head>` of `index.html` to include this style sheet:
@ -552,6 +644,8 @@ Update the `<head>` of `index.html` to include this style sheet:
</code-example>
## Show and hide validation error messages
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
@ -562,12 +656,15 @@ When the user deletes the name, the form should look like this:
<figure class='image-display'>
<img src="assets/images/devguide/forms/name-required-error.png" width="400px" alt="Name required"> </img>
<img src="assets/images/devguide/forms/name-required-error.png" width="400px" alt="Name required"></img>
</figure>
To achieve this effect, extend the `<input>` tag with the following:
- A [template reference variable](guide/template-syntax).
- The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
* A [template reference variable](guide/template-syntax#ref-vars).
* The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
Here's an example of an error message added to the _name_ input box:
@ -576,12 +673,16 @@ Here's an example of an error message added to the _name_ input box:
</code-example>
You need a template reference variable to access the input box's Angular control from within the template.
Here you created a variable called `name` and gave it the value "ngModel".
~~~ {.l-sub-section}
Why "ngModel"?
A directive's [exportAs](api/core/index/Directive-decorator) property
tells Angular how to link the reference variable to the directive.
@ -590,6 +691,8 @@ You set `name` to `ngModel` because the `ngModel` directive's `exportAs` propert
~~~
You control visibility of the name error message by binding properties of the `name`
control to the message `<div>` element's `hidden` property.
@ -598,6 +701,8 @@ control to the message `<div>` element's `hidden` property.
</code-example>
In this example, you hide the message when the control is valid or pristine;
"pristine" means the user hasn't changed the value since it was displayed in this form.
@ -621,16 +726,18 @@ Now you'll add a new hero in this form.
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset">
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset" title="src/app/hero-form.component.html (New Hero button)">
</code-example>
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" linenums="false">
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" title="src/app/hero-form.component.ts (New Hero method)" linenums="false">
</code-example>
Run the application again, click the *New Hero* button, and the form clears.
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
That's understandable as these are required fields.
@ -649,12 +756,16 @@ You have to clear all of the flags imperatively, which you can do
by calling the form's `reset()` method after calling the `newHero()` method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset">
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset" title="src/app/hero-form.component.html (Reset the form)">
</code-example>
Now clicking "New Hero" resets both the form and its control flags.
## Submit the form with _ngSubmit_
The user should be able to submit this form after filling it in.
@ -667,10 +778,12 @@ To make it useful, bind the form's `ngSubmit` event property
to the hero form component's `onSubmit()` method:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="forms/ts/src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example>
You added something extra at the end. You defined a
template reference variable, `#heroForm`, and initialized it with the value "ngForm".
@ -679,6 +792,8 @@ The variable `heroForm` is now a reference to the `NgForm` directive that govern
~~~ {.l-sub-section}
### The _NgForm_ directive
What `NgForm` directive?
@ -695,6 +810,8 @@ control* is valid.
~~~
You'll bind the form's overall validity via
the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code:
@ -704,6 +821,8 @@ using an event binding. Here's the code:
</code-example>
If you run the application now, you find that the button is enabled&mdash;although
it doesn't do anything useful yet.
@ -719,6 +838,8 @@ For you, it was as simple as this:
1. Define a template reference variable on the (enhanced) form element.
2. Refer to that variable in a button many lines away.
## Toggle two form regions (extra credit)
Submitting the form isn't terribly dramatic at the moment.
@ -726,6 +847,8 @@ Submitting the form isn't terribly dramatic at the moment.
~~~ {.l-sub-section}
An unsurprising observation for a demo. To be honest,
jazzing it up won't teach you anything new about forms.
But this is an opportunity to exercise some of your newly won
@ -735,6 +858,8 @@ If you aren't interested, skip to this page's conclusion.
~~~
For a more strikingly visual effect,
hide the data entry area and display something else.
@ -746,6 +871,8 @@ its `hidden` property to the `HeroFormComponent.submitted` property.
</code-example>
The main form is visible from the start because the
`submitted` property is false until you submit the form,
as this fragment from the `HeroFormComponent` shows:
@ -755,6 +882,8 @@ as this fragment from the `HeroFormComponent` shows:
</code-example>
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
as planned.
@ -766,6 +895,8 @@ Add the following HTML below the `<div>` wrapper you just wrote:
</code-example>
There's the hero again, displayed read-only with interpolation bindings.
This `<div>` appears only while the component is in the submitted state.
@ -774,20 +905,22 @@ that clears the `submitted` flag.
When you click the *Edit* button, this block disappears and the editable form reappears.
## Conclusion
The Angular form discussed in this page takes advantage of the following
framework features to provide support for data modification, validation, and more:
- An Angular HTML form template.
- A form component class with a `@Component` decorator.
- Handling form submission by binding to the `NgForm.ngSubmit` event property.
- Template-reference variables such as `#heroForm` and `#name`.
- `[(ngModel)]` syntax for two-way data binding.
- The use of `name` attributes for validation and form-element change tracking.
- The reference variables `valid` property on input controls to check if a control is valid and show/hide error messages.
- Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
- Custom CSS classes that provide visual feedback to users about invalid controls.
* An Angular HTML form template.
* A form component class with a `@Component` decorator.
* Handling form submission by binding to the `NgForm.ngSubmit` event property.
* Template-reference variables such as `#heroForm` and `#name`.
* `[(ngModel)]` syntax for two-way data binding.
* The use of `name` attributes for validation and form-element change tracking.
* The reference variables `valid` property on input controls to check if a control is valid and show/hide error messages.
* Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
* Custom CSS classes that provide visual feedback to users about invalid controls.
The final project folder structure should look like this:
@ -848,6 +981,8 @@ The final project folder structure should look like this:
</aio-filetree>
Heres the code for the final version of the application:

View File

@ -1,5 +1,7 @@
@description
Angular has its own vocabulary.
Most Angular terms are common English words
with a specific meaning within the Angular system.
@ -15,10 +17,14 @@ unexpected definitions.
{@a aot}
## Ahead-of-time (AOT) compilation
~~~ {.l-sub-section}
You can compile Angular applications at build time.
By compiling your application using the compiler-cli, `ngc`, you can bootstrap directly
to a module factory, meaning you don't need to include the Angular compiler in your JavaScript bundle.
@ -27,10 +33,14 @@ Ahead-of-time compiled applications also benefit from decreased load time and in
~~~
## Angular module
~~~ {.l-sub-section}
Helps you organize an application into cohesive blocks of functionality.
An Angular module identifies the components, directives, and pipes that the application uses along with the list of external Angular modules that the application needs, such as `FormsModule`.
@ -42,10 +52,14 @@ For details and examples, see the [Angular Modules (NgModule)](guide/ngmodule) p
~~~
## Annotation
~~~ {.l-sub-section}
In practice, a synonym for [Decoration](guide/glossary#decorator).
@ -57,10 +71,14 @@ In practice, a synonym for [Decoration](guide/glossary#decorator).
{@a attribute-directives}
## Attribute directives
~~~ {.l-sub-section}
A category of [directive](guide/glossary#directive) that can listen to and modify the behavior of
other HTML elements, attributes, properties, and components. They are usually represented
as HTML attributes, hence the name.
@ -73,10 +91,14 @@ Learn about them in the [_Attribute Directives_](guide/attribute-directives) gui
~~~
## Barrel
~~~ {.l-sub-section}
A way to *roll up exports* from several ES2015 modules into a single convenient ES2015 module.
The barrel itself is an ES2015 module file that re-exports *selected* exports of other ES2015 modules.
@ -84,42 +106,52 @@ For example, imagine three ES2015 modules in a `heroes` folder:
<code-example>
// heroes/hero.component.ts
export class HeroComponent {}
export class HeroComponent {}
// heroes/hero.model.ts
export class Hero {}
// heroes/hero.model.ts
export class Hero {}
// heroes/hero.service.ts
export class HeroService {}
// heroes/hero.service.ts
export class HeroService {}
</code-example>
Without a barrel, a consumer needs three import statements:
<code-example>
import { HeroComponent } from '../heroes/hero.component.ts';
import { Hero } from '../heroes/hero.model.ts';
import { HeroService } from '../heroes/hero.service.ts';
import { Hero } from '../heroes/hero.model.ts';
import { HeroService } from '../heroes/hero.service.ts';
</code-example>
You can add a barrel to the `heroes` folder (called `index`, by convention) that exports all of these items:
<code-example>
export * from './hero.model.ts'; // re-export all of its exports
export * from './hero.service.ts'; // re-export all of its exports
export { HeroComponent } from './hero.component.ts'; // re-export the named thing
export * from './hero.service.ts'; // re-export all of its exports
export { HeroComponent } from './hero.component.ts'; // re-export the named thing
</code-example>
Now a consumer can import what it needs from the barrel.
<code-example>
import { Hero, HeroService } from '../heroes'; // index is implied
</code-example>
The Angular [scoped packages](guide/glossary#scoped-package) each have a barrel named `index`.
~~~ {.alert.is-important}
You can often achieve the same result using [Angular modules](guide/glossary#angular-module) instead.
@ -129,10 +161,14 @@ You can often achieve the same result using [Angular modules](guide/glossary#ang
~~~
## Binding
~~~ {.l-sub-section}
Usually refers to [data binding](guide/glossary#data-binding) and the act of
binding an HTML object property to a data object property.
@ -142,10 +178,14 @@ between a "token"&mdash;also referred to as a "key"&mdash;and a dependency [prov
~~~
## Bootstrap
~~~ {.l-sub-section}
You launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`).
Bootstrapping identifies an application's top level "root" [component](guide/glossary#component),
which is the first component that is loaded for the application.
@ -157,10 +197,14 @@ You can bootstrap multiple apps in the same `index.html`, each app with its own
~~~
## camelCase
~~~ {.l-sub-section}
The practice of writing compound words or phrases such that each word or abbreviation begins with a capital letter
_except the first letter, which is lowercase_.
@ -175,10 +219,14 @@ In Angular documentation, "camelCase" always means *lower camel case*.
{@a component}
## Component
~~~ {.l-sub-section}
An Angular class responsible for exposing data to a [view](guide/glossary#view) and handling most of the views display and user-interaction logic.
The *component* is one of the most important building blocks in the Angular system.
@ -196,10 +244,14 @@ the component in the role of "controller" or "view model".
~~~
## dash-case
~~~ {.l-sub-section}
The practice of writing compound words or phrases such that each word is separated by a dash or hyphen (`-`).
This form is also known as kebab-case.
@ -210,10 +262,14 @@ spelled in dash-case.
~~~
## Data binding
~~~ {.l-sub-section}
Applications display data values to a user and respond to user
actions (such as clicks, touches, and keystrokes).
@ -227,13 +283,14 @@ Angular has a rich data-binding framework with a variety of data-binding
operations and supporting declaration syntax.
Read about the following forms of binding in the [Template Syntax](guide/template-syntax) page:
* [Interpolation](guide/template-syntax).
* [Property binding](guide/template-syntax).
* [Event binding](guide/template-syntax).
* [Attribute binding](guide/template-syntax).
* [Class binding](guide/template-syntax).
* [Style binding](guide/template-syntax).
* [Two-way data binding with ngModel](guide/template-syntax).
* [Interpolation](guide/template-syntax#interpolation).
* [Property binding](guide/template-syntax#property-binding).
* [Event binding](guide/template-syntax#event-binding).
* [Attribute binding](guide/template-syntax#attribute-binding).
* [Class binding](guide/template-syntax#class-binding).
* [Style binding](guide/template-syntax#style-binding).
* [Two-way data binding with ngModel](guide/template-syntax#ngModel).
~~~
@ -244,10 +301,14 @@ operations and supporting declaration syntax.
{@a decoration}
## Decorator | decoration
~~~ {.l-sub-section}
A *function* that adds metadata to a class, its members (properties, methods) and function arguments.
Decorators are a JavaScript language [feature](https://github.com/wycats/javascript-decorators), implemented in TypeScript and proposed for ES2016 (also known as ES7).
@ -272,6 +333,8 @@ classes that follow it in the file.
~~~ {.alert.is-important}
Always include parentheses `()` when applying a decorator.
@ -281,10 +344,14 @@ Always include parentheses `()` when applying a decorator.
~~~
## Dependency injection
~~~ {.l-sub-section}
A design pattern and mechanism
for creating and delivering parts of an application to other
parts of an application that request them.
@ -311,7 +378,7 @@ into other application parts where and when needed.
At the core, an [`injector`](guide/glossary#injector) returns dependency values on request.
The expression `injector.get(token)` returns the value associated with the given token.
A token is an Angular type (`OpaqueToken`). You rarely need to work with tokens directly; most
A token is an Angular type (`InjectionToken`). You rarely need to work with tokens directly; most
methods accept a class name (`Foo`) or a string ("foo") and Angular converts it
to a token. When you write `injector.get(Foo)`, the injector returns
the value associated with the token for the `Foo` class, typically an instance of `Foo` itself.
@ -343,10 +410,14 @@ Read more in the [Dependency Injection](guide/dependency-injection) page.
{@a directives}
## Directive
~~~ {.l-sub-section}
An Angular class responsible for creating, reshaping, and interacting with HTML elements
in the browser DOM. The directive is Angular's most fundamental feature.
@ -380,10 +451,14 @@ elements and their children.
~~~
## ECMAScript
~~~ {.l-sub-section}
The [official JavaScript language specification](https://en.wikipedia.org/wiki/ECMAScript).
The latest approved version of JavaScript is
@ -401,26 +476,38 @@ Angular developers can write in ES5 directly.
~~~
## ES2015
~~~ {.l-sub-section}
Short hand for [ECMAScript](guide/glossary#ecmascript) 2015.
~~~
## ES5
~~~ {.l-sub-section}
Short hand for [ECMAScript](guide/glossary#ecmascript) 5, the version of JavaScript run by most modern browsers.
~~~
## ES6
~~~ {.l-sub-section}
Short hand for [ECMAScript](guide/glossary#ecmascript) 2015.
@ -436,10 +523,14 @@ Short hand for [ECMAScript](guide/glossary#ecmascript) 2015.
{@a H}
## Injector
~~~ {.l-sub-section}
An object in the Angular [dependency-injection system](guide/glossary#dependency-injection)
that can find a named dependency in its cache or create a dependency
with a registered [provider](guide/glossary#provider).
@ -447,24 +538,32 @@ with a registered [provider](guide/glossary#provider).
~~~
## Input
~~~ {.l-sub-section}
A directive property that can be the *target* of a
[property binding](guide/template-syntax) (explained in detail in the [Template Syntax](guide/template-syntax) page).
[property binding](guide/template-syntax#property-binding) (explained in detail in the [Template Syntax](guide/template-syntax) page).
Data values flow *into* this property from the data source identified
in the template expression to the right of the equal sign.
See the [Input and output properties](guide/template-syntax) section of the [Template Syntax](guide/template-syntax) page.
See the [Input and output properties](guide/template-syntax#inputs-outputs) section of the [Template Syntax](guide/template-syntax) page.
~~~
## Interpolation
~~~ {.l-sub-section}
A form of [property data binding](guide/glossary#data-binding) in which a
[template expression](guide/glossary#template-expression) between double-curly braces
renders as text. That text may be concatenated with neighboring text
@ -477,7 +576,9 @@ or displayed between element tags, as in this example.
</code-example>
Read more about [interpolation](guide/template-syntax) in the
Read more about [interpolation](guide/template-syntax#interpolation) in the
[Template Syntax](guide/template-syntax) page.
@ -487,10 +588,14 @@ Read more about [interpolation](guide/template-syntax) in the
{@a jit}
## Just-in-time (JIT) compilation
~~~ {.l-sub-section}
A bootstrapping method of compiling components and modules in the browser
and launching the application dynamically. Just-in-time mode is a good choice during development.
Consider using the [ahead-of-time](guide/glossary#aot) mode for production apps.
@ -499,20 +604,28 @@ Consider using the [ahead-of-time](guide/glossary#aot) mode for production apps.
~~~
## kebab-case
~~~ {.l-sub-section}
See [dash-case](guide/glossary#dash-case).
~~~
## Lifecycle hooks
~~~ {.l-sub-section}
[Directives](guide/glossary#directive) and [components](guide/glossary#component) have a lifecycle
managed by Angular as it creates, updates, and destroys them.
@ -523,6 +636,7 @@ Each interface has a single hook method whose name is the interface name prefixe
For example, the `OnInit` interface has a hook method named `ngOnInit`.
Angular calls these hook methods in the following order:
* `ngOnChanges`: when an [input](guide/glossary#input)/[output](guide/glossary#output) binding value changes.
* `ngOnInit`: after the first `ngOnChanges`.
* `ngDoCheck`: developer's custom change detection.
@ -538,6 +652,8 @@ Read more in the [Lifecycle Hooks](guide/lifecycle-hooks) page.
~~~
## Module
~~~ {.l-sub-section}
@ -546,14 +662,19 @@ Read more in the [Lifecycle Hooks](guide/lifecycle-hooks) page.
~~~ {.alert.is-important}
Angular has the following types of modules:
- [Angular modules](guide/glossary#angular-module).
* [Angular modules](guide/glossary#angular-module).
For details and examples, see the [Angular Modules](guide/ngmodule) page.
- ES2015 modules, as described in this section.
* ES2015 modules, as described in this section.
~~~
A cohesive block of code dedicated to a single purpose.
Angular apps are modular.
@ -585,10 +706,14 @@ You rarely access Angular feature modules directly. You usually import them from
{@a N}
## Observable
~~~ {.l-sub-section}
An array whose items arrive asynchronously over time.
Observables help you manage asynchronous data, such as data coming from a backend service.
Observables are used within Angular itself, including Angular's event system and its HTTP client service.
@ -599,26 +724,34 @@ Observables are a proposed feature for ES2016, the next version of JavaScript.
~~~
## Output
~~~ {.l-sub-section}
A directive property that can be the *target* of event binding
(read more in the [event binding](guide/template-syntax)
(read more in the [event binding](guide/template-syntax#event-binding)
section of the [Template Syntax](guide/template-syntax) page).
Events stream *out* of this property to the receiver identified
in the template expression to the right of the equal sign.
See the [Input and output properties](guide/template-syntax) section of the [Template Syntax](guide/template-syntax) page.
See the [Input and output properties](guide/template-syntax#inputs-outputs) section of the [Template Syntax](guide/template-syntax) page.
~~~
## PascalCase
~~~ {.l-sub-section}
The practice of writing individual words, compound words, or phrases such that each word or abbreviation begins with a capital letter.
Class names are typically spelled in PascalCase. For example, `Person` and `HeroDetailComponent`.
@ -628,10 +761,14 @@ In this documentation, "PascalCase" means *upper camel case* and "camelCase" me
~~~
## Pipe
~~~ {.l-sub-section}
An Angular pipe is a function that transforms input values to output values for
display in a [view](guide/glossary#view).
Here's an example that uses the built-in `currency` pipe to display
@ -643,16 +780,22 @@ a numeric value in the local currency.
</code-example>
You can also write your own custom pipes.
Read more in the page on [pipes](guide/pipes).
~~~
## Provider
~~~ {.l-sub-section}
A _provider_ creates a new instance of a dependency for the
[dependency injection](guide/glossary#dependency-injection) system.
It relates a lookup token to code&mdash;sometimes called a "recipe"&mdash;that can create a dependency value.
@ -664,28 +807,37 @@ It relates a lookup token to code&mdash;sometimes called a "recipe"&mdash;that c
{@a Q}
## Reactive forms
~~~ {.l-sub-section}
A technique for building Angular forms through code in a component.
The alternative technique is [template-driven forms](guide/glossary#template-driven-forms).
When building reactive forms:
- The "source of truth" is the component. The validation is defined using code in the component.
- Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
- The template input elements do *not* use `ngModel`.
- The associated Angular directives are all prefixed with `Form`, such as `FormGroup`, `FormControl`, and `FormControlName`.
* The "source of truth" is the component. The validation is defined using code in the component.
* Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`.
* The template input elements do *not* use `ngModel`.
* The associated Angular directives are all prefixed with `Form`, such as `FormGroup`, `FormControl`, and `FormControlName`.
Reactive forms are powerful, flexible, and a good choice for more complex data-entry form scenarios, such as dynamic generation of form controls.
~~~
## Router
~~~ {.l-sub-section}
Most applications consist of many screens or [views](guide/glossary#view).
The user navigates among them by clicking links and buttons,
and performing other similar actions that cause the application to
@ -708,10 +860,14 @@ For more information, see the [Routing & Navigation](guide/router) page.
~~~
## Router module
~~~ {.l-sub-section}
A separate [Angular module](guide/glossary#angular-module) that provides the necessary service providers and directives for navigating through application views.
For more information, see the [Routing & Navigation](guide/router) page.
@ -719,10 +875,14 @@ For more information, see the [Routing & Navigation](guide/router) page.
~~~
## Routing component
~~~ {.l-sub-section}
An Angular [component](guide/glossary#component) with a `RouterOutlet` that displays views based on router navigations.
For more information, see the [Routing & Navigation](guide/router) page.
@ -731,10 +891,14 @@ For more information, see the [Routing & Navigation](guide/router) page.
~~~
## Scoped package
~~~ {.l-sub-section}
A way to group related *npm* packages.
Read more at the [npm-scope](https://docs.npmjs.com/misc/scope) page.
@ -746,7 +910,7 @@ The only difference, from a consumer perspective,
is that the scoped package name begins with the Angular *scope name*, `@angular`.
<code-example path="architecture/src/app/app.component.ts" linenums="false" title="architecture/ts/src/app/app.component.ts (import)" region="import">
<code-example path="architecture/src/app/app.component.ts" linenums="false" title="architecture/src/app/app.component.ts (import)" region="import">
</code-example>
@ -754,10 +918,14 @@ is that the scoped package name begins with the Angular *scope name*, `@angular`
~~~
## Service
~~~ {.l-sub-section}
For data or logic that is not associated
with a specific view or that you want to share across components, build services.
@ -778,10 +946,14 @@ For more information, see the [Services](guide/.ial/toh-pt4) page of the [Tour o
{@a snake-case}
## snake_case
~~~ {.l-sub-section}
The practice of writing compound words or phrases such that an
underscore (`_`) separates one word from the next. This form is also known as *underscore case*.
@ -794,10 +966,14 @@ underscore (`_`) separates one word from the next. This form is also known as *u
{@a structural-directives}
## Structural directives
~~~ {.l-sub-section}
A category of [directive](guide/glossary#directive) that can
shape or reshape HTML layout, typically by adding and removing elements in the DOM.
The `ngIf` "conditional element" directive and the `ngFor` "repeater" directive are well-known examples.
@ -808,10 +984,14 @@ Read more in the [Structural Directives](guide/structural-directives) page.
~~~
## Template
~~~ {.l-sub-section}
A chunk of HTML that Angular uses to render a [view](guide/glossary#view) with
the support and guidance of an Angular [directive](guide/glossary#directive),
most notably a [component](guide/glossary#component).
@ -819,18 +999,23 @@ most notably a [component](guide/glossary#component).
~~~
## Template-driven forms
~~~ {.l-sub-section}
A technique for building Angular forms using HTML forms and input elements in the view.
The alternate technique is [Reactive Forms](guide/glossary#reactive-forms).
When building template-driven forms:
- The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
- [Two-way binding](guide/glossary#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
- Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
- The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
* The "source of truth" is the template. The validation is defined using attributes on the individual input elements.
* [Two-way binding](guide/glossary#data-binding) with `ngModel` keeps the component model synchronized with the user's entry into the input elements.
* Behind the scenes, Angular creates a new control for each input element, provided you have set up a `name` attribute and two-way binding for each input.
* The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`.
Template-driven forms are convenient, quick, and simple. They are a good choice for many basic data-entry form scenarios.
@ -840,34 +1025,46 @@ in the [Forms](guide/forms) page.
~~~
## Template expression
~~~ {.l-sub-section}
A TypeScript-like syntax that Angular evaluates within
a [data binding](guide/glossary#data-binding).
Read about how to write template expressions
in the [Template expressions](guide/template-syntax) section
in the [Template expressions](guide/template-syntax#template-expressions) section
of the [Template Syntax](guide/template-syntax) page.
~~~
## Transpile
~~~ {.l-sub-section}
The process of transforming code written in one form of JavaScript
(such as TypeScript) into another form of JavaScript (such as [ES5](guide/glossary#es5)).
~~~
## TypeScript
~~~ {.l-sub-section}
A version of JavaScript that supports most [ECMAScript 2015](guide/glossary#es2015)
language features such as [decorators](guide/glossary#decorator).
@ -888,10 +1085,14 @@ Read more about TypeScript at [typescriptlang.org](http://www.typescriptlang.org
{@a U}
## View
~~~ {.l-sub-section}
A portion of the screen that displays information and responds
to user actions such as clicks, mouse moves, and keystrokes.
@ -917,10 +1118,14 @@ under the control of a [router](guide/glossary#router).
{@a Y}
## Zone
~~~ {.l-sub-section}
A mechanism for encapsulating and intercepting
a JavaScript application's asynchronous activity.

View File

@ -6,6 +6,8 @@ Angular's hierarchical dependency injection system supports nested injectors in
@description
You learned the basics of Angular Dependency injection in the
[Dependency Injection](guide/dependency-injection) guide.
@ -17,6 +19,8 @@ This guide explores this system and how to use it to your advantage.
Try the <live-example></live-example>.
## The injector tree
In the [Dependency Injection](guide/dependency-injection) guide,
@ -30,6 +34,8 @@ The tree of components parallels the tree of injectors.
~~~ {.l-sub-section}
The component's injector may be a _proxy_ for an ancestor injector higher in the component tree.
That's an implementation detail that improves efficiency.
You won't notice the difference and
@ -38,6 +44,8 @@ your mental model should be that every component has its own injector.
~~~
Consider this guide's variation on the Tour of Heroes application.
At the top is the `AppComponent` which has some sub-components.
One of them is the `HeroesListComponent`.
@ -47,9 +55,11 @@ open simultaneously.
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"> </img>
<img src="assets/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="600"></img>
</figure>
### Injector bubbling
When a component requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector.
@ -61,6 +71,8 @@ If it runs out of ancestors, Angular throws an error.
~~~ {.l-sub-section}
You can cap the bubbling. An intermediate component can declare that it is the "host" component.
The hunt for providers will climb no higher than the injector for that host component.
This is a topic for another day.
@ -68,6 +80,8 @@ This is a topic for another day.
~~~
### Re-providing a service at different levels
You can re-register a provider for a particular dependency token at multiple levels of the injector tree.
@ -81,6 +95,8 @@ It effectively "reconfigures" and "shadows" a provider at a higher level in the
If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat.
All requests bubble up to the root <code>NgModule</code> injector that you configured with the `bootstrapModule` method.
## Component injectors
The ability to configure one or more providers at different levels opens up interesting and useful possibilities.
@ -105,6 +121,8 @@ Instead, provide the `VillainsService` in the `providers` metadata of the `Villa
</code-example>
By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else,
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
It's still a singleton, but it's a singleton that exist solely in the _villain_ domain.
@ -131,9 +149,11 @@ Each tax return component has the following characteristics:
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"> </img>
<img src="assets/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"></img>
</figure>
One might suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes.
That would be a pretty easy task for a simple hero tax return.
In the real world, with a rich tax return data model, the change management would be tricky.
@ -144,17 +164,21 @@ It caches a single `HeroTaxReturn`, tracks changes to that return, and can save
It also delegates to the application-wide singleton `HeroService`, which it gets by injection.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts">
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.service.ts" title="src/app/hero-tax-return.service.ts">
</code-example>
Here is the `HeroTaxReturnComponent` that makes use of it.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts">
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" title="src/app/hero-tax-return.component.ts">
</code-example>
The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters.
The setter initializes the component's own instance of the `HeroTaxReturnService` with the incoming return.
The getter always returns what that service says is the current state of the hero.
@ -172,6 +196,8 @@ Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `provi
</code-example>
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
Recall that every component _instance_ has its own injector.
Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service.
@ -180,12 +206,16 @@ No tax return overwriting. No mess.
~~~ {.l-sub-section}
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.
You can review it and download it from the <live-example></live-example>.
~~~
### Scenario: specialized providers
Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service,
@ -204,9 +234,11 @@ Component (B) is the parent of another component (C) that defines its own, even
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/car-components.png" alt="car components" width="220"> </img>
<img src="assets/images/devguide/dependency-injection/car-components.png" alt="car components" width="220"></img>
</figure>
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
When you resolve an instance of `Car` at the deepest component (C),
@ -215,13 +247,15 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
<figure class='image-display'>
<img src="assets/images/devguide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"> </img>
<img src="assets/images/devguide/dependency-injection/injector-tree.png" alt="car injector tree" width="600"></img>
</figure>
~~~ {.l-sub-section}
The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample
which you can review and download from the <live-example></live-example>.

View File

@ -8,6 +8,8 @@ Translate the app's template text into multiple languages.
{@a top}
Angular's _internationalization_ (_i18n_) tools help make your app available in multiple languages.
## Table of contents
@ -20,9 +22,13 @@ Angular's _internationalization_ (_i18n_) tools help make your app available in
* [Create a translation source file with the **_ng-xi18n_ extraction tool**](guide/i18n#ng-xi18n)
* [Translate text messages](guide/i18n#translate)
* [Merge the completed translation file into the app](guide/i18n#merge)
* [Merge with the JIT compiler](guide/i18n#jit)
* [Internationalization with the AOT compiler](guide/i18n#aot)
* [Translation file maintenance and _id_ changes](guide/i18n#maintenance)
**Try this** <live-example name="cb-i18n" title="i18n Example in Spanish">live example</live-example>
of a JIT-compiled app, translated into Spanish.
@ -30,6 +36,8 @@ of a JIT-compiled app, translated into Spanish.
{@a angular-i18n}
## Angular and _i18n_ template translation
Application internationalization is a challenging, many-faceted effort that
@ -43,12 +51,16 @@ into multiple languages.
~~~ {.l-sub-section}
Practitioners of _internationalization_ refer to a translatable text as a "_message_".
This page uses the words "_text_" and "_message_" interchangably and in the combination, "_text message_".
~~~
The _i18n_ template translation process has four phases:
1. Mark static text messages in your component templates for translation.
@ -67,6 +79,8 @@ You need to build and deploy a separate version of the application for each supp
{@a i18n-attribute}
## Mark text with the _i18n_ attribute
The Angular `i18n` attribute is a marker for translatable content.
@ -75,6 +89,8 @@ Place it on every element tag whose fixed text should be translated.
~~~ {.alert.is-helpful}
`i18n` is not an Angular _directive_.
It's a custom _attribute_, recognized by Angular tools and compilers.
After translation, the compiler removes it.
@ -82,20 +98,26 @@ After translation, the compiler removes it.
~~~
In the accompanying sample, an `<h1>` tag displays a simple English language greeting
that you translate into Spanish:
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" linenums="false">
<code-example path="cb-i18n/src/app/app.component.1.html" region="greeting" title="src/app/app.component.html" linenums="false">
</code-example>
Add the `i18n` attribute to the tag to mark it for translation.
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" linenums="false">
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute" title="src/app/app.component.html" linenums="false">
</code-example>
### Help the translator with a _description_ and _intent_
In order to translate it accurately, the translator may
@ -103,10 +125,12 @@ need a description of the message.
Assign a description to the i18n attribute:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" linenums="false">
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-attribute-desc" title="src/app/app.component.html" linenums="false">
</code-example>
In order to deliver a correct translation, the translator may need to
know your _intent_&mdash;the true _meaning_ of the text
within _this particular_ application context.
@ -114,10 +138,12 @@ In front of the description, add some contextual meaning to the assigned string,
separating it from the description with the `|` character (`<meaning>|<description>`):
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-attribute-meaning" title="src/app/app.component.html" linenums="false">
</code-example>
While all appearances of a message with the _same_ meaning have the _same_ translation,
a message with *a variety of possible meanings* could have different translations.
The Angular extraction tool preserves both the _meaning_ and the _description_ in the translation source file
@ -134,14 +160,16 @@ Here are two techniques to try.
(1) Wrap the text in an `<ng-container>` element. The `<ng-container>` is never renderered:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-ng-container" title="src/app/app.component.html" linenums="false">
</code-example>
(2) Wrap the text in a pair of HTML comments:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-with-comment" title="src/app/app.component.html" linenums="false">
</code-example>
@ -149,14 +177,18 @@ Here are two techniques to try.
{@a translate-attributes}
## Add _i18n-..._ translation attributes
You've added an image to your template. You care about accessibility too so you add a `title` attribute:
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" linenums="false">
<code-example path="cb-i18n/src/app/app.component.1.html" region="i18n-title" title="src/app/app.component.html" linenums="false">
</code-example>
The `title` attribute needs to be translated.
Angular i18n support has more translation attributes in the form,`i18n-x`, where `x` is the
name of the attribute to translate.
@ -164,15 +196,19 @@ name of the attribute to translate.
To translate the `title` on the `img` tag from the previous example, write:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-title-translate" title="src/app/app.component.html" linenums="false">
</code-example>
You can also assign a meaning and a description with the `i18n-x="<meaning>|<description>"` syntax.
{@a cardinality}
## Handle singular and plural
Different languages have different pluralization rules.
@ -184,10 +220,12 @@ Other languages might express the _cardinality_ differently.
Here's how you could mark up the component template to display the phrase appropriate to the number of wolves:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-plural" title="src/app/app.component.html" linenums="false">
</code-example>
* The first parameter is the key. It is bound to the component property (`wolves`)
that determines the number of wolves.
* The second parameter identifies this as a `plural` translation type.
@ -195,6 +233,7 @@ that determines the number of wolves.
categories and their matching values.
Pluralization categories include:
* =0
* =1
* =5
@ -203,6 +242,7 @@ Pluralization categories include:
Put the default _English_ translation in braces (`{}`) next to the pluralization category.
* When you're talking about one wolf, you could write `=1 {one wolf}`.
* For zero wolves, you could write `=0 {no wolves}`.
* For two wolves, you could write `=2 {two wolves}`.
@ -213,6 +253,8 @@ and write something like: `other {a wolf pack}`.
~~~ {.l-sub-section}
This syntax conforms to the
<a href="http://userguide.icu-project.org/formatparse/messages" target="_blank" title="ICU Message Format">ICU Message Format</a>
that derives from the
@ -226,6 +268,8 @@ which specifies the
{@a select}
## Select among alternative texts
The application displays different text depending upon whether the hero is male or female.
These text alternatives require translation too.
@ -240,7 +284,7 @@ property, which outputs either an "m" or an "f".
The message maps those values to the appropriate translation:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false">
</code-example>
@ -248,6 +292,8 @@ The message maps those values to the appropriate translation:
{@a ng-xi18n}
## Create a translation source file with the _ng-xi18n_ tool
Use the **_ng-xi18n_ extraction tool** to extract the `i18n`-marked texts
@ -262,6 +308,8 @@ If you haven't already installed the CLI and its `platform-server` peer dependen
</code-example>
Open a terminal window at the root of the application project and enter the `ng-xi18n` command:
@ -274,16 +322,22 @@ Open a terminal window at the root of the application project and enter the `ng-
~~~ {.l-sub-section}
Windows users may have to quote the command like this: `"./node_modules/.bin/ng-xi18n"`
~~~
By default, the tool generates a translation file named **`messages.xlf`** in the
<a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">XML Localisation Interchange File Format (XLIFF, version 1.2)</a>.
{@a other-formats}
### Other translation formats
You can generate a file named **`messages.xmb`** in the
@ -296,10 +350,14 @@ by adding the `--i18nFormat=xmb` flag.
</code-example>
This sample sticks with the _XLIFF_ format.
{@a ng-xi18n-options}
### Other options
You may have to specify additional options.
For example, if the `tsconfig.json` TypeScript configuration
@ -308,7 +366,7 @@ you must identify the path to it with the `-p` option:
<code-example language="sh" class="code-shell">
./node_modules/.bin/ng-xi18n -p path/to/tsconfig.json
./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json
./node_modules/.bin/ng-xi18n --i18nFormat=xmb -p path/to/tsconfig.json
</code-example>
@ -316,6 +374,8 @@ you must identify the path to it with the `-p` option:
{@a npm-i18n-script}
### Add an _npm_ script for convenience
Consider adding a convenience shortcut to the `scripts` section of the `package.json`
@ -323,25 +383,31 @@ to make the command easier to remember and run:
<code-example format='.' language='sh'>
"scripts": {
"i18n": "ng-xi18n",
...
}
"i18n": "ng-xi18n",
...
}
</code-example>
Now you can issue command variations such as these:
<code-example language="sh" class="code-shell">
npm run i18n
npm run i18n -- -p path/to/tsconfig.json
npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json
npm run i18n -- -p path/to/tsconfig.json
npm run i18n -- --i18nFormat=xmb -p path/to/tsconfig.json
</code-example>
Note the `--` flag before the options.
It tells _npm_ to pass every flag thereafter to `ng-xi18n`.
{@a translate}
## Translate text messages
The `ng-xi18n` command generates a translation source file
@ -352,6 +418,8 @@ files. The cookbook sample creates a Spanish translation file.
{@a localization-folder}
### Create a localization folder
You will probably translate into more than one other language so it's a good idea
@ -362,11 +430,15 @@ One approach is to dedicate a folder to localization and store related assets
~~~ {.l-sub-section}
Localization and internationalization are
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization" target="_blank">different but closely related terms</a>.
~~~
This cookbook follows that suggestion. It has a `locale` folder under the `src/`.
Assets within the folder carry a filename extension that matches a language-culture code from a
<a href="https://msdn.microsoft.com/en-us/library/ee825488(v=cs.20).aspx" target="_blank">well-known codeset</a>.
@ -384,16 +456,18 @@ This sample file is easy to translate without a special editor or knowledge of S
Open `messages.es.xlf` and find the first `<trans-unit>` section:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
This XML element represents the translation of the `<h1>` greeting tag you marked with the `i18n` attribute.
Using the _source_, _description_, and _meaning_ elements to guide your translation,
replace the `<target/>` tag with the Spanish greeting:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-hello" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;, after translation)" linenums="false">
</code-example>
@ -401,6 +475,8 @@ replace the `<target/>` tag with the Spanish greeting:
~~~ {.alert.is-important}
Note that the tool generates the `id`. **Don't touch it.**
Its value depends on the content of the message and its assigned meaning.
Change either factor and the `id` changes as well.
@ -409,16 +485,20 @@ See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
~~~
Translate the other text nodes the same way:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-other-nodes" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
{@a translate-plural-select}
## Translate _plural_ and _select_
Translating _plural_ and _select_ messages is a little tricky.
@ -429,22 +509,28 @@ However, the `XMB` format does support the ICU rules.
You'll just have to look for them in relation to other translation units that you recognize from elsewhere in the source template.
In this example, you know the translation unit for the `select` must be just below the translation unit for the logo.
### Translate _plural_
To translate a `plural`, translate its ICU format match values:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-plural" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
### Translate _select_
The `select` behaves a little differently. Here again is the ICU format message in the component template:
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" linenums="false">
<code-example path="cb-i18n/src/app/app.component.html" region="i18n-select" title="src/app/app.component.html" linenums="false">
</code-example>
The extraction tool broke that into _two_ translation units.
The first unit contains the text that was _outside_ the `select`.
@ -452,21 +538,25 @@ In place of the `select` is a placeholder, `<x id="ICU">`, that represents the `
Translate the text and leave the placeholder where it is.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-1" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
The second translation unit, immediately below the first one, contains the `select` message. Translate that.
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translate-select-2" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
Here they are together, after translation:
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" linenums="false">
<code-example path="cb-i18n/src/locale/messages.es.xlf.html" region="translated-select" title="src/locale/messages.es.xlf (&lt;trans-unit&gt;)" linenums="false">
</code-example>
@ -476,6 +566,8 @@ Here they are together, after translation:
</div>
The entire template translation is complete. It's
time to incorporate that translation into the application.
@ -484,6 +576,8 @@ time to incorporate that translation into the application.
</div>
### The app before translation
When the previous steps finish, the sample app _and_ its translation file are as follows:
@ -517,6 +611,8 @@ When the previous steps finish, the sample app _and_ its translation file are as
{@a merge}
## Merge the completed translation file into the app
To merge the translated text into component templates,
@ -525,6 +621,7 @@ The process is the same whether the file is in `.xlf` format or
in another format (`.xlif` and `.xtb`) that Angular understands.
You provide the Angular compiler with three new pieces of information:
* the translation file
* the translation file format
* the <a href="https://en.wikipedia.org/wiki/XLIFF" target="_blank">_Locale ID_</a>
@ -539,6 +636,8 @@ the JIT (_Just-in-Time_) compiler or the AOT (_Ahead-of-Time_) compiler.
{@a jit}
### Merge with the JIT compiler
The JIT compiler compiles the application in the browser as the application loads.
@ -551,15 +650,19 @@ Translation with the JIT compiler is a dynamic process of:
Open `index.html` and revise the launch script as follows:
<code-example path="cb-i18n/src/index.html" region="i18n" linenums="false">
<code-example path="cb-i18n/src/index.html" region="i18n" title="index.html (launch script)" linenums="false">
</code-example>
In this sample, the user's language is hardcoded as a global `document.locale` variable
in the `index.html`.
{@a text-plugin}
### SystemJS Text plugin
Notice the SystemJS mapping of `text` to a `systemjs-text-plugin.js`.
@ -570,10 +673,12 @@ You'll need it to import the language translation file.
SystemJS doesn't ship with a raw text plugin but it's easy to add.
Create the following `systemjs-text-plugin.js` in the `src/` folder:
<code-example path="cb-i18n/src/systemjs-text-plugin.js" linenums="false">
<code-example path="cb-i18n/src/systemjs-text-plugin.js" title="src/systemjs-text-plugin.js" linenums="false">
</code-example>
### Create translation providers
Three providers tell the JIT compiler how to translate the template texts for a particular language
@ -587,10 +692,12 @@ The `getTranslationProviders` function in the following `src/app/i18n-providers.
creates those providers based on the user's _locale_
and the corresponding translation file:
<code-example path="cb-i18n/src/app/i18n-providers.ts">
<code-example path="cb-i18n/src/app/i18n-providers.ts" title="src/app/i18n-providers.ts">
</code-example>
1. It gets the locale from the global `document.locale` variable that was set in `index.html`.
1. If there is no locale or the language is U.S. English (`en-US`), there is no need to translate.
@ -616,10 +723,12 @@ You'll create an _options_ object with the translation providers from `getTransl
and pass it to `bootstrapModule`.
Open the `src/main.ts` and modify the bootstrap code as follows:
<code-example path="cb-i18n/src/main.ts" linenums="false">
<code-example path="cb-i18n/src/main.ts" title="src/main.ts" linenums="false">
</code-example>
Notice that it waits for the `getTranslationProviders` promise to resolve before
bootstrapping the app.
@ -629,6 +738,8 @@ more languages.
{@a aot}
### _Internationalize_ with the AOT compiler
The JIT compiler translates the application into the target language
@ -657,6 +768,7 @@ Next, issue an `ngc` compile command for each supported language (including Engl
The result is a separate version of the application for each language.
Tell AOT how to translate by adding three options to the `ngc` command:
* `--i18nFile`: the path to the translation file
* `--locale`: the name of the locale
* `--i18nFormat`: the format of the localization file
@ -672,6 +784,8 @@ For this sample, the Spanish language command would be
~~~ {.l-sub-section}
Windows users may have to quote the command:
<code-example language="sh" class="code-shell">
@ -686,6 +800,8 @@ Windows users may have to quote the command:
{@a maintenance}
## Translation file maintenance and _id_ changes
As the application evolves, you will change the _i18n_ markup

View File

@ -6,6 +6,8 @@ How to read and use this documentation.
@description
This page describes the Angular documentation at a high level.
If you're new to Angular, you may want to visit "[Learning Angular](guide/learning-angular)" first.
@ -33,8 +35,10 @@ a collection of pages devoted to that theme.
</td>
<td>
A first taste of Angular<span if-docs="ts"> with zero installation.
Run "Hello World" in an online code editor and start playing with live code</span>.
Run "Hello World" in an online code editor and start playing with live code</span>.
</td>
</tr>
@ -46,9 +50,11 @@ a collection of pages devoted to that theme.
</td>
<td>
Learn the Angular basics (you're already here!) like the setup for local development,
displaying data and accepting user input, injecting application services into components,
and building simple forms.
displaying data and accepting user input, injecting application services into components,
and building simple forms.
</td>
</tr>
@ -60,6 +66,8 @@ a collection of pages devoted to that theme.
</td>
<td>
Authoritative details about each of the Angular libraries.
</td>
@ -72,8 +80,10 @@ a collection of pages devoted to that theme.
</td>
<td>
A step-by-step, immersive approach to learning Angular that
introduces the major features of Angular in an application context.
introduces the major features of Angular in an application context.
</td>
</tr>
@ -85,6 +95,8 @@ a collection of pages devoted to that theme.
</td>
<td>
In-depth analysis of Angular features and development practices.
</td>
@ -97,6 +109,8 @@ a collection of pages devoted to that theme.
</td>
<td>
Recipes for specific application challenges, mostly code snippets with a minimum of exposition.
</td>
@ -105,6 +119,8 @@ a collection of pages devoted to that theme.
</table>
A few early pages are written as tutorials and are clearly marked as such.
The rest of the pages highlight key points in code rather than explain each step necessary to build the sample.
You can always get the full source through the #{_liveLink}s.

View File

@ -9,15 +9,17 @@ A suggested path through the documentation for Angular newcomers.
<figure>
<img src="assets/images/devguide/intro/people.png" width="200px" height="152px" alt="Us" align="left" style="margin-left:-40px;margin-right:10px"> </img>
<img src="assets/images/devguide/intro/people.png" width="200px" height="152px" alt="Us" align="left" style="margin-left:-40px;margin-right:10px"></img>
</figure>
Everyone learns differently.
You don't have to read the documentation straight through. Most pages stand on their own.
Those new to Angular may wish to follow this popular learning path.
<br class="l-clear-left">
1. [Setup](guide/setup) for local Angular development, if you haven't already done so.
1. [Setup](guide/setup "Setup locally withe Quickstart seed") for local Angular development, if you haven't already done so.
1. Take the [*Tour of Heroes* tutorial](tutorial "Tour of Heroes").
@ -25,7 +27,7 @@ Those new to Angular may wish to follow this popular learning path.
to a full-featured example that demonstrates the essential characteristics of a professional application:
a sensible project structure, data binding, master/detail, services, dependency injection, navigation, and remote data access.
1. <a id="architecture"></a>Read the [Architecture](guide/architecture) overview for the big picture.
1. {@a architecture}Read the [Architecture](guide/architecture) overview for the big picture.
1. [The Root Module](guide/appmodule) introduces the `NgModule` class that tells Angular how to compile and run your application.
@ -45,10 +47,12 @@ After reading the above sections, feel free to skip around among the other pages
~~~ {.l-sub-section}
### Next Step
Try the [tutorial](tutorial "Tour of Heroes") if you're ready to start coding or
visit the [Architecture](guide/architecture) page if you prefer to learn the basic concepts first.
visit the [Architecture](guide/architecture "Basic Concepts") page if you prefer to learn the basic concepts first.
~~~

View File

@ -9,9 +9,11 @@ Angular calls lifecycle hook methods on directives and components as it creates,
<figure>
<img src="assets/images/devguide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"> </img>
<img src="assets/images/devguide/lifecycle-hooks/hooks-in-sequence.png" alt="Us" align="left" style="width:200px; margin-left:-40px;margin-right:30px"></img>
</figure>
A component has a lifecycle managed by Angular.
Angular creates it, renders it, creates and renders its children,
@ -22,7 +24,10 @@ that provide visibility into these key life moments and the ability to act when
A directive has the same set of lifecycle hooks, minus the hooks that are specific to component content and views.
<br class="l-clear-both">
## Contents
* [Component lifecycle hooks overview](guide/lifecycle-hooks#hooks-overview)
* [Lifecycle sequence](guide/lifecycle-hooks#hooks-purpose-timing)
* [Interfaces are optional (technically)](guide/lifecycle-hooks#interface-optional)
@ -30,21 +35,30 @@ A directive has the same set of lifecycle hooks, minus the hooks that are specif
* [Lifecycle examples](guide/lifecycle-hooks#the-sample)
* [Peek-a-boo: all hooks](guide/lifecycle-hooks#peek-a-boo)
* [Spying OnInit and OnDestroy](guide/lifecycle-hooks#spy)
* [OnInit](guide/lifecycle-hooks#oninit)
* [OnDestroy](guide/lifecycle-hooks#ondestroy)
* [OnChanges](guide/lifecycle-hooks#onchanges)
* [DoCheck](guide/lifecycle-hooks#docheck)
* [AfterView](guide/lifecycle-hooks#afterview)
* [Abide by the unidirectional data flow rule](guide/lifecycle-hooks#wait-a-tick)
* [AfterContent](guide/lifecycle-hooks#aftercontent)
* [Content projection](guide/lifecycle-hooks#content-projection)
* [AfterContent hooks](guide/lifecycle-hooks#aftercontent-hooks)
* [No unidirectional flow worries with _AfterContent_](guide/lifecycle-hooks#no-unidirectional-flow-worries)
Try the <live-example></live-example>.
{@a hooks-overview}
## Component lifecycle hooks overview
Directive and component instances have a lifecycle
as Angular creates, updates, and destroys them.
@ -55,16 +69,20 @@ Each interface has a single hook method whose name is the interface name prefixe
For example, the `OnInit` interface has a hook method named `ngOnInit()`
that Angular calls shortly after creating the component:
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" linenums="false">
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" title="peek-a-boo.component.ts (excerpt)" linenums="false">
</code-example>
No directive or component will implement all of the lifecycle hooks and some of the hooks only make sense for components.
Angular only calls a directive/component hook method *if it is defined*.
{@a hooks-purpose-timing}
## Lifecycle sequence
*After* creating a component/directive by calling its constructor, Angular
calls the lifecycle hook methods in the following sequence at specific moments:
@ -98,10 +116,12 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Respond when Angular (re)sets data-bound input properties.
The method receives a `SimpleChanges` object of current and previous property values.
Called before `ngOnInit()` and whenever one or more data-bound input properties change.
Respond when Angular (re)sets data-bound input properties.
The method receives a `SimpleChanges` object of current and previous property values.
Called before `ngOnInit()` and whenever one or more data-bound input properties change.
</td>
@ -114,10 +134,12 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties.
Called _once_, after the _first_ `ngOnChanges()`.
Initialize the directive/component after Angular first displays the data-bound properties
and sets the directive/component's input properties.
Called _once_, after the _first_ `ngOnChanges()`.
</td>
@ -130,9 +152,11 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Detect and act upon changes that Angular can't or won't detect on its own.
Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`.
Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`.
</td>
@ -145,11 +169,13 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Respond after Angular projects external content into the component's view.
Called _once_ after the first `ngDoCheck()`.
Called _once_ after the first `ngDoCheck()`.
_A component-only hook_.
_A component-only hook_.
</td>
@ -162,11 +188,13 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Respond after Angular checks the content projected into the component.
Called after the `ngAfterContentInit()` and every subsequent `ngDoCheck()`.
Called after the `ngAfterContentInit()` and every subsequent `ngDoCheck()`.
_A component-only hook_.
_A component-only hook_.
</td>
@ -179,11 +207,13 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Respond after Angular initializes the component's views and child views.
Called _once_ after the first `ngAfterContentChecked()`.
Called _once_ after the first `ngAfterContentChecked()`.
_A component-only hook_.
_A component-only hook_.
</td>
@ -196,11 +226,13 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Respond after Angular checks the component's views and child views.
Called after the `ngAfterViewInit` and every subsequent `ngAfterContentChecked()`.
Called after the `ngAfterViewInit` and every subsequent `ngAfterContentChecked()`.
_A component-only hook_.
_A component-only hook_.
</td>
@ -213,10 +245,12 @@ calls the lifecycle hook methods in the following sequence at specific moments:
</td>
<td>
Cleanup just before Angular destroys the directive/component.
Unsubscribe Observables and detach event handlers to avoid memory leaks.
Called _just before_ Angular destroys the directive/component.
Cleanup just before Angular destroys the directive/component.
Unsubscribe Observables and detach event handlers to avoid memory leaks.
Called _just before_ Angular destroys the directive/component.
</td>
@ -228,7 +262,9 @@ calls the lifecycle hook methods in the following sequence at specific moments:
{@a interface-optional}
## Interfaces are optional (technically)
## Interfaces are optional (technically)
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
The JavaScript language doesn't have interfaces.
@ -246,12 +282,18 @@ in order to benefit from strong typing and editor tooling.
{@a other-lifecycle-hooks}
## Other Angular lifecycle hooks
Other Angular sub-systems may have their own lifecycle hooks apart from these component hooks.
3rd party libraries might implement their hooks as well in order to give developers more
control over how these libraries are used.
## Lifecycle examples
The <live-example></live-example>
@ -293,8 +335,10 @@ Here's a brief description of each exercise:
</td>
<td>
Demonstrates every lifecycle hook.
Each hook method writes to the on-screen log.
Each hook method writes to the on-screen log.
</td>
</tr>
@ -306,12 +350,14 @@ Here's a brief description of each exercise:
</td>
<td>
Directives have lifecycle hooks too.
A `SpyDirective` can log when the element it spies upon is
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
This example applies the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
managed by the parent `SpyComponent`.
Directives have lifecycle hooks too.
A `SpyDirective` can log when the element it spies upon is
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
This example applies the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
managed by the parent `SpyComponent`.
</td>
</tr>
@ -323,9 +369,11 @@ Here's a brief description of each exercise:
</td>
<td>
See how Angular calls the `ngOnChanges()` hook with a `changes` object
every time one of the component input properties changes.
Shows how to interpret the `changes` object.
every time one of the component input properties changes.
Shows how to interpret the `changes` object.
</td>
</tr>
@ -337,8 +385,10 @@ Here's a brief description of each exercise:
</td>
<td>
Implements an `ngDoCheck()` method with custom change detection.
See how often Angular calls this hook and watch it post changes to a log.
See how often Angular calls this hook and watch it post changes to a log.
</td>
</tr>
@ -350,8 +400,10 @@ Here's a brief description of each exercise:
</td>
<td>
Shows what Angular means by a *view*.
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
</td>
</tr>
@ -363,9 +415,11 @@ Here's a brief description of each exercise:
</td>
<td>
Shows how to project external content into a component and
how to distinguish projected content from a component's view children.
Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks.
how to distinguish projected content from a component's view children.
Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks.
</td>
</tr>
@ -377,13 +431,15 @@ Here's a brief description of each exercise:
</td>
<td>
Demonstrates a combination of a component and a directive
each with its own hooks.
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
every time the parent component increments its input counter property.
Meanwhile, the `SpyDirective` from the previous example is applied
to the `CounterComponent` log where it watches log entries being created and destroyed.
Demonstrates a combination of a component and a directive
each with its own hooks.
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
every time the parent component increments its input counter property.
Meanwhile, the `SpyDirective` from the previous example is applied
to the `CounterComponent` log where it watches log entries being created and destroyed.
</td>
@ -391,11 +447,15 @@ Here's a brief description of each exercise:
</table>
The remainder of this page discusses selected exercises in further detail.
{@a peek-a-boo}
## Peek-a-boo: all hooks
The `PeekABooComponent` demonstrates all of the hooks in one component.
@ -405,9 +465,11 @@ The peek-a-boo exists to show how Angular calls the hooks in the expected order.
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
<figure class='image-display'>
<img src="assets/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"> </img>
<img src="assets/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo"></img>
</figure>
The sequence of log messages follows the prescribed hook calling order:
`OnChanges`, `OnInit`, `DoCheck`&nbsp;(3x), `AfterContentInit`, `AfterContentChecked`&nbsp;(3x),
`AfterViewInit`, `AfterViewChecked`&nbsp;(3x), and `OnDestroy`.
@ -415,11 +477,15 @@ The sequence of log messages follows the prescribed hook calling order:
~~~ {.l-sub-section}
The constructor isn't an Angular hook *per se*.
The log confirms that input properties (the `name` property in this case) have no assigned values at construction.
~~~
Had the user clicked the *Update Hero* button, the log would show another `OnChanges` and two more triplets of
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
Clearly these three hooks fire *often*. Keep the logic in these hooks as lean as possible!
@ -429,6 +495,8 @@ The next examples focus on hook details.
{@a spy}
## Spying *OnInit* and *OnDestroy*
Go undercover with these two spy hooks to discover when an element is initialized or destroyed.
@ -439,6 +507,8 @@ The heroes will never know they're being watched.
~~~ {.l-sub-section}
Kidding aside, pay attention to two key points:
1. Angular calls hook methods for *directives* as well as components.<br><br>
@ -452,30 +522,38 @@ But you can watch both with a directive.
~~~
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
that log messages to the parent via an injected `LoggerService`.
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" linenums="false">
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" title="src/app/spy.directive.ts" linenums="false">
</code-example>
You can apply the spy to any native or component element and it'll be initialized and destroyed
at the same time as that element.
Here it is attached to the repeated hero `<div>`:
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" linenums="false">
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" title="src/app/spy.component.html" linenums="false">
</code-example>
Each spy's birth and death marks the birth and death of the attached hero `<div>`
with an entry in the *Hook Log* as seen here:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"> </img>
<img src='assets/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive"></img>
</figure>
Adding a hero results in a new hero `<div>`. The spy's `ngOnInit()` logs that event.
The *Reset* button clears the `heroes` list.
@ -485,9 +563,12 @@ The spy's `ngOnDestroy()` method reports its last moments.
The `ngOnInit()` and `ngOnDestroy()` methods have more vital roles to play in real applications.
{@a oninit}
### _OnInit()_
Use `ngOnInit()` for two main reasons:
1. To perform complex initializations shortly after construction.
1. To set up the component after Angular sets the input properties.
@ -495,6 +576,8 @@ Experienced developers agree that components should be cheap and safe to constru
~~~ {.l-sub-section}
Misko Hevery, Angular team lead,
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
you should avoid complex constructor logic.
@ -502,13 +585,15 @@ you should avoid complex constructor logic.
~~~
Don't fetch data in a component constructor.
You shouldn't worry that a new component will try to contact a remote server when
created under test or before you decide to display it.
Constructors should do no more than set the initial local variables to simple values.
An `ngOnInit()` is a good place for a component to fetch its initial data. The
[Tour of Heroes Tutorial](tutorial/toh-pt4) and [HTTP Client](guide/server-communication)
[Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) and [HTTP Client](guide/server-communication#oninit)
guides show how.
@ -518,17 +603,23 @@ They'll have been set when `ngOnInit()` runs.
~~~ {.l-sub-section}
The `ngOnChanges()` method is your first opportunity to access those properties.
Angular calls `ngOnChanges()` before `ngOnInit()` and many times after that.
It only calls `ngOnInit()` once.
~~~
You can count on Angular to call the `ngOnInit()` method _soon_ after creating the component.
That's where the heavy initialization logic belongs.
{@a ondestroy}
### _OnDestroy()_
Put cleanup logic in `ngOnDestroy()`, the logic that *must* run before Angular destroys the directive.
@ -543,39 +634,49 @@ You risk memory leaks if you neglect to do so.
{@a onchanges}
## _OnChanges()_
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
This example monitors the `OnChanges` hook.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" linenums="false">
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" title="on-changes.component.ts (excerpt)" linenums="false">
</code-example>
The `ngOnChanges()` method takes an object that maps each changed property name to a
[SimpleChange](api/core/index/SimpleChange-class) object holding the current and previous property values.
This hook iterates over the changed properties and logs them.
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" linenums="false">
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" title="src/app/on-changes.component.ts" linenums="false">
</code-example>
The host `OnChangesParentComponent` binds to them like this:
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes">
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes" title="src/app/on-changes-parent.component.html">
</code-example>
Here's the sample in action as the user makes changes.
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"> </img>
<img src='assets/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges"></img>
</figure>
The log entries appear as the string value of the *power* property changes.
But the `ngOnChanges` does not catch changes to `hero.name`
That's surprising at first.
@ -588,30 +689,40 @@ The hero object *reference* didn't change so, from Angular's perspective, there
{@a docheck}
## _DoCheck()_
Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
~~~ {.l-sub-section}
Use this method to detect a change that Angular overlooked.
~~~
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" linenums="false">
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" title="DoCheckComponent (ngDoCheck)" linenums="false">
</code-example>
This code inspects certain _values of interest_, capturing and comparing their current state against previous values.
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
so you can see how often `DoCheck` is called. The results are illuminating:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"> </img>
<img src='assets/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck"></img>
</figure>
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
This hook is called with enormous frequency&mdash;after _every_
change detection cycle no matter where the change occurred.
@ -625,42 +736,52 @@ Clearly our implementation must be very lightweight or the user experience suffe
{@a afterview}
## AfterView
The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` hooks that Angular calls
*after* it creates a component's child views.
Here's a child view that displays a hero's name in an `<input>`:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="child-view" title="ChildComponent" linenums="false">
</code-example>
The `AfterViewComponent` displays this child view *within its template*:
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="template" title="AfterViewComponent (template)" linenums="false">
</code-example>
The following hooks take action based on changing values *within the child view*,
which can only be reached by querying for the child view via the property decorated with
[@ViewChild](api/core/index/ViewChild-decorator).
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" title="AfterViewComponent (class excerpts)" linenums="false">
</code-example>
{@a wait-a-tick}
### Abide by the unidirectional data flow rule
The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" title="AfterViewComponent (doSomething)" linenums="false">
</code-example>
Why does the `doSomething()` method wait a tick before updating `comment`?
Angular's unidirectional data flow rule forbids updates to the view *after* it has been composed.
@ -669,24 +790,32 @@ Both of these hooks fire _after_ the component's view has been composed.
Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!).
The `LoggerService.tick_then()` postpones the log update
for one turn of the browser's JavaScript cycle and that's just long enough.
Here's *AfterView* in action:
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"> </img>
<img src='assets/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView"></img>
</figure>
Notice that Angular frequently calls `AfterViewChecked()`, often when there are no changes of interest.
Write lean hook methods to avoid performance problems.
{@a aftercontent}
## AfterContent
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls
*after* Angular projects external content into the component.
{@a content-projection}
### Content projection
*Content projection* is a way to import HTML content from outside the component and insert that content
into the component's template in a designated spot.
@ -694,51 +823,68 @@ into the component's template in a designated spot.
~~~ {.l-sub-section}
AngularJS developers know this technique as *transclusion*.
~~~
Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afterview) example.
This time, instead of including the child view within the template, it imports the content from
the `AfterContentComponent`'s parent. Here's the parent's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" title="AfterContentParentComponent (template excerpt)" linenums="false">
</code-example>
Notice that the `<my-child>` tag is tucked between the `<after-content>` tags.
Never put content between a component's element tags *unless you intend to project that content
into the component*.
Now look at the component's template:
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" title="AfterContentComponent (template)" linenums="false">
</code-example>
The `<ng-content>` tag is a *placeholder* for the external content.
It tells Angular where to insert that content.
In this case, the projected content is the `<my-child>` from the parent.
<figure class='image-display'>
<img src='assets/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"> </img>
<img src='assets/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content"></img>
</figure>
~~~ {.l-sub-section}
The telltale signs of *content projection* are twofold:
- HTML between component element tags.
- The presence of `<ng-content>` tags in the component's template.
* HTML between component element tags.
* The presence of `<ng-content>` tags in the component's template.
~~~
{@a aftercontent-hooks}
### AfterContent hooks
*AfterContent* hooks are similar to the *AfterView* hooks.
The key difference is in the child component.
@ -753,13 +899,15 @@ which can only be reached by querying for them via the property decorated with
[@ContentChild](api/core/index/ContentChild-decorator).
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" linenums="false">
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="hooks" title="AfterContentComponent (class excerpts)" linenums="false">
</code-example>
{@a no-unidirectional-flow-worries}
### No unidirectional flow worries with _AfterContent_
This component's `doSomething()` method update's the component's data-bound `comment` property immediately.

Some files were not shown because too many files have changed in this diff Show More