;
+
+ activate() {
+ this.active = !this.active;
+ this.views.forEach(
+ view => view.activate()
+ );
+ }
+
+ // #docregion defined-property
+ get buttonLabel() {
+ return this.active ? 'Deactivate' : 'Activate';
+ }
+ // #enddocregion defined-property
+}
+// #enddocregion view
diff --git a/public/docs/_examples/cb-ts-to-js/ts/src/app/hero-title.component.html b/public/docs/_examples/cb-ts-to-js/ts/src/app/hero-title.component.html
new file mode 100644
index 0000000000..164683cb7c
--- /dev/null
+++ b/public/docs/_examples/cb-ts-to-js/ts/src/app/hero-title.component.html
@@ -0,0 +1,4 @@
+
+{{titlePrefix}} {{title}}
+OK
+{{ msg }}
diff --git a/public/docs/_examples/cb-ts-to-js/ts/src/app/hero-title.component.ts b/public/docs/_examples/cb-ts-to-js/ts/src/app/hero-title.component.ts
new file mode 100644
index 0000000000..48aaae7d44
--- /dev/null
+++ b/public/docs/_examples/cb-ts-to-js/ts/src/app/hero-title.component.ts
@@ -0,0 +1,21 @@
+import { Attribute, Component, Inject, Optional } from '@angular/core';
+
+// #docregion
+// #docregion templateUrl
+@Component({
+ selector: 'hero-title',
+ templateUrl: './hero-title.component.html'
+})
+// #enddocregion templateUrl
+export class HeroTitleComponent {
+ msg: string = '';
+ constructor(
+ @Inject('titlePrefix') @Optional() private titlePrefix: string,
+ @Attribute('title') private title: string
+ ) { }
+
+ ok() {
+ this.msg = 'OK!';
+ }
+}
+// #enddocregion
diff --git a/public/docs/_examples/cb-ts-to-js/ts/src/app/hero.component.ts b/public/docs/_examples/cb-ts-to-js/ts/src/app/hero.component.ts
new file mode 100644
index 0000000000..2976ec605e
--- /dev/null
+++ b/public/docs/_examples/cb-ts-to-js/ts/src/app/hero.component.ts
@@ -0,0 +1,15 @@
+// #docregion
+// #docregion metadata
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'hero-view',
+ template: '{{title}}: {{getName()}} '
+})
+// #docregion appexport, class
+export class HeroComponent {
+ title = 'Hero Detail';
+ getName() {return 'Windstorm'; }
+}
+// #enddocregion appexport, class
+// #enddocregion metadata
diff --git a/public/docs/_examples/cb-ts-to-js/ts/src/index.html b/public/docs/_examples/cb-ts-to-js/ts/src/index.html
new file mode 100644
index 0000000000..d9ad1f7aef
--- /dev/null
+++ b/public/docs/_examples/cb-ts-to-js/ts/src/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+ TypeScript to JavaScript
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/cb-ts-to-js/ts/src/main.ts b/public/docs/_examples/cb-ts-to-js/ts/src/main.ts
new file mode 100644
index 0000000000..f22933ba8e
--- /dev/null
+++ b/public/docs/_examples/cb-ts-to-js/ts/src/main.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/cb-visual-studio-2015/ts/.gitignore b/public/docs/_examples/cb-visual-studio-2015/ts/.gitignore
new file mode 100644
index 0000000000..ca3f2dbd5e
--- /dev/null
+++ b/public/docs/_examples/cb-visual-studio-2015/ts/.gitignore
@@ -0,0 +1 @@
+!tsconfig.json
diff --git a/public/docs/_examples/cb-visual-studio-2015/ts/tsconfig.json b/public/docs/_examples/cb-visual-studio-2015/ts/tsconfig.json
new file mode 100644
index 0000000000..2624488890
--- /dev/null
+++ b/public/docs/_examples/cb-visual-studio-2015/ts/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "removeComments": false,
+ "noImplicitAny": true,
+ "suppressImplicitAnyIndexErrors": true,
+ "typeRoots": [
+ "node_modules/@types"
+ ]
+ },
+ "compileOnSave": true
+}
diff --git a/public/docs/_examples/cli-quickstart/e2e-spec.ts b/public/docs/_examples/cli-quickstart/e2e-spec.ts
new file mode 100644
index 0000000000..59816e13c2
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/e2e-spec.ts
@@ -0,0 +1,14 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('cli-quickstart App', () => {
+ beforeEach(() => {
+ return browser.get('/');
+ });
+
+ it('should display message saying app works', () => {
+ let pageTitle = element(by.css('app-root h1')).getText();
+ expect(pageTitle).toEqual('My First Angular App');
+ });
+});
diff --git a/public/docs/_examples/cli-quickstart/ts/.angular-cli.json b/public/docs/_examples/cli-quickstart/ts/.angular-cli.json
new file mode 100644
index 0000000000..49a6b93bd0
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/.angular-cli.json
@@ -0,0 +1,57 @@
+{
+ "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
+ "project": {
+ "name": "master-project"
+ },
+ "apps": [
+ {
+ "root": "src",
+ "outDir": "dist",
+ "assets": [
+ "assets",
+ "favicon.ico"
+ ],
+ "index": "index.html",
+ "main": "main.ts",
+ "polyfills": "polyfills.ts",
+ "test": "test.ts",
+ "tsconfig": "tsconfig.app.json",
+ "testTsconfig": "tsconfig.spec.json",
+ "prefix": "app",
+ "styles": [
+ "styles.css"
+ ],
+ "scripts": [],
+ "environmentSource": "environments/environment.ts",
+ "environments": {
+ "dev": "environments/environment.ts",
+ "prod": "environments/environment.prod.ts"
+ }
+ }
+ ],
+ "e2e": {
+ "protractor": {
+ "config": "./protractor.conf.js"
+ }
+ },
+ "lint": [
+ {
+ "project": "src/tsconfig.app.json"
+ },
+ {
+ "project": "src/tsconfig.spec.json"
+ },
+ {
+ "project": "e2e/tsconfig.e2e.json"
+ }
+ ],
+ "test": {
+ "karma": {
+ "config": "./karma.conf.js"
+ }
+ },
+ "defaults": {
+ "styleExt": "css",
+ "component": {}
+ }
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/.editorconfig b/public/docs/_examples/cli-quickstart/ts/.editorconfig
new file mode 100644
index 0000000000..6e87a003da
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/.editorconfig
@@ -0,0 +1,13 @@
+# Editor configuration, see http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
diff --git a/public/docs/_examples/cli-quickstart/ts/.gitignore b/public/docs/_examples/cli-quickstart/ts/.gitignore
new file mode 100644
index 0000000000..04a48dc643
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/.gitignore
@@ -0,0 +1,45 @@
+# See http://help.github.com/ignore-files/ for more about ignoring files.
+
+# compiled output
+/dist
+/tmp
+
+# dependencies
+/node_modules
+
+# IDEs and editors
+/.idea
+.project
+.classpath
+.c9/
+*.launch
+.settings/
+*.sublime-workspace
+
+# IDE - VSCode
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+# misc
+/.sass-cache
+/connect.lock
+/coverage/*
+/libpeerconnection.log
+npm-debug.log
+testem.log
+/typings
+
+# e2e
+/e2e/*.js
+/e2e/*.map
+
+#System Files
+.DS_Store
+Thumbs.db
+
+!src/styles.css
+!karma.conf.js
+!protractor.conf.js
diff --git a/public/docs/_examples/cli-quickstart/ts/README.md b/public/docs/_examples/cli-quickstart/ts/README.md
new file mode 100644
index 0000000000..efe9ec67ae
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/README.md
@@ -0,0 +1,27 @@
+# MasterProject
+
+This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.0.0-rc.0.
+
+## Development server
+Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
+
+## Code scaffolding
+
+Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive/pipe/service/class/module`.
+
+## Build
+
+Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `-prod` flag for a production build.
+
+## Running unit tests
+
+Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
+
+## Running end-to-end tests
+
+Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
+Before running the tests make sure you are serving the app via `ng serve`.
+
+## Further help
+
+To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md).
diff --git a/public/docs/_examples/cli-quickstart/ts/bs-config.cli.json b/public/docs/_examples/cli-quickstart/ts/bs-config.cli.json
new file mode 100644
index 0000000000..ac61d35f83
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/bs-config.cli.json
@@ -0,0 +1,11 @@
+{
+ "open": false,
+ "logLevel": "silent",
+ "port": 8080,
+ "server": {
+ "baseDir": "dist",
+ "middleware": {
+ "0": null
+ }
+ }
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/e2e/app.e2e-spec.ts b/public/docs/_examples/cli-quickstart/ts/e2e/app.e2e-spec.ts
new file mode 100644
index 0000000000..4346d120c8
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/e2e/app.e2e-spec.ts
@@ -0,0 +1,14 @@
+import { MyAppPage } from './app.po';
+
+describe('my-app App', function() {
+ let page: MyAppPage;
+
+ beforeEach(() => {
+ page = new MyAppPage();
+ });
+
+ it('should display message saying app works', () => {
+ page.navigateTo();
+ expect(page.getParagraphText()).toEqual('app works!');
+ });
+});
diff --git a/public/docs/_examples/cli-quickstart/ts/e2e/app.po.ts b/public/docs/_examples/cli-quickstart/ts/e2e/app.po.ts
new file mode 100644
index 0000000000..5d82157326
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/e2e/app.po.ts
@@ -0,0 +1,11 @@
+import { browser, element, by } from 'protractor';
+
+export class MyAppPage {
+ navigateTo() {
+ return browser.get('/');
+ }
+
+ getParagraphText() {
+ return element(by.css('app-root h1')).getText();
+ }
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/e2e/tsconfig.e2e.json b/public/docs/_examples/cli-quickstart/ts/e2e/tsconfig.e2e.json
new file mode 100644
index 0000000000..74c2bca131
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/e2e/tsconfig.e2e.json
@@ -0,0 +1,19 @@
+{
+ "compilerOptions": {
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [
+ "es2016"
+ ],
+ "outDir": "../dist/out-tsc-e2e",
+ "module": "commonjs",
+ "target": "es6",
+ "types":[
+ "jasmine",
+ "node"
+ ]
+ }
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/example-config.json b/public/docs/_examples/cli-quickstart/ts/example-config.json
new file mode 100644
index 0000000000..313764c3c6
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/example-config.json
@@ -0,0 +1,4 @@
+{
+ "build": "build:cli",
+ "run": "serve:cli"
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/karma.conf.js b/public/docs/_examples/cli-quickstart/ts/karma.conf.js
new file mode 100644
index 0000000000..84b4cd5aca
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/karma.conf.js
@@ -0,0 +1,44 @@
+// Karma configuration file, see link for more information
+// https://karma-runner.github.io/0.13/config/configuration-file.html
+
+module.exports = function (config) {
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine', '@angular/cli'],
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter'),
+ require('karma-coverage-istanbul-reporter'),
+ require('@angular/cli/plugins/karma')
+ ],
+ client:{
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+ files: [
+ { pattern: './src/test.ts', watched: false }
+ ],
+ preprocessors: {
+ './src/test.ts': ['@angular/cli']
+ },
+ mime: {
+ 'text/x-typescript': ['ts','tsx']
+ },
+ coverageIstanbulReporter: {
+ reports: [ 'html', 'lcovonly' ],
+ fixWebpackSourcePaths: true
+ },
+ angularCli: {
+ environment: 'dev'
+ },
+ reporters: config.angularCli && config.angularCli.codeCoverage
+ ? ['progress', 'coverage-istanbul']
+ : ['progress', 'kjhtml'],
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false
+ });
+};
diff --git a/public/docs/_examples/cli-quickstart/ts/protractor.conf.js b/public/docs/_examples/cli-quickstart/ts/protractor.conf.js
new file mode 100644
index 0000000000..2cbc329391
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/protractor.conf.js
@@ -0,0 +1,31 @@
+// Protractor configuration file, see link for more information
+// https://github.com/angular/protractor/blob/master/lib/config.ts
+
+/*global jasmine */
+const { SpecReporter } = require('jasmine-spec-reporter');
+
+exports.config = {
+ allScriptsTimeout: 11000,
+ specs: [
+ './e2e/**/*.e2e-spec.ts'
+ ],
+ capabilities: {
+ 'browserName': 'chrome'
+ },
+ directConnect: true,
+ baseUrl: 'http://localhost:4200/',
+ framework: 'jasmine',
+ jasmineNodeOpts: {
+ showColors: true,
+ defaultTimeoutInterval: 30000,
+ print: function() {}
+ },
+ beforeLaunch: function() {
+ require('ts-node').register({
+ project: 'e2e/tsconfig.e2e.json'
+ });
+ },
+ onPrepare() {
+ jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
+ }
+};
diff --git a/public/docs/_examples/cli-quickstart/ts/src/app/app.component.css b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.css
new file mode 100644
index 0000000000..a2b21fae82
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.css
@@ -0,0 +1,6 @@
+/* #docregion */
+h1 {
+ color: #369;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 250%;
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/src/app/app.component.html b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.html
new file mode 100644
index 0000000000..b6931b538a
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.html
@@ -0,0 +1,3 @@
+
+ {{title}}
+
diff --git a/public/docs/_examples/cli-quickstart/ts/src/app/app.component.spec.ts b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.spec.ts
new file mode 100644
index 0000000000..c740bcd745
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.spec.ts
@@ -0,0 +1,32 @@
+import { TestBed, async } from '@angular/core/testing';
+
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ AppComponent
+ ],
+ }).compileComponents();
+ }));
+
+ it('should create the app', async(() => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.debugElement.componentInstance;
+ expect(app).toBeTruthy();
+ }));
+
+ it(`should have as title 'app works!'`, async(() => {
+ const fixture = TestBed.createComponent(AppComponent);
+ const app = fixture.debugElement.componentInstance;
+ expect(app.title).toEqual('app works!');
+ }));
+
+ it('should render title in a h1 tag', async(() => {
+ const fixture = TestBed.createComponent(AppComponent);
+ fixture.detectChanges();
+ const compiled = fixture.debugElement.nativeElement;
+ expect(compiled.querySelector('h1').textContent).toContain('app works!');
+ }));
+});
diff --git a/public/docs/_examples/cli-quickstart/ts/src/app/app.component.ts b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..4c1b4a9bf7
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/app/app.component.ts
@@ -0,0 +1,16 @@
+// #docregion import
+import { Component } from '@angular/core';
+// #enddocregion import
+
+// #docregion metadata
+@Component({
+ selector: 'app-root',
+ templateUrl: './app.component.html',
+ styleUrls: ['./app.component.css']
+})
+// #enddocregion metadata
+// #docregion title, class
+export class AppComponent {
+ title = 'My First Angular App';
+}
+// #enddocregion title, class
diff --git a/public/docs/_examples/cli-quickstart/ts/src/app/app.module.ts b/public/docs/_examples/cli-quickstart/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..67ae49119b
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/app/app.module.ts
@@ -0,0 +1,20 @@
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ declarations: [
+ AppComponent
+ ],
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule
+ ],
+ providers: [],
+ bootstrap: [AppComponent]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/cli-quickstart/ts/src/assets/.gitkeep b/public/docs/_examples/cli-quickstart/ts/src/assets/.gitkeep
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/cli-quickstart/ts/src/environments/environment.prod.ts b/public/docs/_examples/cli-quickstart/ts/src/environments/environment.prod.ts
new file mode 100644
index 0000000000..3612073bc3
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/environments/environment.prod.ts
@@ -0,0 +1,3 @@
+export const environment = {
+ production: true
+};
diff --git a/public/docs/_examples/cli-quickstart/ts/src/environments/environment.ts b/public/docs/_examples/cli-quickstart/ts/src/environments/environment.ts
new file mode 100644
index 0000000000..b7f639aeca
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/environments/environment.ts
@@ -0,0 +1,8 @@
+// The file contents for the current environment will overwrite these during build.
+// The build system defaults to the dev environment which uses `environment.ts`, but if you do
+// `ng build --env=prod` then `environment.prod.ts` will be used instead.
+// The list of which env maps to which file can be found in `.angular-cli.json`.
+
+export const environment = {
+ production: false
+};
diff --git a/public/docs/_examples/cli-quickstart/ts/src/favicon.ico b/public/docs/_examples/cli-quickstart/ts/src/favicon.ico
new file mode 100644
index 0000000000..8081c7ceaf
Binary files /dev/null and b/public/docs/_examples/cli-quickstart/ts/src/favicon.ico differ
diff --git a/public/docs/_examples/cli-quickstart/ts/src/index.html b/public/docs/_examples/cli-quickstart/ts/src/index.html
new file mode 100644
index 0000000000..8a1a7b32b3
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+ MyApp
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/cli-quickstart/ts/src/main.ts b/public/docs/_examples/cli-quickstart/ts/src/main.ts
new file mode 100644
index 0000000000..a9ca1caf8c
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/main.ts
@@ -0,0 +1,11 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+ enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/cli-quickstart/ts/src/polyfills.ts b/public/docs/_examples/cli-quickstart/ts/src/polyfills.ts
new file mode 100644
index 0000000000..53bdaf1b86
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/polyfills.ts
@@ -0,0 +1,68 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ * 2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ * file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera),
+ * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/** IE9, IE10 and IE11 requires all of the following polyfills. **/
+// import 'core-js/es6/symbol';
+// import 'core-js/es6/object';
+// import 'core-js/es6/function';
+// import 'core-js/es6/parse-int';
+// import 'core-js/es6/parse-float';
+// import 'core-js/es6/number';
+// import 'core-js/es6/math';
+// import 'core-js/es6/string';
+// import 'core-js/es6/date';
+// import 'core-js/es6/array';
+// import 'core-js/es6/regexp';
+// import 'core-js/es6/map';
+// import 'core-js/es6/set';
+
+/** IE10 and IE11 requires the following for NgClass support on SVG elements */
+// import 'classlist.js'; // Run `npm install --save classlist.js`.
+
+/** IE10 and IE11 requires the following to support `@angular/animation`. */
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+/** Evergreen browsers require these. **/
+import 'core-js/es6/reflect';
+import 'core-js/es7/reflect';
+
+
+/** ALL Firefox browsers require the following to support `@angular/animation`. **/
+// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
+
+
+
+/***************************************************************************************************
+ * Zone JS is required by Angular itself.
+ */
+import 'zone.js/dist/zone'; // Included with Angular CLI.
+
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */
+
+/**
+ * Date, currency, decimal and percent pipes.
+ * Needed for: All but Chrome, Firefox, Edge, IE11 and Safari 10
+ */
+// import 'intl'; // Run `npm install --save intl`.
diff --git a/public/docs/_examples/cli-quickstart/ts/src/styles.css b/public/docs/_examples/cli-quickstart/ts/src/styles.css
new file mode 100644
index 0000000000..d81835d0cd
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/styles.css
@@ -0,0 +1,116 @@
+/* #docregion , quickstart, toh */
+/* Master Styles */
+h1 {
+ color: #369;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 250%;
+}
+h2, h3 {
+ color: #444;
+ font-family: Arial, Helvetica, sans-serif;
+ font-weight: lighter;
+}
+body {
+ margin: 2em;
+}
+/* #enddocregion quickstart */
+body, input[text], button {
+ color: #888;
+ font-family: Cambria, Georgia;
+}
+/* #enddocregion toh */
+a {
+ cursor: pointer;
+ cursor: hand;
+}
+button {
+ 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: #aaa;
+ cursor: auto;
+}
+
+/* Navigation link styles */
+nav a {
+ padding: 5px 10px;
+ text-decoration: none;
+ margin-right: 10px;
+ 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;
+}
+
+/* items class */
+.items {
+ margin: 0 0 2em 0;
+ list-style-type: none;
+ padding: 0;
+ width: 24em;
+}
+.items li {
+ cursor: pointer;
+ position: relative;
+ left: 0;
+ background-color: #EEE;
+ margin: .5em;
+ padding: .3em 0;
+ height: 1.6em;
+ border-radius: 4px;
+}
+.items li:hover {
+ color: #607D8B;
+ background-color: #DDD;
+ left: .1em;
+}
+.items li.selected {
+ background-color: #CFD8DC;
+ color: white;
+}
+.items li.selected:hover {
+ background-color: #BBD8DC;
+}
+.items .text {
+ position: relative;
+ top: -3px;
+}
+.items .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;
+}
+/* #docregion toh */
+/* everywhere else */
+* {
+ font-family: Arial, Helvetica, sans-serif;
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/src/test.ts b/public/docs/_examples/cli-quickstart/ts/src/test.ts
new file mode 100644
index 0000000000..9bf72267e9
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/test.ts
@@ -0,0 +1,32 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/dist/long-stack-trace-zone';
+import 'zone.js/dist/proxy.js';
+import 'zone.js/dist/sync-test';
+import 'zone.js/dist/jasmine-patch';
+import 'zone.js/dist/async-test';
+import 'zone.js/dist/fake-async-test';
+import { getTestBed } from '@angular/core/testing';
+import {
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
+declare var __karma__: any;
+declare var require: any;
+
+// Prevent Karma from running prematurely.
+__karma__.loaded = function () {};
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+ BrowserDynamicTestingModule,
+ platformBrowserDynamicTesting()
+);
+// Then we find all the tests.
+const context = require.context('./', true, /\.spec\.ts$/);
+// And load the modules.
+context.keys().map(context);
+// Finally, start Karma to run the tests.
+__karma__.start();
diff --git a/public/docs/_examples/cli-quickstart/ts/src/tsconfig.app.json b/public/docs/_examples/cli-quickstart/ts/src/tsconfig.app.json
new file mode 100644
index 0000000000..9f12c4b849
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/tsconfig.app.json
@@ -0,0 +1,22 @@
+{
+ "compilerOptions": {
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [
+ "es2016",
+ "dom"
+ ],
+ "outDir": "../out-tsc/app",
+ "target": "es5",
+ "module": "es2015",
+ "baseUrl": "",
+ "types": []
+ },
+ "exclude": [
+ "test.ts",
+ "**/*.spec.ts"
+ ]
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/src/tsconfig.spec.json b/public/docs/_examples/cli-quickstart/ts/src/tsconfig.spec.json
new file mode 100644
index 0000000000..6c5160e12e
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/src/tsconfig.spec.json
@@ -0,0 +1,26 @@
+{
+ "compilerOptions": {
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [
+ "es2016"
+ ],
+ "outDir": "../out-tsc/spec",
+ "module": "commonjs",
+ "target": "es6",
+ "baseUrl": "",
+ "types": [
+ "jasmine",
+ "node"
+ ]
+ },
+ "files": [
+ "test.ts"
+ ],
+ "include": [
+ "**/*.spec.ts"
+ ]
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/tsconfig.json b/public/docs/_examples/cli-quickstart/ts/tsconfig.json
new file mode 100644
index 0000000000..cde5e35054
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/tsconfig.json
@@ -0,0 +1,14 @@
+{
+ "compileOnSave": false,
+ "compilerOptions": {
+ "outDir": "./dist/out-tsc",
+ "sourceMap": true,
+ "declaration": false,
+ "moduleResolution": "node",
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [
+ "es2016"
+ ]
+ }
+}
diff --git a/public/docs/_examples/cli-quickstart/ts/zipper.json b/public/docs/_examples/cli-quickstart/ts/zipper.json
new file mode 100644
index 0000000000..70bf41659e
--- /dev/null
+++ b/public/docs/_examples/cli-quickstart/ts/zipper.json
@@ -0,0 +1,11 @@
+{
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[0-9].*",
+ ".angular-cli.json",
+ "protractor.conf.js"
+ ],
+ "removeSystemJsConfig": true,
+ "type": "cli"
+}
diff --git a/public/docs/_examples/component-styles/e2e-spec.ts b/public/docs/_examples/component-styles/e2e-spec.ts
new file mode 100644
index 0000000000..32bd30832c
--- /dev/null
+++ b/public/docs/_examples/component-styles/e2e-spec.ts
@@ -0,0 +1,68 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Component Style Tests', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('scopes component styles to component view', function() {
+ let componentH1 = element(by.css('hero-app > h1'));
+ let externalH1 = element(by.css('body > h1'));
+
+ expect(componentH1.getCssValue('fontWeight')).toEqual('normal');
+ expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal');
+ });
+
+
+ it('allows styling :host element', function() {
+ let host = element(by.css('hero-details'));
+
+ expect(host.getCssValue('borderWidth')).toEqual('1px');
+ });
+
+ it('supports :host() in function form', function() {
+ let host = element(by.css('hero-details'));
+
+ host.element(by.buttonText('Activate')).click();
+ expect(host.getCssValue('borderWidth')).toEqual('3px');
+ });
+
+ it('allows conditional :host-context() styling', function() {
+ let h2 = element(by.css('hero-details h2'));
+
+ expect(h2.getCssValue('backgroundColor')).toEqual('rgba(238, 238, 255, 1)'); // #eeeeff
+ });
+
+ it('styles both view and content children with /deep/', function() {
+ let viewH3 = element(by.css('hero-team h3'));
+ let contentH3 = element(by.css('hero-controls h3'));
+
+ expect(viewH3.getCssValue('fontStyle')).toEqual('italic');
+ expect(contentH3.getCssValue('fontStyle')).toEqual('italic');
+ });
+
+ it('includes styles loaded with CSS @import', function() {
+ let host = element(by.css('hero-details'));
+
+ expect(host.getCssValue('padding')).toEqual('10px');
+ });
+
+ it('processes template inline styles', function() {
+ let button = element(by.css('hero-controls button'));
+ let externalButton = element(by.css('body > button'));
+ expect(button.getCssValue('backgroundColor')).toEqual('rgba(255, 255, 255, 1)'); // #ffffff
+ expect(externalButton.getCssValue('backgroundColor')).not.toEqual('rgba(255, 255, 255, 1)');
+ });
+
+ it('processes template s', function() {
+ let li = element(by.css('hero-team li:first-child'));
+ let externalLi = element(by.css('body > ul li'));
+
+ expect(li.getCssValue('listStyleType')).toEqual('square');
+ expect(externalLi.getCssValue('listStyleType')).not.toEqual('square');
+ });
+
+});
diff --git a/public/docs/_examples/component-styles/ts/example-config.json b/public/docs/_examples/component-styles/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/component-styles/ts/plnkr.json b/public/docs/_examples/component-styles/ts/plnkr.json
new file mode 100644
index 0000000000..e045ebcb38
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Component Styles",
+ "basePath": "src/",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.native.*"
+ ],
+ "tags": ["CSS"]
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/app.module.ts b/public/docs/_examples/component-styles/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..31c72cbbf2
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/app.module.ts
@@ -0,0 +1,23 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { HeroAppComponent } from './hero-app.component';
+import { HeroAppMainComponent } from './hero-app-main.component';
+import { HeroDetailsComponent } from './hero-details.component';
+import { HeroControlsComponent } from './hero-controls.component';
+import { QuestSummaryComponent } from './quest-summary.component';
+import { HeroTeamComponent } from './hero-team.component';
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [
+ HeroAppComponent,
+ HeroAppMainComponent,
+ HeroDetailsComponent,
+ HeroControlsComponent,
+ QuestSummaryComponent,
+ HeroTeamComponent
+ ],
+ bootstrap: [ HeroAppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-app-main.component.ts b/public/docs/_examples/component-styles/ts/src/app/hero-app-main.component.ts
new file mode 100644
index 0000000000..aebb1f729e
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-app-main.component.ts
@@ -0,0 +1,16 @@
+import { Component, Input } from '@angular/core';
+
+import { Hero } from './hero';
+
+@Component({
+ selector: 'hero-app-main',
+ template: `
+
+
+
+
+ `
+})
+export class HeroAppMainComponent {
+ @Input() hero: Hero;
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-app.component.ts b/public/docs/_examples/component-styles/ts/src/app/hero-app.component.ts
new file mode 100644
index 0000000000..5f1923e6f3
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-app.component.ts
@@ -0,0 +1,24 @@
+import { Component, HostBinding } from '@angular/core';
+import { Hero } from './hero';
+
+// #docregion
+@Component({
+ selector: 'hero-app',
+ template: `
+ Tour of Heroes
+ `,
+ styles: ['h1 { font-weight: normal; }']
+})
+export class HeroAppComponent {
+// #enddocregion
+ hero = new Hero(
+ 'Human Torch',
+ ['Mister Fantastic', 'Invisible Woman', 'Thing']
+ );
+
+ @HostBinding('class') get themeClass() {
+ return 'theme-light';
+ }
+// #docregion
+}
+// #enddocregion
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-controls.component.ts b/public/docs/_examples/component-styles/ts/src/app/hero-controls.component.ts
new file mode 100644
index 0000000000..5d293596d2
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-controls.component.ts
@@ -0,0 +1,25 @@
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+// #docregion inlinestyles
+@Component({
+ selector: 'hero-controls',
+ template: `
+
+ Controls
+ Activate
+ `
+})
+// #enddocregion inlinestyles
+export class HeroControlsComponent {
+ @Input() hero: Hero;
+
+ activate() {
+ this.hero.active = true;
+ }
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-details-box.css b/public/docs/_examples/component-styles/ts/src/app/hero-details-box.css
new file mode 100644
index 0000000000..f240073005
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-details-box.css
@@ -0,0 +1,3 @@
+:host {
+ padding: 10px;
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-details.component.css b/public/docs/_examples/component-styles/ts/src/app/hero-details.component.css
new file mode 100644
index 0000000000..fd938ca55c
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-details.component.css
@@ -0,0 +1,28 @@
+/* #docregion import */
+@import 'hero-details-box.css';
+/* #enddocregion import */
+
+/* #docregion host */
+:host {
+ display: block;
+ border: 1px solid black;
+}
+/* #enddocregion host */
+
+/* #docregion hostfunction */
+:host(.active) {
+ border-width: 3px;
+}
+/* #enddocregion hostfunction */
+
+/* #docregion hostcontext */
+:host-context(.theme-light) h2 {
+ background-color: #eef;
+}
+/* #enddocregion hostcontext */
+
+/* #docregion deep */
+:host /deep/ h3 {
+ font-style: italic;
+}
+/* #enddocregion deep */
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-details.component.ts b/public/docs/_examples/component-styles/ts/src/app/hero-details.component.ts
new file mode 100644
index 0000000000..bd86a63e04
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-details.component.ts
@@ -0,0 +1,18 @@
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+// #docregion styleurls
+@Component({
+ selector: 'hero-details',
+ template: `
+ {{hero.name}}
+
+
+ `,
+ styleUrls: ['app/hero-details.component.css']
+})
+export class HeroDetailsComponent {
+ // #enddocregion styleurls
+ @Input() hero: Hero;
+ // #docregion styleurls
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-team.component.css b/public/docs/_examples/component-styles/ts/src/app/hero-team.component.css
new file mode 100644
index 0000000000..b87679886b
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-team.component.css
@@ -0,0 +1,3 @@
+li {
+ list-style-type: square;
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero-team.component.ts b/public/docs/_examples/component-styles/ts/src/app/hero-team.component.ts
new file mode 100644
index 0000000000..4f092d2827
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero-team.component.ts
@@ -0,0 +1,19 @@
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+// #docregion stylelink
+@Component({
+ selector: 'hero-team',
+ template: `
+
+ Team
+ `
+})
+// #enddocregion stylelink
+export class HeroTeamComponent {
+ @Input() hero: Hero;
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/hero.ts b/public/docs/_examples/component-styles/ts/src/app/hero.ts
new file mode 100644
index 0000000000..7f8969e682
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/hero.ts
@@ -0,0 +1,7 @@
+export class Hero {
+ active: boolean;
+
+ constructor(public name: string,
+ public team: string[]) {
+ }
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.css b/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.css
new file mode 100644
index 0000000000..207fa981dd
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.css
@@ -0,0 +1,5 @@
+:host {
+ display: block;
+ background-color: green;
+ color: white;
+}
diff --git a/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.html b/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.html
new file mode 100644
index 0000000000..ace27d2a1c
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.html
@@ -0,0 +1 @@
+No quests in progress
diff --git a/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.ts b/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.ts
new file mode 100644
index 0000000000..2a2dc65fe0
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/app/quest-summary.component.ts
@@ -0,0 +1,20 @@
+/* tslint:disable:no-unused-variable */
+// #docplaster
+import { Component, ViewEncapsulation } from '@angular/core';
+
+// #docregion
+@Component({
+ selector: 'quest-summary',
+ // #docregion urls
+ templateUrl: './quest-summary.component.html',
+ styleUrls: ['./quest-summary.component.css']
+ // #enddocregion urls
+})
+export class QuestSummaryComponent { }
+// #enddocregion
+/*
+ // #docregion encapsulation.native
+ // warning: few browsers support shadow DOM encapsulation at this time
+ encapsulation: ViewEncapsulation.Native
+ // #enddocregion encapsulation.native
+*/
diff --git a/public/docs/_examples/component-styles/ts/src/index.html b/public/docs/_examples/component-styles/ts/src/index.html
new file mode 100644
index 0000000000..6e9dc51b32
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+ Component Styles
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ External H1 Title for E2E test
+
+ External button for E2E test
+
+ External list for E2E test
+
+
+
+
diff --git a/public/docs/_examples/component-styles/ts/src/main.ts b/public/docs/_examples/component-styles/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/component-styles/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/dependency-injection/e2e-spec.ts b/public/docs/_examples/dependency-injection/e2e-spec.ts
new file mode 100644
index 0000000000..5b46dd77d9
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/e2e-spec.ts
@@ -0,0 +1,199 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder } from 'protractor';
+
+describe('Dependency Injection Tests', function () {
+
+ let expectedMsg: string;
+ let expectedMsgRx: RegExp;
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ describe('Cars:', function() {
+
+ it('DI car displays as expected', function () {
+ expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
+ expect(element(by.css('#di')).getText()).toEqual(expectedMsg);
+ });
+
+ it('No DI car displays as expected', function () {
+ expectedMsg = 'No DI car with 4 cylinders and Flintstone tires.';
+ expect(element(by.css('#nodi')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Injector car displays as expected', function () {
+ expectedMsg = 'Injector car with 4 cylinders and Flintstone tires.';
+ expect(element(by.css('#injector')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Factory car displays as expected', function () {
+ expectedMsg = 'Factory car with 4 cylinders and Flintstone tires.';
+ expect(element(by.css('#factory')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Simple car displays as expected', function () {
+ expectedMsg = 'Simple car with 4 cylinders and Flintstone tires.';
+ expect(element(by.css('#simple')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Super car displays as expected', function () {
+ expectedMsg = 'Super car with 12 cylinders and Flintstone tires.';
+ expect(element(by.css('#super')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Test car displays as expected', function () {
+ expectedMsg = 'Test car with 8 cylinders and YokoGoodStone tires.';
+ expect(element(by.css('#test')).getText()).toEqual(expectedMsg);
+ });
+ });
+
+ describe('Other Injections:', function() {
+ it('DI car displays as expected', function () {
+ expectedMsg = 'DI car with 4 cylinders and Flintstone tires.';
+ expect(element(by.css('#car')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Hero displays as expected', function () {
+ expectedMsg = 'Mr. Nice';
+ expect(element(by.css('#hero')).getText()).toEqual(expectedMsg);
+ });
+
+ it('Optional injection displays as expected', function () {
+ expectedMsg = 'R.O.U.S.\'s? I don\'t think they exist!';
+ expect(element(by.css('#rodent')).getText()).toEqual(expectedMsg);
+ });
+ });
+
+ describe('Tests:', function() {
+
+ it('Tests display as expected', function () {
+ expectedMsgRx = /Tests passed/;
+ expect(element(by.css('#tests')).getText()).toMatch(expectedMsgRx);
+ });
+
+ });
+
+ describe('Provider variations:', function() {
+
+ it('P1 (class) displays as expected', function () {
+ expectedMsg = 'Hello from logger provided with Logger class';
+ expect(element(by.css('#p1')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P3 (provide) displays as expected', function () {
+ expectedMsg = 'Hello from logger provided with useClass:Logger';
+ expect(element(by.css('#p3')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P4 (useClass:BetterLogger) displays as expected', function () {
+ expectedMsg = 'Hello from logger provided with useClass:BetterLogger';
+ expect(element(by.css('#p4')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P5 (useClass:EvenBetterLogger - dependency) displays as expected', function () {
+ expectedMsg = 'Message to Bob: Hello from EvenBetterlogger';
+ expect(element(by.css('#p5')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P6a (no alias) displays as expected', function () {
+ expectedMsg = 'Hello OldLogger (but we want NewLogger)';
+ expect(element(by.css('#p6a')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P6b (alias) displays as expected', function () {
+ expectedMsg = 'Hello from NewLogger (via aliased OldLogger)';
+ expect(element(by.css('#p6b')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P7 (useValue) displays as expected', function () {
+ expectedMsg = 'Silent logger says "Shhhhh!". Provided via "useValue"';
+ expect(element(by.css('#p7')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P8 (useFactory) displays as expected', function () {
+ expectedMsg = 'Hero service injected successfully via heroServiceProvider';
+ expect(element(by.css('#p8')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P9 (OpaqueToken) displays as expected', function () {
+ expectedMsg = 'APP_CONFIG Application title is Dependency Injection';
+ expect(element(by.css('#p9')).getText()).toEqual(expectedMsg);
+ });
+
+ it('P10 (optional dependency) displays as expected', function () {
+ expectedMsg = 'Optional logger was not available';
+ expect(element(by.css('#p10')).getText()).toEqual(expectedMsg);
+ });
+ });
+
+ describe('User/Heroes:', function() {
+ it('User is Bob - unauthorized', function () {
+ expectedMsgRx = /Bob, is not authorized/;
+ expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
+ });
+
+ it('should have button', function () {
+ expect(element.all(by.cssContainingText('button', 'Next User'))
+ .get(0).isDisplayed()).toBe(true, '\'Next User\' button should be displayed');
+ });
+
+ it('unauthorized user should have multiple unauthorized heroes', function () {
+ let heroes = element.all(by.css('#unauthorized hero-list div'));
+ expect(heroes.count()).toBeGreaterThan(0);
+ });
+
+ it('unauthorized user should have no secret heroes', function () {
+ let heroes = element.all(by.css('#unauthorized hero-list div'));
+ expect(heroes.count()).toBeGreaterThan(0);
+
+ let filteredHeroes = heroes.filter((elem: ElementFinder, index: number) => {
+ return elem.getText().then((text: string) => {
+ return /secret/.test(text);
+ });
+ });
+
+ expect(filteredHeroes.count()).toEqual(0);
+ });
+
+ it('unauthorized user should have no authorized heroes listed', function () {
+ expect(element.all(by.css('#authorized hero-list div')).count()).toEqual(0);
+ });
+
+ describe('after button click', function() {
+
+ beforeAll(function (done: any) {
+ let buttonEle = element.all(by.cssContainingText('button', 'Next User')).get(0);
+ buttonEle.click().then(done, done);
+ });
+
+ it('User is Alice - authorized', function () {
+ expectedMsgRx = /Alice, is authorized/;
+ expect(element(by.css('#user')).getText()).toMatch(expectedMsgRx);
+ });
+
+ it('authorized user should have multiple authorized heroes ', function () {
+ let heroes = element.all(by.css('#authorized hero-list div'));
+ expect(heroes.count()).toBeGreaterThan(0);
+ });
+
+ it('authorized user should have secret heroes', function () {
+ let heroes = element.all(by.css('#authorized hero-list div'));
+ expect(heroes.count()).toBeGreaterThan(0);
+
+ let filteredHeroes = heroes.filter(function(elem: ElementFinder, index: number){
+ return elem.getText().then(function(text: string) {
+ return /secret/.test(text);
+ });
+ });
+
+ expect(filteredHeroes.count()).toBeGreaterThan(0);
+ });
+
+ it('authorized user should have no unauthorized heroes listed', function () {
+ expect(element.all(by.css('#unauthorized hero-list div')).count()).toEqual(0);
+ });
+ });
+ });
+});
diff --git a/public/docs/_examples/dependency-injection/ts/example-config.json b/public/docs/_examples/dependency-injection/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/dependency-injection/ts/plnkr.json b/public/docs/_examples/dependency-injection/ts/plnkr.json
new file mode 100644
index 0000000000..e8d1ab24b2
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Dependency Injection",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1,2].*"
+ ],
+ "tags": ["dependency", "di"]
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.component.1.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..b398ebeb57
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/app.component.1.ts
@@ -0,0 +1,17 @@
+// Early versions
+
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+
+
+ `
+})
+
+export class AppComponent {
+ title = 'Dependency Injection';
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.component.2.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.component.2.ts
new file mode 100644
index 0000000000..d24df5568c
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/app.component.2.ts
@@ -0,0 +1,25 @@
+// #docregion
+// #docregion imports
+import { Component } from '@angular/core';
+import { Inject } from '@angular/core';
+
+import { APP_CONFIG, AppConfig } from './app.config';
+// #enddocregion imports
+
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+
+
+ `
+})
+export class AppComponent {
+ title: string;
+
+ // #docregion ctor
+ constructor(@Inject(APP_CONFIG) config: AppConfig) {
+ this.title = config.title;
+ }
+ // #enddocregion ctor
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..3563fdc070
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/app.component.ts
@@ -0,0 +1,48 @@
+// #docplaster
+// #docregion
+// #docregion imports
+import { Component, Inject } from '@angular/core';
+
+import { APP_CONFIG, AppConfig } from './app.config';
+import { Logger } from './logger.service';
+import { UserService } from './user.service';
+// #enddocregion imports
+
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+
+
+
+ User
+
+ {{userInfo}}
+ Next User
+
+
+
+
+ `,
+ providers: [Logger]
+})
+export class AppComponent {
+ title: string;
+
+ // #docregion ctor
+ constructor(
+ @Inject(APP_CONFIG) config: AppConfig,
+ private userService: UserService) {
+ this.title = config.title;
+ }
+ // #enddocregion ctor
+
+ get isAuthorized() { return this.user.isAuthorized; }
+ nextUser() { this.userService.getNewUser(); }
+ get user() { return this.userService.user; }
+
+ get userInfo() {
+ return `Current user, ${this.user.name}, is ` +
+ `${this.isAuthorized ? '' : 'not'} authorized. `;
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts
new file mode 100644
index 0000000000..f224e6e1ab
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/app.config.ts
@@ -0,0 +1,16 @@
+// #docregion token
+import { InjectionToken } from '@angular/core';
+
+export let APP_CONFIG = new InjectionToken('app.config');
+// #enddocregion token
+
+// #docregion config
+export interface AppConfig {
+ apiEndpoint: string;
+ title: string;
+}
+
+export const HERO_DI_CONFIG: AppConfig = {
+ apiEndpoint: 'api.heroes.com',
+ title: 'Dependency Injection'
+};
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/app.module.ts b/public/docs/_examples/dependency-injection/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..67ae8ae913
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/app.module.ts
@@ -0,0 +1,60 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+import { CarComponent } from './car/car.component';
+import { HeroesComponent } from './heroes/heroes.component';
+import { HeroListComponent } from './heroes/hero-list.component';
+import { InjectorComponent } from './injector.component';
+import { TestComponent } from './test.component';
+import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
+import { UserService } from './user.service';
+import {
+ ProvidersComponent,
+ Provider1Component,
+ Provider3Component,
+ Provider4Component,
+ Provider5Component,
+ Provider6aComponent,
+ Provider6bComponent,
+ Provider7Component,
+ Provider8Component,
+ Provider9Component,
+ Provider10Component,
+} from './providers.component';
+
+// #docregion ngmodule
+@NgModule({
+ imports: [
+ BrowserModule
+ ],
+ declarations: [
+ AppComponent,
+ CarComponent,
+ HeroesComponent,
+ // #enddocregion ngmodule
+ HeroListComponent,
+ InjectorComponent,
+ TestComponent,
+ ProvidersComponent,
+ Provider1Component,
+ Provider3Component,
+ Provider4Component,
+ Provider5Component,
+ Provider6aComponent,
+ Provider6bComponent,
+ Provider7Component,
+ Provider8Component,
+ Provider9Component,
+ Provider10Component,
+ // #docregion ngmodule
+ ],
+ // #docregion ngmodule-providers
+ providers: [
+ UserService,
+ { provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
+ ],
+ // #enddocregion ngmodule-providers
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/car/car-creations.ts b/public/docs/_examples/dependency-injection/ts/src/app/car/car-creations.ts
new file mode 100644
index 0000000000..c758c72951
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/car/car-creations.ts
@@ -0,0 +1,46 @@
+// Examples with car and engine variations
+
+// #docplaster
+import { Car, Engine, Tires } from './car';
+
+///////// example 1 ////////////
+export function simpleCar() {
+ // #docregion car-ctor-instantiation
+ // Simple car with 4 cylinders and Flintstone tires.
+ let car = new Car(new Engine(), new Tires());
+ // #enddocregion car-ctor-instantiation
+ car.description = 'Simple';
+ return car;
+}
+
+
+///////// example 2 ////////////
+// #docregion car-ctor-instantiation-with-param
+ class Engine2 {
+ constructor(public cylinders: number) { }
+ }
+// #enddocregion car-ctor-instantiation-with-param
+export function superCar() {
+// #docregion car-ctor-instantiation-with-param
+ // Super car with 12 cylinders and Flintstone tires.
+ let bigCylinders = 12;
+ let car = new Car(new Engine2(bigCylinders), new Tires());
+// #enddocregion car-ctor-instantiation-with-param
+ car.description = 'Super';
+ return car;
+}
+
+/////////// example 3 //////////
+ // #docregion car-ctor-instantiation-with-mocks
+ class MockEngine extends Engine { cylinders = 8; }
+ class MockTires extends Tires { make = 'YokoGoodStone'; }
+
+ // #enddocregion car-ctor-instantiation-with-mocks
+export function testCar() {
+ // #docregion car-ctor-instantiation-with-mocks
+ // Test car with 8 cylinders and YokoGoodStone tires.
+ let car = new Car(new MockEngine(), new MockTires());
+ // #enddocregion car-ctor-instantiation-with-mocks
+ car.description = 'Test';
+ return car;
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/car/car-factory.ts b/public/docs/_examples/dependency-injection/ts/src/app/car/car-factory.ts
new file mode 100644
index 0000000000..06daafe63b
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/car/car-factory.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { Engine, Tires, Car } from './car';
+
+// BAD pattern!
+export class CarFactory {
+ createCar() {
+ let car = new Car(this.createEngine(), this.createTires());
+ car.description = 'Factory';
+ return car;
+ }
+
+ createEngine() {
+ return new Engine();
+ }
+
+ createTires() {
+ return new Tires();
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/car/car-injector.ts b/public/docs/_examples/dependency-injection/ts/src/app/car/car-injector.ts
new file mode 100644
index 0000000000..4f7498ee4e
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/car/car-injector.ts
@@ -0,0 +1,27 @@
+import { ReflectiveInjector } from '@angular/core';
+
+import { Car, Engine, Tires } from './car';
+import { Logger } from '../logger.service';
+
+// #docregion injector
+export function useInjector() {
+ let injector: ReflectiveInjector;
+ // #enddocregion injector
+ /*
+ // #docregion injector-no-new
+ // Cannot instantiate an ReflectiveInjector like this!
+ let injector = new ReflectiveInjector([Car, Engine, Tires]);
+ // #enddocregion injector-no-new
+ */
+ // #docregion injector, injector-create-and-call
+ injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
+ // #docregion injector-call
+ let car = injector.get(Car);
+ // #enddocregion injector-call, injector-create-and-call
+ car.description = 'Injector';
+
+ injector = ReflectiveInjector.resolveAndCreate([Logger]);
+ let logger = injector.get(Logger);
+ logger.log('Injector car.drive() said: ' + car.drive());
+ return car;
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/car/car-no-di.ts b/public/docs/_examples/dependency-injection/ts/src/app/car/car-no-di.ts
new file mode 100644
index 0000000000..9026edebc2
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/car/car-no-di.ts
@@ -0,0 +1,24 @@
+// Car without DI
+import { Engine, Tires } from './car';
+
+// #docregion car
+export class Car {
+
+ // #docregion car-ctor
+ public engine: Engine;
+ public tires: Tires;
+ public description = 'No DI';
+
+ constructor() {
+ this.engine = new Engine();
+ this.tires = new Tires();
+ }
+ // #enddocregion car-ctor
+
+ // Method using the engine and tires
+ drive() {
+ return `${this.description} car with ` +
+ `${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
+ }
+}
+// #enddocregion car
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/car/car.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/car/car.component.ts
new file mode 100644
index 0000000000..9a835ef124
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/car/car.component.ts
@@ -0,0 +1,38 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { Car, Engine, Tires } from './car';
+import { Car as CarNoDi } from './car-no-di';
+import { CarFactory } from './car-factory';
+
+import { testCar,
+ simpleCar,
+ superCar } from './car-creations';
+
+import { useInjector } from './car-injector';
+
+
+@Component({
+ selector: 'my-car',
+ template: `
+ Cars
+ {{car.drive()}}
+ {{noDiCar.drive()}}
+ {{injectorCar.drive()}}
+ {{factoryCar.drive()}}
+ {{simpleCar.drive()}}
+ {{superCar.drive()}}
+ {{testCar.drive()}}
+ `,
+ providers: [Car, Engine, Tires]
+})
+export class CarComponent {
+ factoryCar = (new CarFactory).createCar();
+ injectorCar = useInjector();
+ noDiCar = new CarNoDi;
+ simpleCar = simpleCar();
+ superCar = superCar();
+ testCar = testCar();
+
+ constructor(public car: Car) {}
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/car/car.ts b/public/docs/_examples/dependency-injection/ts/src/app/car/car.ts
new file mode 100644
index 0000000000..37162c570b
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/car/car.ts
@@ -0,0 +1,25 @@
+import { Injectable } from '@angular/core';
+
+export class Engine {
+ public cylinders = 4;
+}
+
+export class Tires {
+ public make = 'Flintstone';
+ public model = 'Square';
+}
+
+@Injectable()
+export class Car {
+ // #docregion car-ctor
+ public description = 'DI';
+
+ constructor(public engine: Engine, public tires: Tires) { }
+ // #enddocregion car-ctor
+
+ // Method using the engine and tires
+ drive() {
+ return `${this.description} car with ` +
+ `${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.1.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.1.ts
new file mode 100644
index 0000000000..ba32366aa9
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.1.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { HEROES } from './mock-heroes';
+
+@Component({
+ selector: 'hero-list',
+ template: `
+
+ {{hero.id}} - {{hero.name}}
+
+ `
+})
+export class HeroListComponent {
+ heroes = HEROES;
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.2.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.2.ts
new file mode 100644
index 0000000000..cb23d3257c
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.2.ts
@@ -0,0 +1,31 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+import { Hero } from './hero';
+// #enddocregion
+import { HeroService } from './hero.service.1';
+/*
+// #docregion
+import { HeroService } from './hero.service';
+// #enddocregion
+*/
+// #docregion
+
+@Component({
+ selector: 'hero-list',
+ template: `
+
+ {{hero.id}} - {{hero.name}}
+
+ `
+})
+export class HeroListComponent {
+ heroes: Hero[];
+
+ // #docregion ctor
+ constructor(heroService: HeroService) {
+ this.heroes = heroService.getHeroes();
+ }
+ // #enddocregion ctor
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.ts
new file mode 100644
index 0000000000..db3a325bdb
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero-list.component.ts
@@ -0,0 +1,26 @@
+/* tslint:disable:one-line */
+// #docregion
+import { Component } from '@angular/core';
+
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+@Component({
+ selector: 'hero-list',
+ template: `
+
+ {{hero.id}} - {{hero.name}}
+ ({{hero.isSecret ? 'secret' : 'public'}})
+
+ `,
+})
+export class HeroListComponent {
+ heroes: Hero[];
+
+ // #docregion ctor-signature
+ constructor(heroService: HeroService)
+ // #enddocregion ctor-signature
+ {
+ this.heroes = heroService.getHeroes();
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.1.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.1.ts
new file mode 100644
index 0000000000..2e0e3ca734
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.1.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { HEROES } from './mock-heroes';
+
+@Injectable()
+export class HeroService {
+ getHeroes() { return HEROES; }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.2.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.2.ts
new file mode 100644
index 0000000000..6b3a98306a
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.2.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { HEROES } from './mock-heroes';
+import { Logger } from '../logger.service';
+
+@Injectable()
+export class HeroService {
+
+ // #docregion ctor
+ constructor(private logger: Logger) { }
+ // #enddocregion ctor
+
+ getHeroes() {
+ this.logger.log('Getting heroes ...');
+ return HEROES;
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.provider.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.provider.ts
new file mode 100644
index 0000000000..6de4ebee90
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.provider.ts
@@ -0,0 +1,19 @@
+/* tslint:disable:one-line */
+// #docregion
+import { HeroService } from './hero.service';
+import { Logger } from '../logger.service';
+import { UserService } from '../user.service';
+
+// #docregion factory
+let heroServiceFactory = (logger: Logger, userService: UserService) => {
+ return new HeroService(logger, userService.user.isAuthorized);
+};
+// #enddocregion factory
+
+// #docregion provider
+export let heroServiceProvider =
+ { provide: HeroService,
+ useFactory: heroServiceFactory,
+ deps: [Logger, UserService]
+ };
+// #enddocregion provider
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.ts
new file mode 100644
index 0000000000..fb03ec1de6
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.service.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { HEROES } from './mock-heroes';
+import { Logger } from '../logger.service';
+
+@Injectable()
+export class HeroService {
+ // #docregion internals
+ constructor(
+ private logger: Logger,
+ private isAuthorized: boolean) { }
+
+ getHeroes() {
+ let auth = this.isAuthorized ? 'authorized ' : 'unauthorized';
+ this.logger.log(`Getting heroes for ${auth} user.`);
+ return HEROES.filter(hero => this.isAuthorized || !hero.isSecret);
+ }
+ // #enddocregion internals
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.ts
new file mode 100644
index 0000000000..5c49328241
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/hero.ts
@@ -0,0 +1,6 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+ isSecret = false;
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/heroes.component.1.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/heroes.component.1.ts
new file mode 100644
index 0000000000..e0e9deae08
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/heroes.component.1.ts
@@ -0,0 +1,21 @@
+// #docplaster
+// #docregion full, v1
+import { Component } from '@angular/core';
+// #enddocregion v1
+
+import { HeroService } from './hero.service';
+// #enddocregion full
+
+// #docregion full, v1
+
+@Component({
+ selector: 'my-heroes',
+ // #enddocregion v1
+ providers: [HeroService],
+ // #docregion v1
+ template: `
+ Heroes
+
+ `
+})
+export class HeroesComponent { }
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/heroes.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..5923f7590b
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/heroes.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { heroServiceProvider } from './hero.service.provider';
+
+@Component({
+ selector: 'my-heroes',
+ template: `
+ Heroes
+
+ `,
+ providers: [heroServiceProvider]
+})
+export class HeroesComponent { }
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/heroes/mock-heroes.ts b/public/docs/_examples/dependency-injection/ts/src/app/heroes/mock-heroes.ts
new file mode 100644
index 0000000000..79a91dc03a
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/heroes/mock-heroes.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Hero } from './hero';
+
+export var HEROES: Hero[] = [
+ { id: 11, isSecret: false, name: 'Mr. Nice' },
+ { id: 12, isSecret: false, name: 'Narco' },
+ { id: 13, isSecret: false, name: 'Bombasto' },
+ { id: 14, isSecret: false, name: 'Celeritas' },
+ { id: 15, isSecret: false, name: 'Magneta' },
+ { id: 16, isSecret: false, name: 'RubberMan' },
+ { id: 17, isSecret: false, name: 'Dynama' },
+ { id: 18, isSecret: true, name: 'Dr IQ' },
+ { id: 19, isSecret: true, name: 'Magma' },
+ { id: 20, isSecret: true, name: 'Tornado' }
+];
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/injector.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/injector.component.ts
new file mode 100644
index 0000000000..1b1a065e4c
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/injector.component.ts
@@ -0,0 +1,49 @@
+// #docplaster
+// #docregion
+import { Component, Injector, OnInit } from '@angular/core';
+
+import { Car, Engine, Tires } from './car/car';
+import { Hero } from './heroes/hero';
+import { HeroService } from './heroes/hero.service';
+import { heroServiceProvider } from './heroes/hero.service.provider';
+import { Logger } from './logger.service';
+
+// #docregion injector
+@Component({
+ selector: 'my-injectors',
+ template: `
+ Other Injections
+ {{car.drive()}}
+ {{hero.name}}
+ {{rodent}}
+ `,
+ providers: [Car, Engine, Tires, heroServiceProvider, Logger]
+})
+export class InjectorComponent implements OnInit {
+ car: Car;
+
+ // #docregion get-hero-service
+ heroService: HeroService;
+ // #enddocregion get-hero-service
+ hero: Hero;
+
+ constructor(private injector: Injector) { }
+
+ ngOnInit() {
+ this.car = this.injector.get(Car);
+ this.heroService = this.injector.get(HeroService);
+ this.hero = this.heroService.getHeroes()[0];
+ }
+
+ get rodent() {
+ let rousDontExist = `R.O.U.S.'s? I don't think they exist!`;
+ return this.injector.get(ROUS, rousDontExist);
+ }
+}
+// #enddocregion injector
+
+/**
+ * R.O.U.S. - Rodents Of Unusual Size
+ * // https://www.youtube.com/watch?v=BOv5ZjAOpC8
+ */
+class ROUS { }
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/logger.service.ts b/public/docs/_examples/dependency-injection/ts/src/app/logger.service.ts
new file mode 100644
index 0000000000..e943523ad2
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/logger.service.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class Logger {
+ logs: string[] = []; // capture logs for testing
+
+ log(message: string) {
+ this.logs.push(message);
+ console.log(message);
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/providers.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/providers.component.ts
new file mode 100644
index 0000000000..a96bfce99e
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/providers.component.ts
@@ -0,0 +1,267 @@
+/* tslint:disable:one-line:check-open-brace*/
+// Examples of provider arrays
+// #docplaster
+import { Component, Inject, Injectable, OnInit } from '@angular/core';
+
+import { APP_CONFIG, AppConfig,
+ HERO_DI_CONFIG } from './app.config';
+
+import { HeroService } from './heroes/hero.service';
+import { heroServiceProvider } from './heroes/hero.service.provider';
+import { Logger } from './logger.service';
+import { UserService } from './user.service';
+
+let template = '{{log}}';
+
+//////////////////////////////////////////
+@Component({
+ selector: 'provider-1',
+ template: template,
+ // #docregion providers-1, providers-logger
+ providers: [Logger]
+ // #enddocregion providers-1, providers-logger
+})
+export class Provider1Component {
+ log: string;
+ constructor(logger: Logger) {
+ logger.log('Hello from logger provided with Logger class');
+ this.log = logger.logs[0];
+ }
+}
+
+//////////////////////////////////////////
+@Component({
+ selector: 'provider-3',
+ template: template,
+ providers:
+ // #docregion providers-3
+ [{ provide: Logger, useClass: Logger }]
+ // #enddocregion providers-3
+})
+export class Provider3Component {
+ log: string;
+ constructor(logger: Logger) {
+ logger.log('Hello from logger provided with useClass:Logger');
+ this.log = logger.logs[0];
+ }
+}
+
+//////////////////////////////////////////
+class BetterLogger extends Logger {}
+
+@Component({
+ selector: 'provider-4',
+ template: template,
+ providers:
+ // #docregion providers-4
+ [{ provide: Logger, useClass: BetterLogger }]
+ // #enddocregion providers-4
+})
+export class Provider4Component {
+ log: string;
+ constructor(logger: Logger) {
+ logger.log('Hello from logger provided with useClass:BetterLogger');
+ this.log = logger.logs[0];
+ }
+}
+
+//////////////////////////////////////////
+// #docregion EvenBetterLogger
+@Injectable()
+class EvenBetterLogger extends Logger {
+ constructor(private userService: UserService) { super(); }
+
+ log(message: string) {
+ let name = this.userService.user.name;
+ super.log(`Message to ${name}: ${message}`);
+ }
+}
+// #enddocregion EvenBetterLogger
+
+@Component({
+ selector: 'provider-5',
+ template: template,
+ providers:
+ // #docregion providers-5
+ [ UserService,
+ { provide: Logger, useClass: EvenBetterLogger }]
+ // #enddocregion providers-5
+})
+export class Provider5Component {
+ log: string;
+ constructor(logger: Logger) {
+ logger.log('Hello from EvenBetterlogger');
+ this.log = logger.logs[0];
+ }
+}
+
+//////////////////////////////////////////
+class NewLogger extends Logger {}
+class OldLogger {
+ logs: string[] = [];
+ log(message: string) {
+ throw new Error('Should not call the old logger!');
+ };
+}
+
+@Component({
+ selector: 'provider-6a',
+ template: template,
+ providers:
+ // #docregion providers-6a
+ [ NewLogger,
+ // Not aliased! Creates two instances of `NewLogger`
+ { provide: OldLogger, useClass: NewLogger}]
+ // #enddocregion providers-6a
+})
+export class Provider6aComponent {
+ log: string;
+ constructor(newLogger: NewLogger, oldLogger: OldLogger) {
+ if (newLogger === oldLogger){
+ throw new Error('expected the two loggers to be different instances');
+ }
+ oldLogger.log('Hello OldLogger (but we want NewLogger)');
+ // The newLogger wasn't called so no logs[]
+ // display the logs of the oldLogger.
+ this.log = newLogger.logs[0] || oldLogger.logs[0];
+ }
+}
+
+@Component({
+ selector: 'provider-6b',
+ template: template,
+ providers:
+ // #docregion providers-6b
+ [ NewLogger,
+ // Alias OldLogger w/ reference to NewLogger
+ { provide: OldLogger, useExisting: NewLogger}]
+ // #enddocregion providers-6b
+})
+export class Provider6bComponent {
+ log: string;
+ constructor(newLogger: NewLogger, oldLogger: OldLogger) {
+ if (newLogger !== oldLogger){
+ throw new Error('expected the two loggers to be the same instance');
+ }
+ oldLogger.log('Hello from NewLogger (via aliased OldLogger)');
+ this.log = newLogger.logs[0];
+ }
+}
+
+//////////////////////////////////////////
+// #docregion silent-logger
+// An object in the shape of the logger service
+let silentLogger = {
+ logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
+ log: () => {}
+};
+// #enddocregion silent-logger
+
+@Component({
+ selector: 'provider-7',
+ template: template,
+ providers:
+ // #docregion providers-7
+ [{ provide: Logger, useValue: silentLogger }]
+ // #enddocregion providers-7
+})
+export class Provider7Component {
+ log: string;
+ constructor(logger: Logger) {
+ logger.log('Hello from logger provided with useValue');
+ this.log = logger.logs[0];
+ }
+}
+/////////////////
+
+@Component({
+ selector: 'provider-8',
+ template: template,
+ providers: [heroServiceProvider, Logger, UserService]
+})
+export class Provider8Component {
+ // must be true else this component would have blown up at runtime
+ log = 'Hero service injected successfully via heroServiceProvider';
+
+ // #docregion provider-8-ctor
+ constructor(heroService: HeroService) { }
+ // #enddocregion provider-8-ctor
+}
+
+/////////////////
+@Component({
+ selector: 'provider-9',
+ template: template,
+ /*
+ // #docregion providers-9-interface
+ // FAIL! Can't use interface as provider token
+ [{ provide: AppConfig, useValue: HERO_DI_CONFIG })]
+ // #enddocregion providers-9-interface
+ */
+ // #docregion providers-9
+ providers: [{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }]
+ // #enddocregion providers-9
+})
+export class Provider9Component implements OnInit {
+ log: string;
+ /*
+ // #docregion provider-9-ctor-interface
+ // FAIL! Can't inject using the interface as the parameter type
+ constructor(private config: AppConfig){ }
+ // #enddocregion provider-9-ctor-interface
+ */
+ // #docregion provider-9-ctor
+ constructor(@Inject(APP_CONFIG) private config: AppConfig) { }
+ // #enddocregion provider-9-ctor
+
+ ngOnInit() {
+ this.log = 'APP_CONFIG Application title is ' + this.config.title;
+ }
+}
+//////////////////////////////////////////
+// Sample providers 1 to 7 illustrate a required logger dependency.
+// Optional logger, can be null
+// #docregion import-optional
+import { Optional } from '@angular/core';
+// #enddocregion import-optional
+
+let some_message = 'Hello from the injected logger';
+
+@Component({
+ selector: 'provider-10',
+ template: template,
+ providers: [{ provide: Logger, useValue: null }]
+})
+export class Provider10Component implements OnInit {
+ log: string;
+ // #docregion provider-10-ctor
+ constructor(@Optional() private logger: Logger) {
+ if (this.logger) {
+ this.logger.log(some_message);
+ }
+ }
+ // #enddocregion provider-10-ctor
+
+ ngOnInit() {
+ this.log = this.logger ? this.logger.logs[0] : 'Optional logger was not available';
+ }
+}
+
+/////////////////
+@Component({
+ selector: 'my-providers',
+ template: `
+ Provider variations
+
+
+
+
+
+
+
+
+
+
+ `
+})
+export class ProvidersComponent { }
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/test.component.ts b/public/docs/_examples/dependency-injection/ts/src/app/test.component.ts
new file mode 100644
index 0000000000..fc0fef75a8
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/test.component.ts
@@ -0,0 +1,55 @@
+/* tslint:disable */
+// Simulate a simple test
+// Reader should look to the testing chapter for the real thing
+
+import { Component } from '@angular/core';
+
+import { HeroService } from './heroes/hero.service';
+import { HeroListComponent } from './heroes/hero-list.component';
+
+@Component({
+ selector: 'my-tests',
+ template: `
+ Tests
+ Tests {{results.pass}}: {{results.message}}
+ `
+})
+export class TestComponent {
+ results = runTests();
+}
+
+/////////////////////////////////////
+function runTests() {
+
+ // #docregion spec
+ let expectedHeroes = [{name: 'A'}, {name: 'B'}]
+ let mockService = {getHeroes: () => expectedHeroes }
+
+ it('should have heroes when HeroListComponent created', () => {
+ let hlc = new HeroListComponent(mockService);
+ expect(hlc.heroes.length).toEqual(expectedHeroes.length);
+ });
+ // #enddocregion spec
+
+ return testResults;
+}
+
+//////////////////////////////////
+// Fake Jasmine infrastructure
+var testName: string;
+var testResults: {pass: string; message: string};
+
+function expect(actual: any) {
+ return {
+ toEqual: function(expected: any){
+ testResults = actual === expected ?
+ {pass: 'passed', message: testName} :
+ {pass: 'failed', message: `${testName}; expected ${actual} to equal ${expected}.`};
+ }
+ };
+}
+
+function it(label: string, test: () => void) {
+ testName = label;
+ test();
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/app/user.service.ts b/public/docs/_examples/dependency-injection/ts/src/app/user.service.ts
new file mode 100644
index 0000000000..8fdda925db
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/app/user.service.ts
@@ -0,0 +1,22 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+export class User {
+ constructor(
+ public name: string,
+ public isAuthorized = false) { }
+}
+
+// Todo: get the user; don't 'new' it.
+let alice = new User('Alice', true);
+let bob = new User('Bob', false);
+
+@Injectable()
+export class UserService {
+ user = bob; // initial user is Bob
+
+ // swap users
+ getNewUser() {
+ return this.user = this.user === bob ? alice : bob;
+ }
+}
diff --git a/public/docs/_examples/dependency-injection/ts/src/index.html b/public/docs/_examples/dependency-injection/ts/src/index.html
new file mode 100644
index 0000000000..87a9d7e694
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Dependency Injection
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading my-app ...
+
+
+
diff --git a/public/docs/_examples/dependency-injection/ts/src/main.ts b/public/docs/_examples/dependency-injection/ts/src/main.ts
new file mode 100644
index 0000000000..1a1d481719
--- /dev/null
+++ b/public/docs/_examples/dependency-injection/ts/src/main.ts
@@ -0,0 +1,6 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+// #docregion bootstrap
+platformBrowserDynamic().bootstrapModule(AppModule);
+// #enddocregion bootstrap
diff --git a/public/docs/_examples/deployment/ts/.gitignore b/public/docs/_examples/deployment/ts/.gitignore
new file mode 100644
index 0000000000..7f794a0b16
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/.gitignore
@@ -0,0 +1 @@
+!systemjs.config.server.js
diff --git a/public/docs/_examples/deployment/ts/example-config.json b/public/docs/_examples/deployment/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/deployment/ts/src/app/app.component.ts b/public/docs/_examples/deployment/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..47731c13a4
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/app/app.component.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+ Simple Deployment
+
+ Crisis Center
+ Heroes
+
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/deployment/ts/src/app/app.module.ts b/public/docs/_examples/deployment/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..a885bc2918
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/app/app.module.ts
@@ -0,0 +1,29 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule, Routes } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { HeroListComponent } from './hero-list.component';
+
+const appRoutes: Routes = [
+ { path: 'crisis-center', component: CrisisListComponent },
+ { path: 'heroes', component: HeroListComponent },
+
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' }
+];
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forRoot(appRoutes)
+ ],
+ declarations: [
+ AppComponent,
+ CrisisListComponent,
+ HeroListComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/deployment/ts/src/app/crisis-list.component.ts b/public/docs/_examples/deployment/ts/src/app/crisis-list.component.ts
new file mode 100644
index 0000000000..62ef9e7555
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/app/crisis-list.component.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ CRISIS CENTER
+ Get your crisis here
`
+})
+export class CrisisListComponent { }
diff --git a/public/docs/_examples/deployment/ts/src/app/hero-list.component.ts b/public/docs/_examples/deployment/ts/src/app/hero-list.component.ts
new file mode 100644
index 0000000000..479f73b508
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/app/hero-list.component.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ HEROES
+ Get your heroes here
+ `
+})
+export class HeroListComponent { }
diff --git a/public/docs/_examples/deployment/ts/src/index.html b/public/docs/_examples/deployment/ts/src/index.html
new file mode 100644
index 0000000000..770e64bc4d
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/index.html
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+ Simple Deployment
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loading...
+
+
+
diff --git a/public/docs/_examples/deployment/ts/src/main.ts b/public/docs/_examples/deployment/ts/src/main.ts
new file mode 100644
index 0000000000..6f82bbc745
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/main.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+// #docregion enableProdMode
+import { enableProdMode } from '@angular/core';
+
+// Enable production mode unless running locally
+if (!/localhost/.test(document.location.host)) {
+ enableProdMode();
+}
+// #enddocregion enableProdMode
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/deployment/ts/src/systemjs.config.server.js b/public/docs/_examples/deployment/ts/src/systemjs.config.server.js
new file mode 100644
index 0000000000..40c4426064
--- /dev/null
+++ b/public/docs/_examples/deployment/ts/src/systemjs.config.server.js
@@ -0,0 +1,46 @@
+// #docregion
+/**
+ * System configuration for deployment without installing node_modules
+ * Loads umd packages from the web instead
+ * Adjust as necessary for your application needs.
+ */
+(function (global) {
+ System.config({
+ // #docregion paths
+ paths: {
+ 'npm:': 'https://unpkg.com/' // path serves as alias
+ },
+ // #enddocregion paths
+ // map tells the System loader where to look for things
+ map: {
+ app: 'app', // location of transpiled app files
+
+ // angular minimized umd bundles
+ '@angular/core': 'npm:@angular/core/bundles/core.umd.min.js',
+ '@angular/common': 'npm:@angular/common/bundles/common.umd.min.js',
+ '@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.min.js',
+ '@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.min.js',
+ '@angular/platform-browser-dynamic': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.min.js',
+ '@angular/http': 'npm:@angular/http/bundles/http.umd.min.js',
+ '@angular/router': 'npm:@angular/router/bundles/router.umd.min.js',
+ '@angular/router/upgrade': 'npm:@angular/router/bundles/router-upgrade.umd.min.js',
+ '@angular/forms': 'npm:@angular/forms/bundles/forms.umd.min.js',
+ '@angular/upgrade': 'npm:@angular/upgrade/bundles/upgrade.umd.min.js',
+ '@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.min.js',
+
+ // other libraries
+ 'rxjs': 'npm:rxjs@5.0.1',
+ 'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
+ },
+ // packages tells the System loader how to load when no filename and/or no extension
+ packages: {
+ app: {
+ main: './main.js',
+ defaultExtension: 'js'
+ },
+ rxjs: {
+ defaultExtension: 'js'
+ }
+ }
+ });
+ })(this);
diff --git a/public/docs/_examples/displaying-data/e2e-spec.ts b/public/docs/_examples/displaying-data/e2e-spec.ts
new file mode 100644
index 0000000000..96c52c5d00
--- /dev/null
+++ b/public/docs/_examples/displaying-data/e2e-spec.ts
@@ -0,0 +1,29 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Displaying Data Tests', function () {
+ let _title = 'Tour of Heroes';
+ let _defaultHero = 'Windstorm';
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('should display correct title: ' + _title, function () {
+ expect(element(by.css('h1')).getText()).toEqual(_title);
+ });
+
+ it('should have correct default hero: ' + _defaultHero, function () {
+ expect(element(by.css('h2')).getText()).toContain(_defaultHero);
+ });
+
+ it('should have heroes', function () {
+ let heroEls = element.all(by.css('li'));
+ expect(heroEls.count()).not.toBe(0, 'should have heroes');
+ });
+
+ it('should display "there are many heroes!"', function () {
+ expect(element(by.css('ul ~ p')).getText()).toContain('There are many heroes!');
+ });
+});
diff --git a/public/docs/_examples/displaying-data/ts/example-config.json b/public/docs/_examples/displaying-data/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/displaying-data/ts/plnkr.json b/public/docs/_examples/displaying-data/ts/plnkr.json
new file mode 100644
index 0000000000..b4572f0fb6
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/plnkr.json
@@ -0,0 +1,11 @@
+{
+ "description": "Displaying Data",
+ "basePath": "src/",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/app-ctor.component.ts",
+ "!**/*.[1,2,3].*"
+ ],
+ "tags": ["Template"]
+}
diff --git a/public/docs/_examples/displaying-data/ts/src/app/app-ctor.component.ts b/public/docs/_examples/displaying-data/ts/src/app/app-ctor.component.ts
new file mode 100644
index 0000000000..b275baa8e6
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/app-ctor.component.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app-ctor',
+ template: `
+ {{title}} [Ctor version]
+ My favorite hero is: {{myHero}}
+ `
+})
+// #docregion class
+export class AppCtorComponent {
+ title: string;
+ myHero: string;
+
+ constructor() {
+ this.title = 'Tour of Heroes';
+ this.myHero = 'Windstorm';
+ }
+}
diff --git a/public/docs/_examples/displaying-data/ts/src/app/app.component.1.ts b/public/docs/_examples/displaying-data/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..1cbeb0f731
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/app.component.1.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ {{title}}
+ My favorite hero is: {{myHero}}
+ `
+ // #enddocregion template
+})
+export class AppComponent {
+ title = 'Tour of Heroes';
+ myHero = 'Windstorm';
+}
diff --git a/public/docs/_examples/displaying-data/ts/src/app/app.component.2.ts b/public/docs/_examples/displaying-data/ts/src/app/app.component.2.ts
new file mode 100644
index 0000000000..da7a653973
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/app.component.2.ts
@@ -0,0 +1,26 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ {{title}}
+ My favorite hero is: {{myHero}}
+ Heroes:
+
+ // #docregion li
+
+ {{ hero }}
+
+ // #enddocregion li
+
+ `
+ // #enddocregion template
+})
+// #docregion class
+export class AppComponent {
+ title = 'Tour of Heroes';
+ heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
+ myHero = this.heroes[0];
+}
diff --git a/public/docs/_examples/displaying-data/ts/src/app/app.component.3.ts b/public/docs/_examples/displaying-data/ts/src/app/app.component.3.ts
new file mode 100644
index 0000000000..06ab060557
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/app.component.3.ts
@@ -0,0 +1,35 @@
+// #docregion
+import { Component } from '@angular/core';
+
+// #docregion import
+import { Hero } from './hero';
+// #enddocregion import
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ {{title}}
+ My favorite hero is: {{myHero.name}}
+ Heroes:
+
+ `
+ // #enddocregion template
+})
+// #docregion class
+export class AppComponent {
+ title = 'Tour of Heroes';
+ // #docregion heroes
+ heroes = [
+ new Hero(1, 'Windstorm'),
+ new Hero(13, 'Bombasto'),
+ new Hero(15, 'Magneta'),
+ new Hero(20, 'Tornado')
+ ];
+ myHero = this.heroes[0];
+ // #enddocregion heroes
+}
diff --git a/public/docs/_examples/displaying-data/ts/src/app/app.component.ts b/public/docs/_examples/displaying-data/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..7234959265
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/app.component.ts
@@ -0,0 +1,32 @@
+// #docplaster
+// #docregion final
+import { Component } from '@angular/core';
+
+import { Hero } from './hero';
+
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+ My favorite hero is: {{myHero.name}}
+ Heroes:
+
+ // #docregion message
+ 3">There are many heroes!
+ // #enddocregion message
+`
+})
+export class AppComponent {
+ title = 'Tour of Heroes';
+ heroes = [
+ new Hero(1, 'Windstorm'),
+ new Hero(13, 'Bombasto'),
+ new Hero(15, 'Magneta'),
+ new Hero(20, 'Tornado')
+ ];
+ myHero = this.heroes[0];
+}
diff --git a/public/docs/_examples/displaying-data/ts/src/app/app.module.ts b/public/docs/_examples/displaying-data/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..362f3401fa
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/app.module.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule
+ ],
+ declarations: [
+ AppComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/displaying-data/ts/src/app/hero.ts b/public/docs/_examples/displaying-data/ts/src/app/hero.ts
new file mode 100644
index 0000000000..f89d26ad63
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/app/hero.ts
@@ -0,0 +1,9 @@
+// #docregion
+export class Hero {
+ constructor(
+ // #docregion id
+ public id: number,
+ // #enddocregion id
+ public name: string) { }
+}
+// #enddocregion
diff --git a/public/docs/_examples/displaying-data/ts/src/index.html b/public/docs/_examples/displaying-data/ts/src/index.html
new file mode 100644
index 0000000000..ddcbade46b
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/index.html
@@ -0,0 +1,28 @@
+
+
+
+ Displaying Data
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loading...
+
+
+
+
diff --git a/public/docs/_examples/displaying-data/ts/src/main.ts b/public/docs/_examples/displaying-data/ts/src/main.ts
new file mode 100644
index 0000000000..f332d1d245
--- /dev/null
+++ b/public/docs/_examples/displaying-data/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/forms/e2e-spec.ts b/public/docs/_examples/forms/e2e-spec.ts
new file mode 100644
index 0000000000..2afd370103
--- /dev/null
+++ b/public/docs/_examples/forms/e2e-spec.ts
@@ -0,0 +1,63 @@
+import { browser, element, by } from 'protractor';
+import { appLang, describeIf } from '../protractor-helpers';
+
+describeIf(appLang.appIsTs || appLang.appIsJs, 'Forms Tests', function () {
+
+ beforeEach(function () {
+ browser.get('');
+ });
+
+ it('should display correct title', function () {
+ expect(element.all(by.css('h1')).get(0).getText()).toEqual('Hero Form');
+ });
+
+
+ it('should not display message before submit', function () {
+ let ele = element(by.css('h2'));
+ expect(ele.isDisplayed()).toBe(false);
+ });
+
+ it('should hide form after submit', function () {
+ let ele = element.all(by.css('h1')).get(0);
+ expect(ele.isDisplayed()).toBe(true);
+ let b = element.all(by.css('button[type=submit]')).get(0);
+ b.click().then(function() {
+ expect(ele.isDisplayed()).toBe(false);
+ });
+ });
+
+ it('should display message after submit', function () {
+ let b = element.all(by.css('button[type=submit]')).get(0);
+ b.click().then(function() {
+ expect(element(by.css('h2')).getText()).toContain('You submitted the following');
+ });
+ });
+
+ it('should hide form after submit', function () {
+ let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
+ expect(alterEgoEle.isDisplayed()).toBe(true);
+ let submitButtonEle = element.all(by.css('button[type=submit]')).get(0);
+ submitButtonEle.click().then(function() {
+ expect(alterEgoEle.isDisplayed()).toBe(false);
+ });
+ });
+
+ it('should reflect submitted data after submit', function () {
+ let test = 'testing 1 2 3';
+ let newValue: string;
+ let alterEgoEle = element.all(by.css('input[name=alterEgo]')).get(0);
+ alterEgoEle.getAttribute('value').then(function(value: string) {
+ alterEgoEle.sendKeys(test);
+ newValue = value + test;
+ expect(alterEgoEle.getAttribute('value')).toEqual(newValue);
+ let b = element.all(by.css('button[type=submit]')).get(0);
+ return b.click();
+ }).then(function() {
+ let alterEgoTextEle = element(by.cssContainingText('div', 'Alter Ego'));
+ expect(alterEgoTextEle.isPresent()).toBe(true, 'cannot locate "Alter Ego" label');
+ let divEle = element(by.cssContainingText('div', newValue));
+ expect(divEle.isPresent()).toBe(true, 'cannot locate div with this text: ' + newValue);
+ });
+ });
+});
+
diff --git a/public/docs/_examples/forms/js/example-config.json b/public/docs/_examples/forms/js/example-config.json
new file mode 100644
index 0000000000..81f31aaf0d
--- /dev/null
+++ b/public/docs/_examples/forms/js/example-config.json
@@ -0,0 +1,3 @@
+{
+ "build": "build:babel"
+}
diff --git a/public/docs/_examples/forms/js/plnkr.json b/public/docs/_examples/forms/js/plnkr.json
new file mode 100644
index 0000000000..946cbb88f6
--- /dev/null
+++ b/public/docs/_examples/forms/js/plnkr.json
@@ -0,0 +1,5 @@
+{
+ "description": "Forms",
+ "basePath": "src/",
+ "files":["app/**/*.js", "**/*.html", "**/*.css"]
+}
diff --git a/public/docs/_examples/forms/js/src/app/app.component.js b/public/docs/_examples/forms/js/src/app/app.component.js
new file mode 100644
index 0000000000..56bd982416
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/app/app.component.js
@@ -0,0 +1,11 @@
+// #docregion
+(function(app) {
+ app.AppComponent = ng.core
+ .Component({
+ selector: 'my-app',
+ template: ' '
+ })
+ .Class({
+ constructor: function() {}
+ });
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/forms/js/src/app/app.module.js b/public/docs/_examples/forms/js/src/app/app.module.js
new file mode 100644
index 0000000000..92c7f8b9e5
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/app/app.module.js
@@ -0,0 +1,19 @@
+// #docplaster
+// #docregion
+(function(app) {
+ app.AppModule =
+ ng.core.NgModule({
+ imports: [
+ ng.platformBrowser.BrowserModule,
+ ng.forms.FormsModule
+ ],
+ declarations: [
+ app.AppComponent,
+ app.HeroFormComponent
+ ],
+ bootstrap: [ app.AppComponent ]
+ })
+ .Class({
+ constructor: function() {}
+ });
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/forms/js/src/app/hero-form.component.html b/public/docs/_examples/forms/js/src/app/hero-form.component.html
new file mode 100644
index 0000000000..279ded0866
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/app/hero-form.component.html
@@ -0,0 +1,196 @@
+
+
+
+
+
+
+
+
+
You submitted the following:
+
+
Name
+
{{ model.name }}
+
+
+
Alter Ego
+
{{ model.alterEgo }}
+
+
+
Power
+
{{ model.power }}
+
+
+
Edit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TODO: remove this: {{model.name}}
+
+
+
+
+ TODO: remove this: {{model.name}}
+
+
+
+
+
+
+ Name via form.controls = {{showFormControls(heroForm)}}
+
+
+
diff --git a/public/docs/_examples/forms/js/src/app/hero-form.component.js b/public/docs/_examples/forms/js/src/app/hero-form.component.js
new file mode 100644
index 0000000000..505993a1fd
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/app/hero-form.component.js
@@ -0,0 +1,52 @@
+// #docplaster
+// #docregion
+// #docregion first, final
+(function(app) {
+ app.HeroFormComponent = ng.core
+ .Component({
+ selector: 'hero-form',
+ templateUrl: 'app/hero-form.component.html'
+ })
+ .Class({
+ // #docregion submitted
+ constructor: [function() {
+ // #enddocregion submitted
+ this.powers = ['Really Smart', 'Super Flexible',
+ 'Super Hot', 'Weather Changer'
+ ];
+
+ this.model = new app.Hero(18, 'Dr IQ', this.powers[0],
+ 'Chuck Overstreet');
+
+ // #docregion submitted
+ this.submitted = false;
+ }],
+ onSubmit: function() {
+ this.submitted = true;
+ },
+ // #enddocregion submitted
+
+ // #enddocregion final
+ // TODO: Remove this when we're done
+ diagnostic: function() {
+ return JSON.stringify(this.model);
+ },
+ // #enddocregion first
+
+
+ //////// DO NOT SHOW IN DOCS ////////
+
+ // Reveal in html:
+ // AlterEgo via form.controls = {{showFormControls(hf)}}
+ showFormControls: function(form) {
+ return form.controls['alterEgo'] &&
+ // #docregion form-controls
+ form.controls['name'].value; // Dr. IQ
+ // #enddocregion form-controls
+ },
+ /////////////////////////////
+
+ // #docregion first, final
+ });
+})(window.app || (window.app = {}));
+// #enddocregion first, final
diff --git a/public/docs/_examples/forms/js/src/app/hero.js b/public/docs/_examples/forms/js/src/app/hero.js
new file mode 100644
index 0000000000..9c2449c922
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/app/hero.js
@@ -0,0 +1,11 @@
+// #docregion
+(function(app) {
+ app.Hero = Hero;
+
+ function Hero(id, name, power, alterEgo) {
+ this.id = id;
+ this.name = name;
+ this.power = power;
+ this.alterEgo = alterEgo;
+ }
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/forms/js/src/forms.css b/public/docs/_examples/forms/js/src/forms.css
new file mode 100644
index 0000000000..d7e11405b1
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/forms.css
@@ -0,0 +1,9 @@
+/* #docregion */
+.ng-valid[required] {
+ border-left: 5px solid #42A948; /* green */
+}
+
+.ng-invalid {
+ border-left: 5px solid #a94442; /* red */
+}
+/* #enddocregion */
\ No newline at end of file
diff --git a/public/docs/_examples/forms/js/src/index.html b/public/docs/_examples/forms/js/src/index.html
new file mode 100644
index 0000000000..3a41d74a3b
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/index.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+ Hero Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/forms/js/src/main.js b/public/docs/_examples/forms/js/src/main.js
new file mode 100644
index 0000000000..785823fa84
--- /dev/null
+++ b/public/docs/_examples/forms/js/src/main.js
@@ -0,0 +1,8 @@
+// #docregion
+(function(app) {
+ document.addEventListener('DOMContentLoaded', function() {
+ ng.platformBrowserDynamic
+ .platformBrowserDynamic()
+ .bootstrapModule(app.AppModule);
+ });
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/forms/ts/example-config.json b/public/docs/_examples/forms/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/forms/ts/plnkr.json b/public/docs/_examples/forms/ts/plnkr.json
new file mode 100644
index 0000000000..3f0abbfc3d
--- /dev/null
+++ b/public/docs/_examples/forms/ts/plnkr.json
@@ -0,0 +1,8 @@
+{
+ "description": "Forms",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js"
+ ]
+}
diff --git a/public/docs/_examples/forms/ts/src/app/app.component.ts b/public/docs/_examples/forms/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..454f7e03db
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/app.component.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/forms/ts/src/app/app.module.ts b/public/docs/_examples/forms/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..f214c02714
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/app.module.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { HeroFormComponent } from './hero-form.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ declarations: [
+ AppComponent,
+ HeroFormComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/forms/ts/src/app/hero-form.component.html b/public/docs/_examples/forms/ts/src/app/hero-form.component.html
new file mode 100644
index 0000000000..73b703f789
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/hero-form.component.html
@@ -0,0 +1,212 @@
+
+
+
+
+
+
+
+
+
You submitted the following:
+
+
Name
+
{{ model.name }}
+
+
+
Alter Ego
+
{{ model.alterEgo }}
+
+
+
Power
+
{{ model.power }}
+
+
+
Edit
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ TODO: remove this: {{model.name}}
+
+
+
+
+ TODO: remove this: {{model.name}}
+
+
+
+
+
TODO: remove this: {{spy.className}}
+
+
+
diff --git a/public/docs/_examples/forms/ts/src/app/hero-form.component.ts b/public/docs/_examples/forms/ts/src/app/hero-form.component.ts
new file mode 100644
index 0000000000..34be523a7f
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/hero-form.component.ts
@@ -0,0 +1,59 @@
+// #docplaster
+// #docregion , v1, final
+import { Component } from '@angular/core';
+
+import { Hero } from './hero';
+
+@Component({
+ selector: 'hero-form',
+ templateUrl: './hero-form.component.html'
+})
+export class HeroFormComponent {
+
+ powers = ['Really Smart', 'Super Flexible',
+ 'Super Hot', 'Weather Changer'];
+
+ model = new Hero(18, 'Dr IQ', this.powers[0], 'Chuck Overstreet');
+
+ // #docregion submitted
+ submitted = false;
+
+ onSubmit() { this.submitted = true; }
+ // #enddocregion submitted
+
+ // #enddocregion final
+ // TODO: Remove this when we're done
+ get diagnostic() { return JSON.stringify(this.model); }
+ // #enddocregion v1
+
+ // #docregion final, new-hero
+ newHero() {
+ this.model = new Hero(42, '', '');
+ }
+ // #enddocregion final, new-hero
+
+ skyDog(): Hero {
+ // #docregion SkyDog
+ let myHero = new Hero(42, 'SkyDog',
+ 'Fetch any object at any distance',
+ 'Leslie Rollover');
+ console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog"
+ // #enddocregion SkyDog
+ return myHero;
+ }
+
+ //////// NOT SHOWN IN DOCS ////////
+
+ // Reveal in html:
+ // Name via form.controls = {{showFormControls(heroForm)}}
+ showFormControls(form: any) {
+ return form && form.controls['name'] &&
+ // #docregion form-controls
+ form.controls['name'].value; // Dr. IQ
+ // #enddocregion form-controls
+ }
+
+ /////////////////////////////
+
+ // #docregion v1, final
+}
diff --git a/public/docs/_examples/forms/ts/src/app/hero.ts b/public/docs/_examples/forms/ts/src/app/hero.ts
new file mode 100644
index 0000000000..c128626452
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/app/hero.ts
@@ -0,0 +1,11 @@
+// #docregion
+export class Hero {
+
+ constructor(
+ public id: number,
+ public name: string,
+ public power: string,
+ public alterEgo?: string
+ ) { }
+
+}
diff --git a/public/docs/_examples/forms/ts/src/forms.css b/public/docs/_examples/forms/ts/src/forms.css
new file mode 100644
index 0000000000..13ffbe1203
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/forms.css
@@ -0,0 +1,9 @@
+/* #docregion */
+.ng-valid[required], .ng-valid.required {
+ border-left: 5px solid #42A948; /* green */
+}
+
+.ng-invalid:not(form) {
+ border-left: 5px solid #a94442; /* red */
+}
+/* #enddocregion */
diff --git a/public/docs/_examples/forms/ts/src/index.html b/public/docs/_examples/forms/ts/src/index.html
new file mode 100644
index 0000000000..032888ca3e
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/index.html
@@ -0,0 +1,35 @@
+
+
+
+
+ Hero Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/forms/ts/src/main.ts b/public/docs/_examples/forms/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/forms/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/gettingstarted/js/main.js b/public/docs/_examples/gettingstarted/js/main.js
deleted file mode 100644
index b752be57b4..0000000000
--- a/public/docs/_examples/gettingstarted/js/main.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// #docregion
-function AppComponent() {}
-
-AppComponent.annotations = [
- new angular.ComponentAnnotation({
- selector: 'my-app'
- }),
- new angular.ViewAnnotation({
- template: 'My first Angular 2 App '
- })
-];
-
-// #docregion bootstrap
-document.addEventListener('DOMContentLoaded', function() {
- angular.bootstrap(AppComponent);
-});
-// #enddocregion
\ No newline at end of file
diff --git a/public/docs/_examples/gettingstarted/protractor-spec.js b/public/docs/_examples/gettingstarted/protractor-spec.js
deleted file mode 100644
index 2a7633774e..0000000000
--- a/public/docs/_examples/gettingstarted/protractor-spec.js
+++ /dev/null
@@ -1,20 +0,0 @@
-// protractor-spec.js
-describe('Protractor quick start test', function() {
-
- // #docregion javascript
- it('should display Alice with JavaScript', function() {
- browser.get('gettingstarted/js/index.html');
- });
- // #enddocregion
-
- // #docregion typescript
- it('should display Alice with TypeScrip', function() {
- browser.get('gettingstarted/ts/index.html');
- });
- // #enddocregion
-
- afterEach(function() {
- expect(element(by.id('output')).getText()).toEqual('My first Angular 2 App');
- });
-});
-
diff --git a/public/docs/_examples/gettingstarted/ts/index.html b/public/docs/_examples/gettingstarted/ts/index.html
deleted file mode 100644
index b44d34d016..0000000000
--- a/public/docs/_examples/gettingstarted/ts/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/public/docs/_examples/gettingstarted/ts/main.js.map b/public/docs/_examples/gettingstarted/ts/main.js.map
deleted file mode 100644
index a03477dbb1..0000000000
--- a/public/docs/_examples/gettingstarted/ts/main.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"main.js","sourceRoot":"","sources":["main.ts"],"names":["AppComponent","AppComponent.constructor"],"mappings":";;;;;;;;;;;AAEA,AAFA,UAAU;AACV,iBAAiB;AACjB,yBAAyC,mBAAmB,CAAC,CAAA;AAG7D,AAFA,aAAa;;IAEbA;IAOAC,CAACA;IAPDD;QAACA,oBAASA,CAACA;YACTA,QAAQA,EAAEA,QAAQA;SACnBA,CAACA;QACDA,eAAIA,CAACA;YACJA,QAAQA,EAAEA,6CAA6CA;SACxDA,CAACA;;qBAEDA;IAADA,mBAACA;AAADA,CAACA,AAPD,IAOC;AAGD,AADA,oBAAoB;AACpB,oBAAS,CAAC,YAAY,CAAC,CAAC;AACxB,aAAa"}
\ No newline at end of file
diff --git a/public/docs/_examples/gettingstarted/ts/main.ts b/public/docs/_examples/gettingstarted/ts/main.ts
deleted file mode 100644
index 7cf1b4659a..0000000000
--- a/public/docs/_examples/gettingstarted/ts/main.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-// #docregion
-// #docregion import
-import {Component, View, bootstrap} from 'angular2/angular2';
-// #enddocregion
-
-@Component({
- selector: 'my-app'
-})
-@View({
- template: 'My first Angular 2 App '
-})
-class AppComponent {
-}
-// #docregion bootstrap
-bootstrap(AppComponent);
-// #enddocregion
diff --git a/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.ts b/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.ts
new file mode 100644
index 0000000000..4630453f78
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/e2e-spec.ts
@@ -0,0 +1,103 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, by, element } from 'protractor';
+
+describe('Hierarchical dependency injection', () => {
+
+ beforeAll(() => {
+ browser.get('');
+ });
+
+ describe('Heroes Scenario', () => {
+ let page = {
+ heroName: '',
+ income: '',
+
+ // queries
+ heroEl: element.all(by.css('heroes-list li')).get(0), // first hero
+ heroCardEl: element(by.css('heroes-list hero-tax-return')), // first hero tax-return
+ taxReturnNameEl: element.all(by.css('heroes-list hero-tax-return #name')).get(0),
+ incomeInputEl: element.all(by.css('heroes-list hero-tax-return input')).get(0),
+ cancelButtonEl: element(by.cssContainingText('heroes-list hero-tax-return button', 'Cancel')),
+ closeButtonEl: element(by.cssContainingText('heroes-list hero-tax-return button', 'Close')),
+ saveButtonEl: element(by.cssContainingText('heroes-list hero-tax-return button', 'Save'))
+ };
+
+ it('should list multiple heroes', () => {
+ expect(element.all(by.css('heroes-list li')).count()).toBeGreaterThan(1);
+ });
+
+ it('should show no hero tax-returns at the start', () => {
+ expect(element.all(by.css('heroes-list li hero-tax-return')).count()).toBe(0);
+ });
+
+ it('should open first hero in hero-tax-return view after click', () => {
+ page.heroEl.getText()
+ .then(val => {
+ page.heroName = val;
+ })
+ .then(() => page.heroEl.click())
+ .then(() => {
+ expect(page.heroCardEl.isDisplayed()).toBe(true);
+ });
+ });
+
+ it('hero tax-return should have first hero\'s name', () => {
+ // Not `page.tax-returnNameInputEl.getAttribute('value')` although later that is essential
+ expect(page.taxReturnNameEl.getText()).toEqual(page.heroName);
+ });
+
+ it('should be able to cancel change', () => {
+ page.incomeInputEl.clear()
+ .then(() => page.incomeInputEl.sendKeys('777'))
+ .then(() => {
+ expect(page.incomeInputEl.getAttribute('value')).toBe('777', 'income should be 777');
+ return page.cancelButtonEl.click();
+ })
+ .then(() => {
+ expect(page.incomeInputEl.getAttribute('value')).not.toBe('777', 'income should not be 777');
+ });
+ });
+
+ it('should be able to save change', () => {
+ page.incomeInputEl.clear()
+ .then(() => page.incomeInputEl.sendKeys('999'))
+ .then(() => {
+ expect(page.incomeInputEl.getAttribute('value')).toBe('999', 'income should be 999');
+ return page.saveButtonEl.click();
+ })
+ .then(() => {
+ expect(page.incomeInputEl.getAttribute('value')).toBe('999', 'income should still be 999');
+ });
+ });
+
+ it('should be able to close tax-return', () => {
+ page.saveButtonEl.click()
+ .then(() => {
+ expect(element.all(by.css('heroes-list li hero-tax-return')).count()).toBe(0);
+ });
+ });
+
+ });
+
+ describe('Villains Scenario', () => {
+ it('should list multiple villains', () => {
+ expect(element.all(by.css('villains-list li')).count()).toBeGreaterThan(1);
+ });
+ });
+
+ describe('Cars Scenario', () => {
+
+ it('A-component should use expected services', () => {
+ expect(element(by.css('a-car')).getText()).toContain('C1-E1-T1');
+ });
+
+ it('B-component should use expected services', () => {
+ expect(element(by.css('b-car')).getText()).toContain('C2-E2-T1');
+ });
+
+ it('C-component should use expected services', () => {
+ expect(element(by.css('c-car')).getText()).toContain('C3-E2-T1');
+ });
+ });
+});
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/example-config.json b/public/docs/_examples/hierarchical-dependency-injection/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/plnkr.json b/public/docs/_examples/hierarchical-dependency-injection/ts/plnkr.json
new file mode 100644
index 0000000000..ca92b93b06
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Hierachical Dependency Injection",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js"
+ ],
+ "tags": ["dependency", "injection"]
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/app.component.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..34b5dd0a5a
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/app.component.ts
@@ -0,0 +1,21 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+ Heroes
+ Villains
+ Cars
+
+ Hierarchical Dependency Injection
+
+
+
+
+ `
+})
+export class AppComponent {
+ showCars = true;
+ showHeroes = true;
+ showVillains = true;
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/app.module.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..6ea18655af
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/app.module.ts
@@ -0,0 +1,33 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { HeroTaxReturnComponent } from './hero-tax-return.component';
+import { HeroesListComponent } from './heroes-list.component';
+import { HeroesService } from './heroes.service';
+import { VillainsListComponent } from './villains-list.component';
+
+import { carComponents, carServices } from './car.components';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ providers: [
+ carServices,
+ HeroesService
+ ],
+ declarations: [
+ AppComponent,
+ carComponents,
+ HeroesListComponent,
+ HeroTaxReturnComponent,
+ VillainsListComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/car.components.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/car.components.ts
new file mode 100644
index 0000000000..5b4df19696
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/car.components.ts
@@ -0,0 +1,74 @@
+import { Component } from '@angular/core';
+
+import {
+ CarService, CarService2, CarService3,
+ EngineService, EngineService2, TiresService
+} from './car.services';
+
+////////// CCarComponent ////////////
+@Component({
+ selector: 'c-car',
+ template: `C: {{description}}
`,
+ providers: [
+ { provide: CarService, useClass: CarService3 }
+ ]
+})
+export class CCarComponent {
+ description: string;
+ constructor(carService: CarService) {
+ this.description = `${carService.getCar().description} (${carService.name})`;
+ }
+}
+
+////////// BCarComponent ////////////
+@Component({
+ selector: 'b-car',
+ template: `
+ B: {{description}}
+
+ `,
+ providers: [
+ { provide: CarService, useClass: CarService2 },
+ { provide: EngineService, useClass: EngineService2 }
+ ]
+})
+export class BCarComponent {
+ description: string;
+ constructor(carService: CarService) {
+ this.description = `${carService.getCar().description} (${carService.name})`;
+ }
+}
+
+////////// ACarComponent ////////////
+@Component({
+ selector: 'a-car',
+ template: `
+ A: {{description}}
+ `
+})
+export class ACarComponent {
+ description: string;
+ constructor(carService: CarService) {
+ this.description = `${carService.getCar().description} (${carService.name})`;
+ }
+}
+////////// CarsComponent ////////////
+@Component({
+ selector: 'my-cars',
+ template: `
+ Cars
+ `
+})
+export class CarsComponent { }
+
+////////////////
+
+export const carComponents = [
+ CarsComponent,
+ ACarComponent, BCarComponent, CCarComponent
+];
+
+// generic car-related services
+export const carServices = [
+ CarService, EngineService, TiresService
+];
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/car.services.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/car.services.ts
new file mode 100644
index 0000000000..03c79270b0
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/car.services.ts
@@ -0,0 +1,95 @@
+import { Injectable } from '@angular/core';
+
+/// Model ///
+export class Car {
+ name = 'Avocado Motors';
+ constructor(public engine: Engine, public tires: Tires) { }
+
+ get description() {
+ return `${this.name} car with ` +
+ `${this.engine.cylinders} cylinders and ${this.tires.make} tires.`;
+ }
+}
+
+export class Engine {
+ cylinders = 4;
+}
+
+export class Tires {
+ make = 'Flintstone';
+ model = 'Square';
+}
+
+//// Engine services ///
+@Injectable()
+export class EngineService {
+ id = 'E1';
+ getEngine() { return new Engine(); }
+}
+
+@Injectable()
+export class EngineService2 {
+ id = 'E2';
+ getEngine() {
+ const eng = new Engine();
+ eng.cylinders = 8;
+ return eng;
+ }
+}
+
+//// Tire services ///
+@Injectable()
+export class TiresService {
+ id = 'T1';
+ getTires() { return new Tires(); }
+}
+
+/// Car Services ///
+@Injectable()
+export class CarService {
+ id = 'C1';
+ constructor(
+ protected engineService: EngineService,
+ protected tiresService: TiresService) { }
+
+ getCar() {
+ return new Car(
+ this.engineService.getEngine(),
+ this.tiresService.getTires());
+ }
+
+ get name() {
+ return `${this.id}-${this.engineService.id}-${this.tiresService.id}`;
+ }
+}
+
+@Injectable()
+export class CarService2 extends CarService {
+ id = 'C2';
+ constructor(
+ protected engineService: EngineService,
+ protected tiresService: TiresService) {
+ super(engineService, tiresService);
+ }
+ getCar() {
+ const car = super.getCar();
+ car.name = 'BamBam Motors, BroVan 2000';
+ return car;
+ }
+}
+
+@Injectable()
+export class CarService3 extends CarService2 {
+ id = 'C3';
+ constructor(
+ protected engineService: EngineService,
+ protected tiresService: TiresService) {
+ super(engineService, tiresService);
+ }
+ getCar() {
+ const car = super.getCar();
+ car.name = 'Chizzamm Motors, Calico UltraMax Supreme';
+ return car;
+ }
+}
+
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.css b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.css
new file mode 100644
index 0000000000..1d29a1d168
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.css
@@ -0,0 +1,22 @@
+.tax-return { border: thin dashed green; margin: 1em; padding: 1em; width: 18em; position: relative }
+#name { font-weight: bold;}
+#tid { float: right; }
+input { font-size: 100%; padding-left: 2px; width: 6em; }
+input.num { text-align: right; padding-left: 0; padding-right: 4px; width: 4em;}
+fieldset { border: 0 none;}
+
+.msg {
+ color: white;
+ font-size: 150%;
+ position: absolute;
+ /*opacity: 0.3;*/
+ left: 2px;
+ top: 3em;
+ width: 98%;
+ background-color: green;
+ text-align: center;
+}
+.msg.canceled {
+ color: white;
+ background-color: red;
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.html b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.html
new file mode 100644
index 0000000000..ebf2dcbaaa
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.html
@@ -0,0 +1,20 @@
+
+
{{message}}
+
+ {{taxReturn.name}}
+ TID: {{taxReturn.tid}}
+
+
+
+ Income:
+
+
+
+ Tax: {{taxReturn.tax}}
+
+
+ Save
+ Cancel
+ Close
+
+
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts
new file mode 100644
index 0000000000..ad8e0153d9
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts
@@ -0,0 +1,44 @@
+// #docregion
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+import { HeroTaxReturn } from './hero';
+import { HeroTaxReturnService } from './hero-tax-return.service';
+
+@Component({
+ selector: 'hero-tax-return',
+ templateUrl: './hero-tax-return.component.html',
+ styleUrls: [ './hero-tax-return.component.css' ],
+ // #docregion providers
+ providers: [ HeroTaxReturnService ]
+ // #enddocregion providers
+})
+export class HeroTaxReturnComponent {
+ message = '';
+ @Output() close = new EventEmitter();
+
+ get taxReturn(): HeroTaxReturn {
+ return this.heroTaxReturnService.taxReturn;
+ }
+ @Input()
+ set taxReturn (htr: HeroTaxReturn) {
+ this.heroTaxReturnService.taxReturn = htr;
+ }
+
+ constructor(private heroTaxReturnService: HeroTaxReturnService ) { }
+
+ onCanceled() {
+ this.flashMessage('Canceled');
+ this.heroTaxReturnService.restoreTaxReturn();
+ };
+
+ onClose() { this.close.emit(); };
+
+ onSaved() {
+ this.flashMessage('Saved');
+ this.heroTaxReturnService.saveTaxReturn();
+ }
+
+ flashMessage(msg: string) {
+ this.message = msg;
+ setTimeout(() => this.message = '', 500);
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts
new file mode 100644
index 0000000000..d6ff0f7fff
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts
@@ -0,0 +1,30 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { HeroTaxReturn } from './hero';
+import { HeroesService } from './heroes.service';
+
+@Injectable()
+export class HeroTaxReturnService {
+ private currentTaxReturn: HeroTaxReturn;
+ private originalTaxReturn: HeroTaxReturn;
+
+ constructor(private heroService: HeroesService) { }
+
+ set taxReturn (htr: HeroTaxReturn) {
+ this.originalTaxReturn = htr;
+ this.currentTaxReturn = htr.clone();
+ }
+
+ get taxReturn (): HeroTaxReturn {
+ return this.currentTaxReturn;
+ }
+
+ restoreTaxReturn() {
+ this.taxReturn = this.originalTaxReturn;
+ }
+
+ saveTaxReturn() {
+ this.taxReturn = this.currentTaxReturn;
+ this.heroService.saveTaxReturn(this.currentTaxReturn).subscribe();
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero.ts
new file mode 100644
index 0000000000..4ad6ccd8eb
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/hero.ts
@@ -0,0 +1,31 @@
+// #docregion
+
+export class Hero {
+ id: number;
+ name: string;
+ tid: string; // tax id
+}
+
+//// HeroTaxReturn ////
+let nextId = 100;
+
+export class HeroTaxReturn {
+ constructor(
+ public id = nextId++,
+ public hero: Hero,
+ public income = 0 ) {
+ if (id === 0) { id = nextId++; }
+ }
+
+ get name() { return this.hero.name; }
+ get tax() { return this.income ? .10 * this.income : 0; }
+ get tid() { return this.hero.tid; }
+
+ toString() {
+ return `${this.hero.name}`;
+ }
+
+ clone() {
+ return new HeroTaxReturn(this.id, this.hero, this.income);
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/heroes-list.component.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/heroes-list.component.ts
new file mode 100644
index 0000000000..36cb5ec1c3
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/heroes-list.component.ts
@@ -0,0 +1,48 @@
+// #docregion
+import { Component } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { Hero, HeroTaxReturn } from './hero';
+import { HeroesService } from './heroes.service';
+
+@Component({
+ selector: 'heroes-list',
+ template: `
+
+
Hero Tax Returns
+
+
+
+
+ `,
+ styles: [ 'li {cursor: pointer;}' ]
+})
+export class HeroesListComponent {
+ heroes: Observable;
+ selectedTaxReturns: HeroTaxReturn[] = [];
+
+ constructor(private heroesService: HeroesService) {
+ this.heroes = heroesService.getHeroes();
+ }
+
+ showTaxReturn(hero: Hero) {
+ this.heroesService.getTaxReturn(hero)
+ .subscribe(htr => {
+ // show if not currently shown
+ if (!this.selectedTaxReturns.find(tr => tr.id === htr.id)) {
+ this.selectedTaxReturns.push(htr);
+ }
+ });
+ }
+
+ closeTaxReturn(ix: number) {
+ this.selectedTaxReturns.splice(ix, 1);
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/heroes.service.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/heroes.service.ts
new file mode 100644
index 0000000000..85b33c89a0
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/heroes.service.ts
@@ -0,0 +1,47 @@
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import { Observer } from 'rxjs/Observer';
+
+import { Hero, HeroTaxReturn } from './hero';
+
+@Injectable()
+export class HeroesService {
+ heroes: Hero[] = [
+ { id: 1, name: 'RubberMan', tid: '082-27-5678'},
+ { id: 2, name: 'Tornado', tid: '099-42-4321'}
+ ];
+
+ heroTaxReturns: HeroTaxReturn[] = [
+ new HeroTaxReturn(10, this.heroes[0], 35000),
+ new HeroTaxReturn(20, this.heroes[1], 1250000)
+ ];
+
+ getHeroes(): Observable {
+ return new Observable((observer: Observer) => {
+ observer.next(this.heroes);
+ observer.complete();
+ });
+ }
+
+ getTaxReturn(hero: Hero): Observable {
+ return new Observable((observer: Observer) => {
+ const htr = this.heroTaxReturns.find(t => t.hero.id === hero.id);
+ observer.next(htr || new HeroTaxReturn(0, hero));
+ observer.complete();
+ });
+ }
+
+ saveTaxReturn(heroTaxReturn: HeroTaxReturn): Observable {
+ return new Observable((observer: Observer) => {
+ const htr = this.heroTaxReturns.find(t => t.id === heroTaxReturn.id);
+ if (htr) {
+ heroTaxReturn = Object.assign(htr, heroTaxReturn); // demo: mutate
+ } else {
+ this.heroTaxReturns.push(heroTaxReturn);
+ }
+ observer.next(heroTaxReturn);
+ observer.complete();
+ });
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains-list.component.html b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains-list.component.html
new file mode 100644
index 0000000000..75e5ba3237
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains-list.component.html
@@ -0,0 +1,6 @@
+
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains-list.component.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains-list.component.ts
new file mode 100644
index 0000000000..5226d4f2a9
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains-list.component.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { Villain, VillainsService } from './villains.service';
+
+// #docregion metadata
+@Component({
+ selector: 'villains-list',
+ templateUrl: './villains-list.component.html',
+ providers: [ VillainsService ]
+})
+// #enddocregion metadata
+export class VillainsListComponent {
+ villains: Observable;
+
+ constructor(private villainsService: VillainsService) {
+ this.villains = villainsService.getVillains();
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains.service.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains.service.ts
new file mode 100644
index 0000000000..3d480c20af
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/app/villains.service.ts
@@ -0,0 +1,17 @@
+import { Injectable } from '@angular/core';
+
+import { of } from 'rxjs/observable/of';
+
+export interface Villain { id: number; name: string; }
+
+@Injectable()
+export class VillainsService {
+ villains: Villain[] = [
+ { id: 1, name: 'Dr. Evil'},
+ { id: 2, name: 'Moriarty'}
+ ];
+
+ getVillains() {
+ return of(this.villains);
+ }
+}
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/index.html b/public/docs/_examples/hierarchical-dependency-injection/ts/src/index.html
new file mode 100644
index 0000000000..0ea32679e6
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/index.html
@@ -0,0 +1,26 @@
+
+
+
+ Hierarchical Injectors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loading...
+
+
+
diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/src/main.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/hierarchical-dependency-injection/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/homepage-hello-world/e2e-spec.ts b/public/docs/_examples/homepage-hello-world/e2e-spec.ts
new file mode 100644
index 0000000000..c4c6464937
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/e2e-spec.ts
@@ -0,0 +1,30 @@
+'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 + '!');
+ });
+ });
+});
diff --git a/public/docs/_examples/homepage-hello-world/ts/example-config.json b/public/docs/_examples/homepage-hello-world/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/homepage-hello-world/ts/plnkr.json b/public/docs/_examples/homepage-hello-world/ts/plnkr.json
new file mode 100644
index 0000000000..bfe6aecc8b
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Hello World",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ]
+}
diff --git a/public/docs/_examples/homepage-hello-world/ts/src/app/app.module.ts b/public/docs/_examples/homepage-hello-world/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..55a2ef3693
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/src/app/app.module.ts
@@ -0,0 +1,16 @@
+// #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 { }
diff --git a/public/docs/_examples/homepage-hello-world/ts/src/app/hello_world.html b/public/docs/_examples/homepage-hello-world/ts/src/app/hello_world.html
new file mode 100644
index 0000000000..13af8fb34e
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/src/app/hello_world.html
@@ -0,0 +1,7 @@
+
+Name:
+
+
+
+
+Hello {{yourName}}!
\ No newline at end of file
diff --git a/public/docs/_examples/homepage-hello-world/ts/src/app/hello_world.ts b/public/docs/_examples/homepage-hello-world/ts/src/app/hello_world.ts
new file mode 100644
index 0000000000..145d22e1e9
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/src/app/hello_world.ts
@@ -0,0 +1,15 @@
+// #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 = '';
+}
diff --git a/public/docs/_examples/homepage-hello-world/ts/src/index.1.html b/public/docs/_examples/homepage-hello-world/ts/src/index.1.html
new file mode 100644
index 0000000000..37c1bf787c
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/src/index.1.html
@@ -0,0 +1,32 @@
+
+
+
+
+ Angular Hello World
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/homepage-hello-world/ts/src/index.html b/public/docs/_examples/homepage-hello-world/ts/src/index.html
new file mode 100644
index 0000000000..9dab660d10
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/src/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+ Angular Hello World
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/homepage-hello-world/ts/src/main.ts b/public/docs/_examples/homepage-hello-world/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/homepage-hello-world/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/homepage-tabs/e2e-spec.ts b/public/docs/_examples/homepage-tabs/e2e-spec.ts
new file mode 100644
index 0000000000..2131d75906
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/e2e-spec.ts
@@ -0,0 +1,17 @@
+'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);
+ });
+
+});
diff --git a/public/docs/_examples/homepage-tabs/ts/example-config.json b/public/docs/_examples/homepage-tabs/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/homepage-tabs/ts/plnkr.json b/public/docs/_examples/homepage-tabs/ts/plnkr.json
new file mode 100644
index 0000000000..c1a33dc8f0
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Tabs",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ]
+}
diff --git a/public/docs/_examples/homepage-tabs/ts/src/app/app.module.ts b/public/docs/_examples/homepage-tabs/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..bd2193755c
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/src/app/app.module.ts
@@ -0,0 +1,17 @@
+// #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 { }
diff --git a/public/docs/_examples/homepage-tabs/ts/src/app/di_demo.ts b/public/docs/_examples/homepage-tabs/ts/src/app/di_demo.ts
new file mode 100644
index 0000000000..be72ae5c79
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/src/app/di_demo.ts
@@ -0,0 +1,45 @@
+// #docregion
+import { Component } from '@angular/core';
+
+class Detail {
+ title: string;
+ text: string;
+}
+
+@Component({
+ selector: 'di-demo',
+ template: `
+ Tabs Demo
+
+
+ You have {{details.length}} details.
+
+
+ {{detail.text}}
+ Remove
+
+
+ Next last ID is {{id}}.
+
+
+
+ Add Detail
+ `
+})
+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);
+ }
+}
+
diff --git a/public/docs/_examples/homepage-tabs/ts/src/app/ui_tabs.ts b/public/docs/_examples/homepage-tabs/ts/src/app/ui_tabs.ts
new file mode 100644
index 0000000000..5e2f47b140
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/src/app/ui_tabs.ts
@@ -0,0 +1,51 @@
+// #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) { }
+
+ @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: `
+
+
+ `,
+ styles: ['a { cursor: pointer; cursor: hand; }']
+})
+export class UiTabsComponent {
+ @ContentChildren(UiPaneDirective) panes: QueryList;
+
+ select(pane: UiPaneDirective) {
+ this.panes.toArray().forEach((p: UiPaneDirective) => p.active = p === pane);
+ }
+}
+
diff --git a/public/docs/_examples/homepage-tabs/ts/src/index.1.html b/public/docs/_examples/homepage-tabs/ts/src/index.1.html
new file mode 100644
index 0000000000..a25071b484
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/src/index.1.html
@@ -0,0 +1,33 @@
+
+
+
+
+ Angular Tabs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/homepage-tabs/ts/src/index.html b/public/docs/_examples/homepage-tabs/ts/src/index.html
new file mode 100644
index 0000000000..d38b36e033
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/src/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+ Angular Tabs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/homepage-tabs/ts/src/main.ts b/public/docs/_examples/homepage-tabs/ts/src/main.ts
new file mode 100644
index 0000000000..f332d1d245
--- /dev/null
+++ b/public/docs/_examples/homepage-tabs/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/homepage-todo/e2e-spec.ts b/public/docs/_examples/homepage-todo/e2e-spec.ts
new file mode 100644
index 0000000000..fb74e4e70f
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/e2e-spec.ts
@@ -0,0 +1,17 @@
+'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);
+ });
+
+});
diff --git a/public/docs/_examples/homepage-todo/ts/example-config.json b/public/docs/_examples/homepage-todo/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/homepage-todo/ts/plnkr.json b/public/docs/_examples/homepage-todo/ts/plnkr.json
new file mode 100644
index 0000000000..8e69701221
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Todo",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ]
+}
diff --git a/public/docs/_examples/homepage-todo/ts/src/app/app.module.ts b/public/docs/_examples/homepage-todo/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..a77bb640ab
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/app/app.module.ts
@@ -0,0 +1,22 @@
+// #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 { }
diff --git a/public/docs/_examples/homepage-todo/ts/src/app/todo.ts b/public/docs/_examples/homepage-todo/ts/src/app/todo.ts
new file mode 100644
index 0000000000..35faec9705
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/app/todo.ts
@@ -0,0 +1,6 @@
+// #docregion
+// Declare an interaface for type safety
+export interface Todo {
+ text: string;
+ done: boolean;
+}
diff --git a/public/docs/_examples/homepage-todo/ts/src/app/todo_app.ts b/public/docs/_examples/homepage-todo/ts/src/app/todo_app.ts
new file mode 100644
index 0000000000..e56bad95b6
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/app/todo_app.ts
@@ -0,0 +1,38 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { Todo } from './todo';
+
+@Component({
+ selector: 'todo-app',
+ template: `
+ Todo
+ {{remaining}} of {{todos.length}} remaining
+ [ archive ]
+
+
+ `,
+ 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);
+ }
+}
diff --git a/public/docs/_examples/homepage-todo/ts/src/app/todo_form.ts b/public/docs/_examples/homepage-todo/ts/src/app/todo_form.ts
new file mode 100644
index 0000000000..38e8d991a7
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/app/todo_form.ts
@@ -0,0 +1,25 @@
+// #docregion
+import { Component, Output, EventEmitter } from '@angular/core';
+import { Todo } from './todo';
+
+@Component({
+ selector: 'todo-form',
+ template: `
+ `
+})
+export class TodoFormComponent {
+ @Output() newTask = new EventEmitter();
+ task: string = '';
+
+ addTodo() {
+ if (this.task) {
+ this.newTask.emit({text: this.task, done: false});
+ }
+ this.task = '';
+ }
+}
+
diff --git a/public/docs/_examples/homepage-todo/ts/src/app/todo_list.ts b/public/docs/_examples/homepage-todo/ts/src/app/todo_list.ts
new file mode 100644
index 0000000000..b6495f37c2
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/app/todo_list.ts
@@ -0,0 +1,24 @@
+// #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: `
+ `
+})
+export class TodoListComponent {
+ @Input() todos: Todo[];
+}
diff --git a/public/docs/_examples/homepage-todo/ts/src/index.1.html b/public/docs/_examples/homepage-todo/ts/src/index.1.html
new file mode 100644
index 0000000000..4e8d034fd3
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/index.1.html
@@ -0,0 +1,33 @@
+
+
+
+
+ Angular Todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/homepage-todo/ts/src/index.html b/public/docs/_examples/homepage-todo/ts/src/index.html
new file mode 100644
index 0000000000..e7a656a106
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/index.html
@@ -0,0 +1,29 @@
+
+
+
+
+ Angular Todos
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/homepage-todo/ts/src/main.ts b/public/docs/_examples/homepage-todo/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/homepage-todo/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/jsconfig.json b/public/docs/_examples/jsconfig.json
new file mode 100644
index 0000000000..82750484d1
--- /dev/null
+++ b/public/docs/_examples/jsconfig.json
@@ -0,0 +1,6 @@
+{
+ "compilerOptions": {
+ "target": "ES5",
+ "module": "commonjs"
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/e2e-spec.ts b/public/docs/_examples/lifecycle-hooks/e2e-spec.ts
new file mode 100644
index 0000000000..8e9acb1c76
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/e2e-spec.ts
@@ -0,0 +1,178 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Lifecycle hooks', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('should open correctly', function () {
+ expect(element.all(by.css('h2')).get(0).getText()).toEqual('Peek-A-Boo');
+ });
+
+ it('should support peek-a-boo', function () {
+ let pabComp = element(by.css('peek-a-boo-parent peek-a-boo'));
+ expect(pabComp.isPresent()).toBe(false, 'should not be able to find the "peek-a-boo" component');
+ let pabButton = element.all(by.css('peek-a-boo-parent button')).get(0);
+ let updateHeroButton = element.all(by.css('peek-a-boo-parent button')).get(1);
+ expect(pabButton.getText()).toContain('Create Peek');
+ pabButton.click().then(function () {
+ expect(pabButton.getText()).toContain('Destroy Peek');
+ expect(pabComp.isDisplayed()).toBe(true, 'should be able to see the "peek-a-boo" component');
+ expect(pabComp.getText()).toContain('Windstorm');
+ expect(pabComp.getText()).not.toContain('Windstorm!');
+ expect(updateHeroButton.isPresent()).toBe(true, 'should be able to see the update hero button');
+ return updateHeroButton.click();
+ }).then(function () {
+ expect(pabComp.getText()).toContain('Windstorm!');
+ return pabButton.click();
+ }).then(function () {
+ expect(pabComp.isPresent()).toBe(false, 'should no longer be able to find the "peek-a-boo" component');
+ });
+ });
+
+ it('should support OnChanges hook', function () {
+ let onChangesViewEle = element.all(by.css('on-changes div')).get(0);
+ let inputEles = element.all(by.css('on-changes-parent input'));
+ let heroNameInputEle = inputEles.get(1);
+ let powerInputEle = inputEles.get(0);
+ let titleEle = onChangesViewEle.element(by.css('p'));
+ let changeLogEles = onChangesViewEle.all(by.css('div'));
+
+ expect(titleEle.getText()).toContain('Windstorm can sing');
+ expect(changeLogEles.count()).toEqual(2, 'should start with 2 messages');
+ heroNameInputEle.sendKeys('-foo-');
+ expect(titleEle.getText()).toContain('Windstorm-foo- can sing');
+ expect(changeLogEles.count()).toEqual(2, 'should still have 2 messages');
+ powerInputEle.sendKeys('-bar-');
+ expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-');
+ // 7 == 2 previously + length of '-bar-'
+ expect(changeLogEles.count()).toEqual(7, 'should have 7 messages now');
+ });
+
+ it('should support DoCheck hook', function () {
+ let doCheckViewEle = element.all(by.css('do-check div')).get(0);
+ let inputEles = element.all(by.css('do-check-parent input'));
+ let heroNameInputEle = inputEles.get(1);
+ let powerInputEle = inputEles.get(0);
+ let titleEle = doCheckViewEle.element(by.css('p'));
+ let changeLogEles = doCheckViewEle.all(by.css('div'));
+ let logCount: number;
+
+ expect(titleEle.getText()).toContain('Windstorm can sing');
+ changeLogEles.count().then(function(count: number) {
+ // 3 messages to start
+ expect(count).toEqual(3, 'should start with 3 messages');
+ logCount = count;
+ return heroNameInputEle.sendKeys('-foo-');
+ }).then(function () {
+ expect(titleEle.getText()).toContain('Windstorm-foo- can sing');
+ return changeLogEles.count();
+ }).then(function(count: number) {
+ // one more for each keystroke
+ expect(count).toEqual(logCount + 5, 'should add 5 more messages');
+ logCount = count;
+ return powerInputEle.sendKeys('-bar-');
+ }).then(function () {
+ expect(titleEle.getText()).toContain('Windstorm-foo- can sing-bar-');
+ expect(changeLogEles.count()).toEqual(logCount + 6, 'should add 6 more messages');
+ });
+ });
+
+ it('should support AfterView hooks', function () {
+ let parentEle = element(by.tagName('after-view-parent'));
+ let buttonEle = parentEle.element(by.tagName('button')); // Reset
+ let commentEle = parentEle.element(by.className('comment'));
+ let logEles = parentEle.all(by.css('h4 ~ div'));
+ let childViewInputEle = parentEle.element(by.css('my-child-view input'));
+ let logCount: number;
+
+ expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
+ expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM');
+
+ logEles.count().then(function(count: number) {
+ logCount = count;
+ return childViewInputEle.sendKeys('-test-');
+ }).then(function() {
+ expect(childViewInputEle.getAttribute('value')).toContain('-test-');
+ expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars');
+ expect(commentEle.getText()).toContain('long name');
+ return logEles.count();
+ }).then(function(count: number) {
+ expect(logCount + 7).toEqual(count, '7 additional log messages should have been added');
+ logCount = count;
+ return buttonEle.click();
+ }).then(function() {
+ expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset');
+ });
+ });
+
+
+ it('should support AfterContent hooks', function () {
+ let parentEle = element(by.tagName('after-content-parent'));
+ let buttonEle = parentEle.element(by.tagName('button')); // Reset
+ let commentEle = parentEle.element(by.className('comment'));
+ let logEles = parentEle.all(by.css('h4 ~ div'));
+ let childViewInputEle = parentEle.element(by.css('my-child input'));
+ let logCount: number;
+
+ expect(childViewInputEle.getAttribute('value')).toContain('Magneta');
+ expect(commentEle.isPresent()).toBe(false, 'comment should not be in DOM');
+
+ logEles.count().then(function(count: number) {
+ logCount = count;
+ return childViewInputEle.sendKeys('-test-');
+ }).then(function() {
+ expect(childViewInputEle.getAttribute('value')).toContain('-test-');
+ expect(commentEle.isPresent()).toBe(true, 'should have comment because >10 chars');
+ expect(commentEle.getText()).toContain('long name');
+ return logEles.count();
+ }).then(function(count: number) {
+ expect(logCount + 5).toEqual(count, '5 additional log messages should have been added');
+ logCount = count;
+ return buttonEle.click();
+ }).then(function() {
+ expect(logEles.count()).toBeLessThan(logCount, 'log should shrink after reset');
+ });
+ });
+
+ it('should support spy\'s OnInit & OnDestroy hooks', function () {
+ let inputEle = element(by.css('spy-parent input'));
+ let addHeroButtonEle = element(by.cssContainingText('spy-parent button', 'Add Hero'));
+ let resetHeroesButtonEle = element(by.cssContainingText('spy-parent button', 'Reset Heroes'));
+ let heroEles = element.all(by.css('spy-parent div[mySpy'));
+ let logEles = element.all(by.css('spy-parent h4 ~ div'));
+ expect(heroEles.count()).toBe(2, 'should have two heroes displayed');
+ expect(logEles.count()).toBe(2, 'should have two log entries');
+ inputEle.sendKeys('-test-').then(function() {
+ return addHeroButtonEle.click();
+ }).then(function() {
+ expect(heroEles.count()).toBe(3, 'should have added one hero');
+ expect(heroEles.get(2).getText()).toContain('-test-');
+ expect(logEles.count()).toBe(3, 'should now have 3 log entries');
+ return resetHeroesButtonEle.click();
+ }).then(function() {
+ expect(heroEles.count()).toBe(0, 'should no longer have any heroes');
+ expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 orig + 1 reset + 3 removeall');
+ });
+ });
+
+ it('should support "spy counter"', function () {
+ let updateCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Update'));
+ let resetCounterButtonEle = element(by.cssContainingText('counter-parent button', 'Reset'));
+ let textEle = element(by.css('counter-parent my-counter > div'));
+ let logEles = element.all(by.css('counter-parent h4 ~ div'));
+ expect(textEle.getText()).toContain('Counter = 0');
+ expect(logEles.count()).toBe(2, 'should start with two log entries');
+ updateCounterButtonEle.click().then(function() {
+ expect(textEle.getText()).toContain('Counter = 1');
+ expect(logEles.count()).toBe(3, 'should now have 3 log entries');
+ return resetCounterButtonEle.click();
+ }).then(function() {
+ expect(textEle.getText()).toContain('Counter = 0');
+ expect(logEles.count()).toBe(7, 'should now have 7 log entries - 3 prev + 1 reset + 2 destroy + 1 init');
+ });
+ });
+});
diff --git a/public/docs/_examples/lifecycle-hooks/ts/example-config.json b/public/docs/_examples/lifecycle-hooks/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/lifecycle-hooks/ts/plnkr.json b/public/docs/_examples/lifecycle-hooks/ts/plnkr.json
new file mode 100644
index 0000000000..49290aa804
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Lifecycle Hooks",
+ "basePath": "src/",
+ "files":["!**/*.d.ts", "!**/*.js"],
+ "tags": ["lifecycle", "hooks",
+ "OnInit", "OnDestroy", "OnChange", "DoCheck",
+ "AfterContentInit", "AfterContentChecked",
+ "AfterViewInit", "AfterViewChecked"]
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/after-content.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/after-content.component.ts
new file mode 100644
index 0000000000..dfd2b80d24
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/after-content.component.ts
@@ -0,0 +1,116 @@
+// #docplaster
+// #docregion
+import { AfterContentChecked, AfterContentInit, Component, ContentChild } from '@angular/core';
+
+import { LoggerService } from './logger.service';
+
+//////////////////
+@Component({
+ selector: 'my-child',
+ template: ' '
+})
+export class ChildComponent {
+ hero = 'Magneta';
+}
+
+//////////////////////
+@Component({
+ selector: 'after-content',
+// #docregion template
+ template: `
+ -- projected content begins --
+
+ -- projected content ends --
`
+// #enddocregion template
+ + `
+
+ `
+})
+// #docregion hooks
+export class AfterContentComponent implements AfterContentChecked, AfterContentInit {
+ private prevHero = '';
+ comment = '';
+
+ // Query for a CONTENT child of type `ChildComponent`
+ @ContentChild(ChildComponent) contentChild: ChildComponent;
+
+// #enddocregion hooks
+ constructor(private logger: LoggerService) {
+ this.logIt('AfterContent constructor');
+ }
+
+// #docregion hooks
+ ngAfterContentInit() {
+ // contentChild is set after the content has been initialized
+ this.logIt('AfterContentInit');
+ this.doSomething();
+ }
+
+ ngAfterContentChecked() {
+ // contentChild is updated after the content has been checked
+ if (this.prevHero === this.contentChild.hero) {
+ this.logIt('AfterContentChecked (no change)');
+ } else {
+ this.prevHero = this.contentChild.hero;
+ this.logIt('AfterContentChecked');
+ this.doSomething();
+ }
+ }
+// #enddocregion hooks
+// #docregion do-something
+
+ // This surrogate for real business logic sets the `comment`
+ private doSomething() {
+ this.comment = this.contentChild.hero.length > 10 ? `That's a long name` : '';
+ }
+
+ private logIt(method: string) {
+ let child = this.contentChild;
+ let message = `${method}: ${child ? child.hero : 'no'} child content`;
+ this.logger.log(message);
+ }
+// #docregion hooks
+ // ...
+}
+// #enddocregion hooks
+
+//////////////
+@Component({
+ selector: 'after-content-parent',
+ template: `
+
+
AfterContent
+
+
` +
+// #docregion parent-template
+ `
+
+ `
+// #enddocregion parent-template
++ `
+
+
-- AfterContent Logs --
+
Reset
+
{{msg}}
+
+ `,
+ styles: ['.parent {background: burlywood}'],
+ providers: [LoggerService]
+})
+export class AfterContentParentComponent {
+ logs: string[];
+ show = true;
+
+ constructor(private logger: LoggerService) {
+ this.logs = logger.logs;
+ }
+
+ reset() {
+ this.logs.length = 0;
+ // quickly remove and reload AfterContentComponent which recreates it
+ this.show = false;
+ this.logger.tick_then(() => this.show = true);
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/after-view.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/after-view.component.ts
new file mode 100644
index 0000000000..71310e4530
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/after-view.component.ts
@@ -0,0 +1,118 @@
+// #docplaster
+// #docregion
+import { AfterViewChecked, AfterViewInit, Component, ViewChild } from '@angular/core';
+
+import { LoggerService } from './logger.service';
+
+//////////////////
+// #docregion child-view
+@Component({
+ selector: 'my-child-view',
+ template: ' '
+})
+export class ChildViewComponent {
+ hero = 'Magneta';
+}
+// #enddocregion child-view
+
+//////////////////////
+@Component({
+ selector: 'after-view',
+// #docregion template
+ template: `
+ -- child view begins --
+
+ -- child view ends --
`
+// #enddocregion template
+ + `
+
+ `
+})
+// #docregion hooks
+export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
+ private prevHero = '';
+
+ // Query for a VIEW child of type `ChildViewComponent`
+ @ViewChild(ChildViewComponent) viewChild: ChildViewComponent;
+
+// #enddocregion hooks
+ constructor(private logger: LoggerService) {
+ this.logIt('AfterView constructor');
+ }
+
+// #docregion hooks
+ ngAfterViewInit() {
+ // viewChild is set after the view has been initialized
+ this.logIt('AfterViewInit');
+ this.doSomething();
+ }
+
+ ngAfterViewChecked() {
+ // viewChild is updated after the view has been checked
+ if (this.prevHero === this.viewChild.hero) {
+ this.logIt('AfterViewChecked (no change)');
+ } else {
+ this.prevHero = this.viewChild.hero;
+ this.logIt('AfterViewChecked');
+ this.doSomething();
+ }
+ }
+// #enddocregion hooks
+
+ comment = '';
+
+// #docregion do-something
+ // This surrogate for real business logic sets the `comment`
+ private doSomething() {
+ let c = this.viewChild.hero.length > 10 ? `That's a long name` : '';
+ if (c !== this.comment) {
+ // Wait a tick because the component's view has already been checked
+ this.logger.tick_then(() => this.comment = c);
+ }
+ }
+// #enddocregion do-something
+
+ private logIt(method: string) {
+ let child = this.viewChild;
+ let message = `${method}: ${child ? child.hero : 'no'} child view`;
+ this.logger.log(message);
+ }
+// #docregion hooks
+ // ...
+}
+// #enddocregion hooks
+
+//////////////
+@Component({
+ selector: 'after-view-parent',
+ template: `
+
+
AfterView
+
+
+
+
-- AfterView Logs --
+
Reset
+
{{msg}}
+
+ `,
+ styles: ['.parent {background: burlywood}'],
+ providers: [LoggerService]
+})
+export class AfterViewParentComponent {
+ logs: string[];
+ show = true;
+
+ constructor(private logger: LoggerService) {
+ this.logs = logger.logs;
+ }
+
+ reset() {
+ this.logs.length = 0;
+ // quickly remove and reload AfterViewComponent which recreates it
+ this.show = false;
+ this.logger.tick_then(() => this.show = true);
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/app.component.html b/public/docs/_examples/lifecycle-hooks/ts/src/app/app.component.html
new file mode 100644
index 0000000000..d0692e28ac
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/app.component.html
@@ -0,0 +1,37 @@
+
+Component Lifecycle Hooks
+Peek-a-boo: (most) lifecycle hooks
+OnChanges
+DoCheck
+AfterViewInit & AfterViewChecked
+AfterContentInit & AfterContentChecked
+Spy: directive with OnInit & OnDestroy
+Counter: OnChanges + Spy directive
+
+
+
+back to top
+
+
+
+back to top
+
+
+
+back to top
+
+
+
+back to top
+
+
+
+back to top
+
+
+
+back to top
+
+
+
+back to top
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/app.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..5bd535a113
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/app.component.ts
@@ -0,0 +1,7 @@
+// #docregion
+import { Component } from '@angular/core';
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/app.module.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..4d9cabf218
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/app.module.ts
@@ -0,0 +1,67 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+
+import {
+ AfterContentParentComponent,
+ AfterContentComponent,
+ ChildComponent
+} from './after-content.component';
+
+import {
+ AfterViewParentComponent,
+ AfterViewComponent,
+ ChildViewComponent
+} from './after-view.component';
+
+import {
+ CounterParentComponent,
+ MyCounterComponent
+} from './counter.component';
+
+import {
+ DoCheckParentComponent,
+ DoCheckComponent
+} from './do-check.component';
+
+import {
+ OnChangesParentComponent,
+ OnChangesComponent
+} from './on-changes.component';
+
+import { PeekABooParentComponent } from './peek-a-boo-parent.component';
+import { PeekABooComponent } from './peek-a-boo.component';
+
+import { SpyParentComponent } from './spy.component';
+import { SpyDirective } from './spy.directive';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ declarations: [
+ AppComponent,
+ AfterContentParentComponent,
+ AfterContentComponent,
+ ChildComponent,
+ AfterViewParentComponent,
+ AfterViewComponent,
+ ChildViewComponent,
+ CounterParentComponent,
+ MyCounterComponent,
+ DoCheckParentComponent,
+ DoCheckComponent,
+ OnChangesParentComponent,
+ OnChangesComponent,
+ PeekABooParentComponent,
+ PeekABooComponent,
+ SpyParentComponent,
+ SpyDirective
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/counter.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/counter.component.ts
new file mode 100644
index 0000000000..8efd1fbd52
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/counter.component.ts
@@ -0,0 +1,84 @@
+// #docregion
+import {
+ Component, Input,
+ OnChanges, SimpleChanges,
+} from '@angular/core';
+
+import { LoggerService } from './logger.service';
+
+@Component({
+ selector: 'my-counter',
+ template: `
+
+ Counter = {{counter}}
+
+
-- Counter Change Log --
+
{{chg}}
+
+ `,
+ styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}']
+})
+export class MyCounterComponent implements OnChanges {
+ @Input() counter: number;
+ changeLog: string[] = [];
+
+ ngOnChanges(changes: SimpleChanges) {
+
+ // Empty the changeLog whenever counter goes to zero
+ // hint: this is a way to respond programmatically to external value changes.
+ if (this.counter === 0) {
+ this.changeLog.length = 0;
+ }
+
+ // A change to `counter` is the only change we care about
+ let chng = changes['counter'];
+ let cur = chng.currentValue;
+ let prev = JSON.stringify(chng.previousValue); // first time is {}; after is integer
+ this.changeLog.push(`counter: currentValue = ${cur}, previousValue = ${prev}`);
+ }
+}
+
+/***************************************/
+
+@Component({
+ selector: 'counter-parent',
+ template: `
+
+
Counter Spy
+
+
Update counter
+
Reset Counter
+
+
+
+
-- Spy Lifecycle Hook Log --
+
{{msg}}
+
+ `,
+ styles: ['.parent {background: gold;}'],
+ providers: [LoggerService]
+})
+export class CounterParentComponent {
+ value: number;
+ spyLog: string[] = [];
+
+ private logger: LoggerService;
+
+ constructor(logger: LoggerService) {
+ this.logger = logger;
+ this.spyLog = logger.logs;
+ this.reset();
+ }
+
+ updateCounter() {
+ this.value += 1;
+ this.logger.tick();
+ }
+
+ reset() {
+ this.logger.log('-- reset --');
+ this.value = 0;
+ this.logger.tick();
+ }
+}
+
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/do-check-parent.component.html b/public/docs/_examples/lifecycle-hooks/ts/src/app/do-check-parent.component.html
new file mode 100644
index 0000000000..cf7c2b91ce
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/do-check-parent.component.html
@@ -0,0 +1,13 @@
+
+
{{title}}
+
+
+
Reset Log
+
+
+
+
+
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/do-check.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/do-check.component.ts
new file mode 100644
index 0000000000..6196dd8edd
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/do-check.component.ts
@@ -0,0 +1,95 @@
+/* tslint:disable:forin */
+// #docregion
+import { Component, DoCheck, Input, ViewChild } from '@angular/core';
+
+class Hero {
+ constructor(public name: string) {}
+}
+
+@Component({
+ selector: 'do-check',
+ template: `
+
+
{{hero.name}} can {{power}}
+
+
-- Change Log --
+
{{chg}}
+
+ `,
+ styles: [
+ '.hero {background: LightYellow; padding: 8px; margin-top: 8px}',
+ 'p {background: Yellow; padding: 8px; margin-top: 8px}'
+ ]
+})
+export class DoCheckComponent implements DoCheck {
+ @Input() hero: Hero;
+ @Input() power: string;
+
+ changeDetected = false;
+ changeLog: string[] = [];
+ oldHeroName = '';
+ oldPower = '';
+ oldLogLength = 0;
+ noChangeCount = 0;
+
+ // #docregion ng-do-check
+ ngDoCheck() {
+
+ if (this.hero.name !== this.oldHeroName) {
+ this.changeDetected = true;
+ this.changeLog.push(`DoCheck: Hero name changed to "${this.hero.name}" from "${this.oldHeroName}"`);
+ this.oldHeroName = this.hero.name;
+ }
+
+ if (this.power !== this.oldPower) {
+ this.changeDetected = true;
+ this.changeLog.push(`DoCheck: Power changed to "${this.power}" from "${this.oldPower}"`);
+ this.oldPower = this.power;
+ }
+
+ if (this.changeDetected) {
+ this.noChangeCount = 0;
+ } else {
+ // log that hook was called when there was no relevant change.
+ let count = this.noChangeCount += 1;
+ let noChangeMsg = `DoCheck called ${count}x when no change to hero or power`;
+ if (count === 1) {
+ // add new "no change" message
+ this.changeLog.push(noChangeMsg);
+ } else {
+ // update last "no change" message
+ this.changeLog[this.changeLog.length - 1] = noChangeMsg;
+ }
+ }
+
+ this.changeDetected = false;
+ }
+ // #enddocregion ng-do-check
+
+ reset() {
+ this.changeDetected = true;
+ this.changeLog.length = 0;
+ }
+}
+
+/***************************************/
+
+@Component({
+ selector: 'do-check-parent',
+ templateUrl: './do-check-parent.component.html',
+ styles: ['.parent {background: Lavender}']
+})
+export class DoCheckParentComponent {
+ hero: Hero;
+ power: string;
+ title = 'DoCheck';
+ @ViewChild(DoCheckComponent) childView: DoCheckComponent;
+
+ constructor() { this.reset(); }
+
+ reset() {
+ this.hero = new Hero('Windstorm');
+ this.power = 'sing';
+ if (this.childView) { this.childView.reset(); }
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/logger.service.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/logger.service.ts
new file mode 100644
index 0000000000..56ddaf9063
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/logger.service.ts
@@ -0,0 +1,26 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class LoggerService {
+ logs: string[] = [];
+ prevMsg = '';
+ prevMsgCount = 1;
+
+ log(msg: string) {
+ if (msg === this.prevMsg) {
+ // Repeat message; update last log entry with count.
+ this.logs[this.logs.length - 1] = msg + ` (${this.prevMsgCount += 1}x)`;
+ } else {
+ // New message; log it.
+ this.prevMsg = msg;
+ this.prevMsgCount = 1;
+ this.logs.push(msg);
+ }
+ }
+
+ clear() { this.logs.length = 0; }
+
+ // schedules a view refresh to ensure display catches up
+ tick() { this.tick_then(() => { }); }
+ tick_then(fn: () => any) { setTimeout(fn, 0); }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/on-changes-parent.component.html b/public/docs/_examples/lifecycle-hooks/ts/src/app/on-changes-parent.component.html
new file mode 100644
index 0000000000..a0fd404931
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/on-changes-parent.component.html
@@ -0,0 +1,13 @@
+
+
{{title}}
+
+
+
Reset Log
+
+
+
+
+
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/on-changes.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/on-changes.component.ts
new file mode 100644
index 0000000000..a5a49a61e9
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/on-changes.component.ts
@@ -0,0 +1,73 @@
+/* tslint:disable:forin */
+// #docregion
+import {
+ Component, Input, OnChanges,
+ SimpleChanges, ViewChild
+} from '@angular/core';
+
+class Hero {
+ constructor(public name: string) {}
+}
+
+@Component({
+ selector: 'on-changes',
+ template: `
+
+
{{hero.name}} can {{power}}
+
+
-- Change Log --
+
{{chg}}
+
+ `,
+ styles: [
+ '.hero {background: LightYellow; padding: 8px; margin-top: 8px}',
+ 'p {background: Yellow; padding: 8px; margin-top: 8px}'
+ ]
+})
+export class OnChangesComponent implements OnChanges {
+// #docregion inputs
+ @Input() hero: Hero;
+ @Input() power: string;
+// #enddocregion inputs
+
+ changeLog: string[] = [];
+
+ // #docregion ng-on-changes
+ ngOnChanges(changes: SimpleChanges) {
+ for (let propName in changes) {
+ let chng = changes[propName];
+ let cur = JSON.stringify(chng.currentValue);
+ let prev = JSON.stringify(chng.previousValue);
+ this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
+ }
+ }
+ // #enddocregion ng-on-changes
+
+ reset() { this.changeLog.length = 0; }
+}
+
+/***************************************/
+
+@Component({
+ selector: 'on-changes-parent',
+ templateUrl: './on-changes-parent.component.html',
+ styles: ['.parent {background: Lavender;}']
+})
+export class OnChangesParentComponent {
+ hero: Hero;
+ power: string;
+ title = 'OnChanges';
+ @ViewChild(OnChangesComponent) childView: OnChangesComponent;
+
+ constructor() {
+ this.reset();
+ }
+
+ reset() {
+ // new Hero object every time; triggers onChanges
+ this.hero = new Hero('Windstorm');
+ // setting power only triggers onChanges if this value is different
+ this.power = 'sing';
+ if (this.childView) { this.childView.reset(); }
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/peek-a-boo-parent.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/peek-a-boo-parent.component.ts
new file mode 100644
index 0000000000..3f2bd8585d
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/peek-a-boo-parent.component.ts
@@ -0,0 +1,53 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { LoggerService } from './logger.service';
+
+@Component({
+ selector: 'peek-a-boo-parent',
+ template: `
+
+
Peek-A-Boo
+
+
+ {{hasChild ? 'Destroy' : 'Create'}} PeekABooComponent
+
+
Update Hero
+
+
+
+
+
-- Lifecycle Hook Log --
+
{{msg}}
+
+ `,
+ styles: ['.parent {background: moccasin}'],
+ providers: [ LoggerService ]
+})
+export class PeekABooParentComponent {
+
+ hasChild = false;
+ hookLog: string[];
+
+ heroName = 'Windstorm';
+ private logger: LoggerService;
+
+ constructor(logger: LoggerService) {
+ this.logger = logger;
+ this.hookLog = logger.logs;
+ }
+
+ toggleChild() {
+ this.hasChild = !this.hasChild;
+ if (this.hasChild) {
+ this.heroName = 'Windstorm';
+ this.logger.clear(); // clear log on create
+ }
+ this.logger.tick();
+ }
+
+ updateHero() {
+ this.heroName += '!';
+ this.logger.tick();
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/peek-a-boo.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/peek-a-boo.component.ts
new file mode 100644
index 0000000000..dcee428753
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/peek-a-boo.component.ts
@@ -0,0 +1,85 @@
+import {
+ AfterContentChecked,
+ AfterContentInit,
+ AfterViewChecked,
+ AfterViewInit,
+ DoCheck,
+ OnChanges,
+ OnDestroy,
+ OnInit,
+ SimpleChanges
+} from '@angular/core';
+import { Component, Input } from '@angular/core';
+import { LoggerService } from './logger.service';
+
+let nextId = 1;
+
+// #docregion ngOnInit
+export class PeekABoo implements OnInit {
+ constructor(private logger: LoggerService) { }
+
+ // implement OnInit's `ngOnInit` method
+ ngOnInit() { this.logIt(`OnInit`); }
+
+ logIt(msg: string) {
+ this.logger.log(`#${nextId++} ${msg}`);
+ }
+}
+// #enddocregion ngOnInit
+
+@Component({
+ selector: 'peek-a-boo',
+ template: 'Now you see my hero, {{name}}
',
+ styles: ['p {background: LightYellow; padding: 8px}']
+})
+// Don't HAVE to mention the Lifecycle Hook interfaces
+// unless we want typing and tool support.
+export class PeekABooComponent extends PeekABoo implements
+ OnChanges, OnInit, DoCheck,
+ AfterContentInit, AfterContentChecked,
+ AfterViewInit, AfterViewChecked,
+ OnDestroy {
+ @Input() name: string;
+
+ private verb = 'initialized';
+
+ constructor(logger: LoggerService) {
+ super(logger);
+
+ let is = this.name ? 'is' : 'is not';
+ this.logIt(`name ${is} known at construction`);
+ }
+
+ // only called for/if there is an @input variable set by parent.
+ ngOnChanges(changes: SimpleChanges) {
+ let changesMsgs: string[] = [];
+ for (let propName in changes) {
+ if (propName === 'name') {
+ let name = changes['name'].currentValue;
+ changesMsgs.push(`name ${this.verb} to "${name}"`);
+ } else {
+ changesMsgs.push(propName + ' ' + this.verb);
+ }
+ }
+ this.logIt(`OnChanges: ${changesMsgs.join('; ')}`);
+ this.verb = 'changed'; // next time it will be a change
+ }
+
+ // Beware! Called frequently!
+ // Called in every change detection cycle anywhere on the page
+ ngDoCheck() { this.logIt(`DoCheck`); }
+
+ ngAfterContentInit() { this.logIt(`AfterContentInit`); }
+
+ // Beware! Called frequently!
+ // Called in every change detection cycle anywhere on the page
+ ngAfterContentChecked() { this.logIt(`AfterContentChecked`); }
+
+ ngAfterViewInit() { this.logIt(`AfterViewInit`); }
+
+ // Beware! Called frequently!
+ // Called in every change detection cycle anywhere on the page
+ ngAfterViewChecked() { this.logIt(`AfterViewChecked`); }
+
+ ngOnDestroy() { this.logIt(`OnDestroy`); }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.component.html b/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.component.html
new file mode 100644
index 0000000000..782435b961
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.component.html
@@ -0,0 +1,16 @@
+
+
Spy Directive
+
+
+
Add Hero
+
Reset Heroes
+
+
+
+
+ {{hero}}
+
+
+
-- Spy Lifecycle Hook Log --
+
{{msg}}
+
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.component.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.component.ts
new file mode 100644
index 0000000000..0f3b0d35f1
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.component.ts
@@ -0,0 +1,40 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { LoggerService } from './logger.service';
+
+@Component({
+ selector: 'spy-parent',
+ templateUrl: './spy.component.html',
+ styles: [
+ '.parent {background: khaki;}',
+ '.heroes {background: LightYellow; padding: 0 8px}'
+ ],
+ providers: [LoggerService]
+})
+export class SpyParentComponent {
+ newName = 'Herbie';
+ heroes: string[] = ['Windstorm', 'Magneta'];
+ spyLog: string[];
+
+ constructor(private logger: LoggerService) {
+ this.spyLog = logger.logs;
+ }
+
+ addHero() {
+ if (this.newName.trim()) {
+ this.heroes.push(this.newName.trim());
+ this.newName = '';
+ this.logger.tick();
+ }
+ }
+ removeHero(hero: string) {
+ this.heroes.splice(this.heroes.indexOf(hero), 1);
+ this.logger.tick();
+ }
+ reset() {
+ this.logger.log('-- reset --');
+ this.heroes.length = 0;
+ this.logger.tick();
+ }
+}
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.directive.ts b/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.directive.ts
new file mode 100644
index 0000000000..01f3f95880
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/app/spy.directive.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { Directive, OnInit, OnDestroy } from '@angular/core';
+
+import { LoggerService } from './logger.service';
+
+let nextId = 1;
+
+// #docregion spy-directive
+// Spy on any element to which it is applied.
+// Usage: ...
+@Directive({selector: '[mySpy]'})
+export class SpyDirective implements OnInit, OnDestroy {
+
+ constructor(private logger: LoggerService) { }
+
+ ngOnInit() { this.logIt(`onInit`); }
+
+ ngOnDestroy() { this.logIt(`onDestroy`); }
+
+ private logIt(msg: string) {
+ this.logger.log(`Spy #${nextId++} ${msg}`);
+ }
+}
+// #enddocregion spy-directive
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/index.html b/public/docs/_examples/lifecycle-hooks/ts/src/index.html
new file mode 100644
index 0000000000..cf7059f780
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+ Angular Lifecycle Hooks
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/main.ts b/public/docs/_examples/lifecycle-hooks/ts/src/main.ts
new file mode 100644
index 0000000000..f22933ba8e
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/main.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/lifecycle-hooks/ts/src/sample.css b/public/docs/_examples/lifecycle-hooks/ts/src/sample.css
new file mode 100644
index 0000000000..df17c897c6
--- /dev/null
+++ b/public/docs/_examples/lifecycle-hooks/ts/src/sample.css
@@ -0,0 +1,13 @@
+.parent {
+ color: #666;
+ margin: 14px 0;
+ padding: 8px;
+}
+input {
+ margin: 4px;
+ padding: 4px;
+}
+.comment {
+ color: red;
+ font-style: italic;
+}
diff --git a/public/docs/_examples/ngcontainer/ts/example-config.json b/public/docs/_examples/ngcontainer/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/ngcontainer/ts/plnkr.json b/public/docs/_examples/ngcontainer/ts/plnkr.json
new file mode 100644
index 0000000000..a6cdc4ba1a
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/plnkr.json
@@ -0,0 +1,11 @@
+{
+ "description": "",
+ "basePath": "src/",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js"
+ ],
+ "tags": [
+ "ngcontainer", "structural", "directives"
+ ]
+}
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/app.component.css b/public/docs/_examples/ngcontainer/ts/src/app/app.component.css
new file mode 100644
index 0000000000..953ed3c39d
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/app.component.css
@@ -0,0 +1,34 @@
+/* #docregion */
+button {
+ min-width: 100px;
+ font-size: 100%;
+}
+
+code, .code {
+ background-color: #eee;
+ color: black;
+ font-family: Courier, sans-serif;
+ font-size: 85%;
+}
+
+div.code {
+ width: 400px;
+}
+
+.heroic {
+ font-size: 150%;
+ font-weight: bold;
+}
+
+hr {
+ margin: 40px 0
+}
+
+td, th {
+ text-align: left;
+ vertical-align: top;
+}
+
+/* #docregion p-span */
+p span { color: red; font-size: 70%; }
+/* #enddocregion p-span */
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/app.component.html b/public/docs/_examples/ngcontainer/ts/src/app/app.component.html
new file mode 100644
index 0000000000..afd0b00f36
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/app.component.html
@@ -0,0 +1,279 @@
+
+
+<ng-container>
+
+
+{{hero.name}}
+
+
+
+
+<ng-container> and CSS
+Examples demonstrating issues with rigid CSS styles.
+
+Toggle hero
+
+#1 <ng-container> and <p>
+
+
+ I turned the corner
+
+ and saw {{hero.name}}. I waved
+
+ and continued on my way.
+
+
+
+
+ I turned the corner
+
+ and saw {{hero.name}}. I waved
+
+ and continued on my way.
+
+
+
+#2 <ng-container> and <p>
+
+
+
+
+ {{hero.name}} is
+
+ ,
+ and
+ {{trait}}
+ .
+
+
+
+
+
+ {{hero.name}} is
+
+ ,
+ and
+ {{trait}}
+ .
+
+
+
+
+
+
#3 <ng-container> and <p>
+
+
+ Show ID
+
+
+
+
+ The
hero.id in the <span>
+ is caught by the
p-span CSS:
+
+
+
+ Id: ({{hero.id}})
+
+ Name: {{hero.name}}
+
+
+
+
+
+ The
hero.id in the <ng-container>
+ is unaffected by the
p-span CSS:
+
+
+ Id: ({{hero.id}})
+
+ Name: {{hero.name}}
+
+
+
+
+ The
hero.id in the <template *ngIf> disappears:
+
+
+ Id: ({{hero.id}})
+
+ Name: {{hero.name}}
+
+
+
+
+ The
hero.id in the <template [ngIf]>
+ is unaffected by the
p-span CSS:
+
+
+ Id: ({{hero.id}})
+
+ Name: {{hero.name}}
+
+
+
+
+
+
+
+<ng-container> and layout-sensitive elements
+
+ Examples demonstrating issues with layout-sensitive elements
+ such as <select> and <table>.
+
+
+#1 <ng-container> and <options>
+
+<select> with <span>
+
+ Pick your favorite hero
+ ( show sad )
+
+
+
+
+
+ {{h.name}} ({{h?.emotion}})
+
+
+
+
+
+<select> with <ng-container>
+
+ Pick your favorite hero
+ ( show sad )
+
+
+
+
+
+ {{h.name}} ({{h?.emotion}})
+
+
+
+
+
+
+
+#2 <ng-container> and <options>
+
+
+ Show default traits
+
+
+
+Options with <ng-container>
+
+
+
+
+
+ LOYAL
+ CLEAN
+ REVERENT
+
+
+ {{ trait | uppercase }}
+
+
+
+
+
+Options with <span>
+
+
+
+
+ LOYAL
+ CLEAN
+ REVERENT
+
+
+ {{ trait | uppercase }}
+
+
+
+
+
+
+<ng-container> and <table>
+
+ Attribute directives
+ Structural directives
+ div with *ngIf
+
+
+
+
+ Directive
+ Type
+ Description
+
+
+
+ NgClass A Add or remove multiple CSS classes.
+
+
+
+
+
+
+ xxx S div with *ngIf formats crazy.
+
+
+ yyy S div with *ngIf formats crazy.
+
+
+
+
+
+
+
+
+ NgFor S Repeat the template for each item in a list.
+
+
+ NgIf S Add or remove DOM elements.
+
+
+
+
+
+
+ NgStyle A Add or remove multiple style attributes.
+
+
+
+
+
+ NgSwitch S Include in DOM if case matches the switch value.
+
+
+
+
+
+
+
+Do not confuse <ng-container> with <ng-content>
+
+<ng-container>Inside ng-container</ng-container>
+
+Inside ng-container
+
+
+<ng-content>this is an Angular parse error</ng-content>
+
+
+
+Template parse errors:
+<ng-content> element cannot have content.
+
+Demo of </ng-content>
+
+
+ Projected content
+
+
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/app.component.ts b/public/docs/_examples/ngcontainer/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..65c511acd2
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/app.component.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { heroes } from './hero';
+
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html',
+ styleUrls: [ './app.component.css' ]
+})
+export class AppComponent {
+ heroes = heroes;
+ hero = this.heroes[0];
+ heroTraits = [ 'honest', 'brave', 'considerate' ];
+
+ // flags for the table
+ attrDirs = true;
+ strucDirs = true;
+ divNgIf = false;
+
+ showId = true;
+ showDefaultTraits = true;
+ showSad = true;
+}
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/app.module.ts b/public/docs/_examples/ngcontainer/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..57ac92f518
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/app.module.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+import { ContentComponent } from './content.component';
+import { heroComponents } from './hero.components';
+
+@NgModule({
+ imports: [ BrowserModule, FormsModule ],
+ declarations: [
+ AppComponent,
+ ContentComponent,
+ heroComponents
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/content.component.ts b/public/docs/_examples/ngcontainer/ts/src/app/content.component.ts
new file mode 100644
index 0000000000..63c5646f99
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/content.component.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'content-comp',
+ // #docregion template
+ template:
+ `
+
+
`,
+ // #enddocregion template
+ styles: [ `
+ div { border: medium dashed green; padding: 1em; width: 150px; text-align: center}
+ `]
+})
+export class ContentComponent { }
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/hero.components.ts b/public/docs/_examples/ngcontainer/ts/src/app/hero.components.ts
new file mode 100644
index 0000000000..43e400ed8e
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/hero.components.ts
@@ -0,0 +1,43 @@
+// #docregion
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+@Component({
+ selector: 'happy-hero',
+ template: `Wow. You like {{hero.name}}. What a happy hero ... just like you.`
+})
+export class HappyHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'sad-hero',
+ template: `You like {{hero.name}}? Such a sad hero. Are you sad too?`
+})
+export class SadHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'confused-hero',
+ template: `Are you as confused as {{hero.name}}?`
+})
+export class ConfusedHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'unknown-hero',
+ template: `{{message}}`
+})
+export class UnknownHeroComponent {
+ @Input() hero: Hero;
+ get message() {
+ return this.hero && this.hero.name ?
+ `${this.hero.name} is strange and mysterious.` :
+ 'Are you feeling indecisive?';
+ }
+}
+
+export const heroComponents =
+ [ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
diff --git a/public/docs/_examples/ngcontainer/ts/src/app/hero.ts b/public/docs/_examples/ngcontainer/ts/src/app/hero.ts
new file mode 100644
index 0000000000..a1de3b3b82
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/app/hero.ts
@@ -0,0 +1,13 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+ emotion?: string;
+}
+
+export const heroes: Hero[] = [
+ { id: 1, name: 'Mr. Nice', emotion: 'happy'},
+ { id: 2, name: 'Narco', emotion: 'sad' },
+ { id: 3, name: 'Windstorm', emotion: 'confused' },
+ { id: 4, name: 'Magneta'}
+];
diff --git a/public/docs/_examples/ngcontainer/ts/src/index.html b/public/docs/_examples/ngcontainer/ts/src/index.html
new file mode 100644
index 0000000000..fc5ff417c3
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+ Angular <ng-container>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/ngcontainer/ts/src/main.ts b/public/docs/_examples/ngcontainer/ts/src/main.ts
new file mode 100644
index 0000000000..105b06712d
--- /dev/null
+++ b/public/docs/_examples/ngcontainer/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+
diff --git a/public/docs/_examples/ngmodule/e2e-spec.ts b/public/docs/_examples/ngmodule/e2e-spec.ts
new file mode 100644
index 0000000000..0fbce1213d
--- /dev/null
+++ b/public/docs/_examples/ngmodule/e2e-spec.ts
@@ -0,0 +1,223 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('NgModule', function () {
+
+ // helpers
+ const gold = 'rgba(255, 215, 0, 1)';
+ const powderblue = 'rgba(176, 224, 230, 1)';
+ const lightgray = 'rgba(211, 211, 211, 1)';
+ const white = 'rgba(0, 0, 0, 0)';
+
+ function getCommonsSectionStruct() {
+ const buttons = element.all(by.css('nav a'));
+
+ return {
+ title: element.all(by.tagName('h1')).get(0),
+ subtitle: element.all(by.css('app-title p i')).get(0),
+ contactButton: buttons.get(0),
+ crisisButton: buttons.get(1),
+ heroesButton: buttons.get(2)
+ };
+ }
+
+ function getContactSectionStruct() {
+ const buttons = element.all(by.css('app-contact form button'));
+
+ return {
+ header: element.all(by.css('app-contact h2')).get(0),
+ popupMessage: element.all(by.css('app-contact div')).get(0),
+ contactNameHeader: element.all(by.css('app-contact form h3')).get(0),
+ input: element.all(by.css('app-contact form input')).get(0),
+ validationError: element.all(by.css('app-contact form .alert')).get(0),
+ saveButton: buttons.get(0), // can't be tested
+ nextContactButton: buttons.get(1),
+ newContactButton: buttons.get(2)
+ };
+ }
+
+ function getCrisisSectionStruct() {
+ return {
+ title: element.all(by.css('ng-component h3')).get(0),
+ items: element.all(by.css('ng-component a')),
+ itemId: element.all(by.css('ng-component div')).get(0),
+ listLink: element.all(by.css('ng-component a')).get(0),
+ };
+ }
+
+ function getHeroesSectionStruct() {
+ return {
+ header: element.all(by.css('ng-component h2')).get(0),
+ title: element.all(by.css('ng-component h3')).get(0),
+ items: element.all(by.css('ng-component a')),
+ itemId: element.all(by.css('ng-component ng-component div div')).get(0),
+ itemInput: element.all(by.css('ng-component ng-component input')).get(0),
+ listLink: element.all(by.css('ng-component ng-component a')).get(0),
+ };
+ }
+
+ // tests
+ function appTitleTests(color: string, name?: string) {
+ return function() {
+ it('should have a gray header', function() {
+ const commons = getCommonsSectionStruct();
+ expect(commons.title.getCssValue('backgroundColor')).toBe(color);
+ });
+
+ it('should welcome us', function () {
+ const commons = getCommonsSectionStruct();
+ expect(commons.subtitle.getText()).toBe('Welcome, ' + (name || 'Sherlock Holmes'));
+ });
+ };
+ }
+
+ function contactTests(color: string, name?: string) {
+ return function() {
+ it('shows the contact\'s owner', function() {
+ const contacts = getContactSectionStruct();
+ expect(contacts.header.getText()).toBe('Contact of ' + (name || 'Sherlock Holmes'));
+ });
+
+ it('can cycle between contacts', function () {
+ const contacts = getContactSectionStruct();
+ const nextButton = contacts.nextContactButton;
+ expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spade');
+ expect(contacts.contactNameHeader.getCssValue('backgroundColor')).toBe(color);
+ nextButton.click().then(function () {
+ expect(contacts.contactNameHeader.getText()).toBe('Awesome Nick Danger');
+ return nextButton.click();
+ }).then(function () {
+ expect(contacts.contactNameHeader.getText()).toBe('Awesome Nancy Drew');
+ });
+ });
+
+ it('can change an existing contact', function () {
+ const contacts = getContactSectionStruct();
+ contacts.input.sendKeys('a');
+ expect(contacts.input.getCssValue('backgroundColor')).toBe(color);
+ expect(contacts.contactNameHeader.getText()).toBe('Awesome Sam Spadea');
+ });
+
+ it('can create a new contact', function () {
+ const contacts = getContactSectionStruct();
+ const newContactButton = contacts.newContactButton;
+ newContactButton.click().then(function () {
+ expect(contacts.validationError.getText()).toBe('Name is required');
+ contacts.input.sendKeys('John Doe');
+ expect(contacts.contactNameHeader.getText()).toBe('Awesome John Doe');
+ expect(contacts.validationError.getText()).toBe('');
+ });
+ });
+ };
+ }
+
+ describe('index.html', function () {
+ beforeEach(function () {
+ browser.get('');
+ });
+
+ describe('app-title', appTitleTests(white, 'Miss Marple'));
+
+ describe('contact', contactTests(lightgray, 'Miss Marple'));
+
+ describe('crisis center', function () {
+ beforeEach(function () {
+ getCommonsSectionStruct().crisisButton.click();
+ });
+
+ it('shows a list of crisis', function () {
+ const crisis = getCrisisSectionStruct();
+ expect(crisis.title.getText()).toBe('Crisis List');
+ expect(crisis.items.count()).toBe(4);
+ expect(crisis.items.get(0).getText()).toBe('1 - Dragon Burning Cities');
+ });
+
+ it('can navigate to one crisis details', function () {
+ const crisis = getCrisisSectionStruct();
+ crisis.items.get(0).click().then(function() {
+ expect(crisis.itemId.getText()).toBe('Crisis id: 1');
+ return crisis.listLink.click();
+ }).then(function () {
+ // We are back to the list
+ expect(crisis.items.count()).toBe(4);
+ });
+ });
+ });
+
+ describe('heroes', function () {
+ beforeEach(function () {
+ getCommonsSectionStruct().heroesButton.click();
+ });
+
+ it('shows a list of heroes', function() {
+ const heroes = getHeroesSectionStruct();
+ expect(heroes.header.getText()).toBe('Heroes of Miss Marple');
+ expect(heroes.title.getText()).toBe('Hero List');
+ expect(heroes.items.count()).toBe(6);
+ expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice');
+ });
+
+ it('can navigate and edit one hero details', function () {
+ const heroes = getHeroesSectionStruct();
+ heroes.items.get(0).click().then(function () {
+ expect(heroes.itemId.getText()).toBe('Id: 11');
+ heroes.itemInput.sendKeys(' try');
+ return heroes.listLink.click();
+ }).then(function () {
+ // We are back to the list
+ expect(heroes.items.count()).toBe(6);
+ expect(heroes.items.get(0).getText()).toBe('11 - Mr. Nice try');
+ });
+ });
+ });
+ });
+
+ describe('index.0.html', function() {
+ beforeEach(function () {
+ browser.get('index.0.html');
+ });
+
+ it('has a title', function () {
+ const title = element.all(by.tagName('h1')).get(0);
+ expect(title.getText()).toBe('Minimal NgModule');
+ });
+ });
+
+ describe('index.1.html', function () {
+ beforeEach(function () {
+ browser.get('index.1.html');
+ });
+
+ describe('app-title', appTitleTests(powderblue));
+ });
+
+ describe('index.1b.html', function () {
+ beforeEach(function () {
+ browser.get('index.1b.html');
+ });
+
+ describe('app-title', appTitleTests(powderblue));
+
+ describe('contact', contactTests(powderblue));
+ });
+
+ describe('index.2.html', function () {
+ beforeEach(function () {
+ browser.get('index.2.html');
+ });
+
+ describe('app-title', appTitleTests(gold));
+
+ describe('contact', contactTests(powderblue));
+ });
+
+ describe('index.3.html', function () {
+ beforeEach(function () {
+ browser.get('index.3.html');
+ });
+
+ describe('app-title', appTitleTests(gold));
+ });
+
+});
diff --git a/public/docs/_examples/ngmodule/ts/contact.1b.plnkr.json b/public/docs/_examples/ngmodule/ts/contact.1b.plnkr.json
new file mode 100644
index 0000000000..0f61c91f75
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/contact.1b.plnkr.json
@@ -0,0 +1,25 @@
+{
+ "description": "Contact NgModule v.1",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.1b.ts",
+ "app/app.module.1b.ts",
+ "app/highlight.directive.ts",
+ "app/title.component.html",
+ "app/title.component.ts",
+ "app/user.service.ts",
+
+ "app/contact/awesome.pipe.ts",
+ "app/contact/contact.component.css",
+ "app/contact/contact.component.html",
+ "app/contact/contact.component.3.ts",
+ "app/contact/contact.service.ts",
+ "app/contact/highlight.directive.ts",
+
+ "main.1b.ts",
+ "styles.css",
+ "index.1b.html"
+ ],
+ "main": "index.1b.html",
+ "tags": ["NgModule"]
+}
diff --git a/public/docs/_examples/ngmodule/ts/contact.2.plnkr.json b/public/docs/_examples/ngmodule/ts/contact.2.plnkr.json
new file mode 100644
index 0000000000..6555b76dcd
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/contact.2.plnkr.json
@@ -0,0 +1,27 @@
+{
+ "description": "Contact NgModule v.2",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.2.ts",
+ "app/app.module.2.ts",
+ "app/highlight.directive.ts",
+ "app/title.component.html",
+ "app/title.component.ts",
+ "app/user.service.ts",
+
+ "app/contact/contact.component.css",
+ "app/contact/contact.component.html",
+ "app/contact/contact.service.ts",
+
+ "app/contact/awesome.pipe.ts",
+ "app/contact/contact.component.3.ts",
+ "app/contact/contact.module.2.ts",
+ "app/contact/highlight.directive.ts",
+
+ "main.2.ts",
+ "styles.css",
+ "index.2.html"
+ ],
+ "main": "index.2.html",
+ "tags": ["NgModule"]
+}
diff --git a/public/docs/_examples/ngmodule/ts/example-config.json b/public/docs/_examples/ngmodule/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/ngmodule/ts/minimal.0.plnkr.json b/public/docs/_examples/ngmodule/ts/minimal.0.plnkr.json
new file mode 100644
index 0000000000..e5e37ac4ee
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/minimal.0.plnkr.json
@@ -0,0 +1,13 @@
+{
+ "description": "Minimal NgModule",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.0.ts",
+ "app/app.module.0.ts",
+ "main.0.ts",
+ "styles.css",
+ "index.0.html"
+ ],
+ "main": "index.0.html",
+ "tags": ["NgModule"]
+}
diff --git a/public/docs/_examples/ngmodule/ts/plnkr.json b/public/docs/_examples/ngmodule/ts/plnkr.json
new file mode 100644
index 0000000000..4d9d2ec20b
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/plnkr.json
@@ -0,0 +1,41 @@
+{
+ "description": "NgModule Final",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.ts",
+ "app/app.module.ts",
+ "app/app-routing.module.ts",
+
+ "app/contact/contact.component.css",
+ "app/contact/contact.component.html",
+ "app/contact/contact.service.ts",
+
+ "app/contact/contact.component.ts",
+ "app/contact/contact.module.ts",
+ "app/contact/contact-routing.module.ts",
+
+ "app/crisis/*.ts",
+
+ "app/hero/hero-detail.component.ts",
+ "app/hero/hero-list.component.ts",
+ "app/hero/hero.service.ts",
+
+ "app/hero/hero.component.ts",
+ "app/hero/hero.module.ts",
+ "app/hero/hero-routing.module.ts",
+
+ "app/core/*.css",
+ "app/core/*.html",
+ "app/core/*.ts",
+
+ "app/shared/*.css",
+ "app/shared/*.html",
+ "app/shared/*.ts",
+
+ "main.ts",
+ "styles.css",
+ "index.html"
+ ],
+ "main": "index.html",
+ "tags": ["NgModule"]
+}
diff --git a/public/docs/_examples/ngmodule/ts/pre-shared.3.plnkr.json b/public/docs/_examples/ngmodule/ts/pre-shared.3.plnkr.json
new file mode 100644
index 0000000000..9747801604
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/pre-shared.3.plnkr.json
@@ -0,0 +1,41 @@
+{
+ "description": "NgModule v.3",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.3.ts",
+ "app/app.module.3.ts",
+ "app/app-routing.module.3.ts",
+
+ "app/highlight.directive.ts",
+ "app/title.component.html",
+ "app/title.component.ts",
+ "app/user.service.ts",
+
+ "app/contact/contact.component.css",
+ "app/contact/contact.component.html",
+ "app/contact/contact.service.ts",
+
+ "app/contact/awesome.pipe.ts",
+ "app/contact/contact.component.3.ts",
+ "app/contact/contact.module.3.ts",
+ "app/contact/contact-routing.module.3.ts",
+ "app/contact/highlight.directive.ts",
+
+ "app/crisis/*.ts",
+
+ "app/hero/hero-detail.component.ts",
+ "app/hero/hero-list.component.ts",
+ "app/hero/hero.service.ts",
+
+ "app/hero/hero.component.3.ts",
+ "app/hero/hero.module.3.ts",
+ "app/hero/hero-routing.module.3.ts",
+ "app/hero/highlight.directive.ts",
+
+ "main.3.ts",
+ "styles.css",
+ "index.3.html"
+ ],
+ "main": "index.3.html",
+ "tags": ["NgModule"]
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app-routing.module.3.ts b/public/docs/_examples/ngmodule/ts/src/app/app-routing.module.3.ts
new file mode 100644
index 0000000000..1d53b708f8
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app-routing.module.3.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+export const routes: Routes = [
+ { path: '', redirectTo: 'contact', pathMatch: 'full'},
+ { path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
+ { path: 'heroes', loadChildren: 'app/hero/hero.module.3#HeroModule' }
+];
+
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app-routing.module.ts b/public/docs/_examples/ngmodule/ts/src/app/app-routing.module.ts
new file mode 100644
index 0000000000..c753dcd488
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app-routing.module.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+export const routes: Routes = [
+ { path: '', redirectTo: 'contact', pathMatch: 'full'},
+// #docregion lazy-routes
+ { path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
+ { path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule' }
+// #enddocregion lazy-routes
+];
+
+// #docregion forRoot
+@NgModule({
+ imports: [RouterModule.forRoot(routes)],
+ exports: [RouterModule]
+})
+export class AppRoutingModule {}
+// #enddocregion forRoot
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.component.0.ts b/public/docs/_examples/ngmodule/ts/src/app/app.component.0.ts
new file mode 100644
index 0000000000..4977890c3b
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.component.0.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: '{{title}} ',
+})
+export class AppComponent {
+ title = 'Minimal NgModule';
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.component.1.ts b/public/docs/_examples/ngmodule/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..ccf44d4416
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.component.1.ts
@@ -0,0 +1,19 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+// #enddocregion
+ /*
+ // #docregion template
+ template: '{{title}} '
+ // #enddocregion template
+ */
+// #docregion
+ template: ' '
+})
+export class AppComponent {
+ subtitle = '(v1)';
+}
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.component.1b.ts b/public/docs/_examples/ngmodule/ts/src/app/app.component.1b.ts
new file mode 100644
index 0000000000..291bf0ac6b
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.component.1b.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+
+
+ `
+ // #enddocregion template
+})
+export class AppComponent {
+ subtitle = '(v1)';
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.component.2.ts b/public/docs/_examples/ngmodule/ts/src/app/app.component.2.ts
new file mode 100644
index 0000000000..a68b7d337d
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.component.2.ts
@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
+ `
+})
+export class AppComponent {
+ subtitle = '(v2)';
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.component.3.ts b/public/docs/_examples/ngmodule/ts/src/app/app.component.3.ts
new file mode 100644
index 0000000000..6d69a56f70
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.component.3.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+
+
+ Contact
+ Crisis Center
+ Heroes
+
+
+ `
+ // #enddocregion template
+})
+export class AppComponent {
+ subtitle = '(v3)';
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.component.ts b/public/docs/_examples/ngmodule/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..67336c8b08
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.component.ts
@@ -0,0 +1,19 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
+ Contact
+ Crisis Center
+ Heroes
+
+
+ `
+})
+export class AppComponent {
+ subtitle = '(Final)';
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.module.0.ts b/public/docs/_examples/ngmodule/ts/src/app/app.module.0.ts
new file mode 100644
index 0000000000..144ad7bb50
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.module.0.ts
@@ -0,0 +1,23 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import
+// #enddocregion
+ { AppComponent } from './app.component.0';
+/*
+// #docregion
+ { AppComponent } from './app.component';
+// #enddocregion
+*/
+// #docregion
+
+@NgModule({
+// #docregion imports
+ imports: [ BrowserModule ],
+// #enddocregion imports
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.module.1.ts b/public/docs/_examples/ngmodule/ts/src/app/app.module.1.ts
new file mode 100644
index 0000000000..f9f50e18d0
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.module.1.ts
@@ -0,0 +1,54 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import
+// #enddocregion
+ { AppComponent } from './app.component.1';
+/*
+// #docregion
+ { AppComponent } from './app.component';
+// #enddocregion
+*/
+// #docregion
+import { HighlightDirective } from './highlight.directive';
+import { TitleComponent } from './title.component';
+import { UserService } from './user.service';
+
+/* Contact Related Imports */
+import { FormsModule } from '@angular/forms';
+
+import { AwesomePipe } from './contact/awesome.pipe';
+import { ContactComponent } from './contact/contact.component.3';
+
+// #docregion import-contact-directive
+import {
+ HighlightDirective as ContactHighlightDirective
+} from './contact/highlight.directive';
+// #enddocregion import-contact-directive
+
+@NgModule({
+// #docregion imports
+ imports: [ BrowserModule, FormsModule ],
+// #enddocregion imports
+// #docregion declarations, directive, component
+ declarations: [
+ AppComponent,
+ HighlightDirective,
+// #enddocregion directive
+ TitleComponent,
+// #enddocregion component
+
+ AwesomePipe,
+ ContactComponent,
+ ContactHighlightDirective
+// #docregion directive, component
+ ],
+// #enddocregion declarations, directive, component
+// #docregion providers
+ providers: [ UserService ],
+// #enddocregion providers
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.module.1b.ts b/public/docs/_examples/ngmodule/ts/src/app/app.module.1b.ts
new file mode 100644
index 0000000000..ae04326239
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.module.1b.ts
@@ -0,0 +1,53 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+/* App Root */
+import
+// #enddocregion
+ { AppComponent } from './app.component.1b';
+/*
+// #docregion
+ { AppComponent } from './app.component';
+// #enddocregion
+*/
+// #docregion
+import { HighlightDirective } from './highlight.directive';
+import { TitleComponent } from './title.component';
+import { UserService } from './user.service';
+
+/* Contact Imports */
+import
+// #enddocregion
+ { ContactComponent } from './contact/contact.component.3';
+/*
+// #docregion
+ { ContactComponent } from './contact/contact.component';
+// #enddocregion
+*/
+// #docregion
+import { ContactService } from './contact/contact.service';
+import { AwesomePipe } from './contact/awesome.pipe';
+
+// #docregion import-alias
+import {
+ HighlightDirective as ContactHighlightDirective
+} from './contact/highlight.directive';
+// #enddocregion import-alias
+
+import { FormsModule } from '@angular/forms';
+
+@NgModule({
+ imports: [ BrowserModule, FormsModule ],
+// #docregion declarations
+ declarations: [
+ AppComponent, HighlightDirective, TitleComponent,
+ AwesomePipe, ContactComponent, ContactHighlightDirective
+ ],
+// #docregion providers
+ providers: [ ContactService, UserService ],
+// #enddocregion providers
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.module.2.ts b/public/docs/_examples/ngmodule/ts/src/app/app.module.2.ts
new file mode 100644
index 0000000000..f00e9b5d27
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.module.2.ts
@@ -0,0 +1,37 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+/* App Root */
+import
+// #enddocregion
+ { AppComponent } from './app.component.2';
+/*
+// #docregion
+ { AppComponent } from './app.component';
+// #enddocregion
+*/
+// #docregion
+import { HighlightDirective } from './highlight.directive';
+import { TitleComponent } from './title.component';
+import { UserService } from './user.service';
+
+/* Contact Imports */
+import
+// #enddocregion
+ { ContactModule } from './contact/contact.module.2';
+/*
+// #docregion
+ { ContactModule } from './contact/contact.module';
+// #enddocregion
+*/
+// #docregion
+
+@NgModule({
+ imports: [ BrowserModule, ContactModule ],
+ declarations: [ AppComponent, HighlightDirective, TitleComponent ],
+ providers: [ UserService ],
+ bootstrap: [ AppComponent ],
+})
+export class AppModule { }
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.module.3.ts b/public/docs/_examples/ngmodule/ts/src/app/app.module.3.ts
new file mode 100644
index 0000000000..8ca0a46d9a
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.module.3.ts
@@ -0,0 +1,30 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+/* App Root */
+import { AppComponent } from './app.component.3';
+import { HighlightDirective } from './highlight.directive';
+import { TitleComponent } from './title.component';
+import { UserService } from './user.service';
+
+/* Feature Modules */
+import { ContactModule } from './contact/contact.module.3';
+
+/* Routing Module */
+import { AppRoutingModule } from './app-routing.module.3';
+
+@NgModule({
+// #docregion imports
+ imports: [
+ BrowserModule,
+ ContactModule,
+ AppRoutingModule
+ ],
+// #enddocregion imports
+ providers: [ UserService ],
+ declarations: [ AppComponent, HighlightDirective, TitleComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/ngmodule/ts/src/app/app.module.ts b/public/docs/_examples/ngmodule/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..da03420804
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/app.module.ts
@@ -0,0 +1,40 @@
+// #docplaster
+// #docregion
+// #docregion v4
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+/* App Root */
+import { AppComponent } from './app.component';
+
+/* Feature Modules */
+import { ContactModule } from './contact/contact.module';
+import { CoreModule } from './core/core.module';
+
+/* Routing Module */
+import { AppRoutingModule } from './app-routing.module';
+
+@NgModule({
+ // #docregion import-for-root
+ imports: [
+ BrowserModule,
+ ContactModule,
+// #enddocregion v4
+// #enddocregion import-for-root
+/*
+// #docregion v4
+ CoreModule,
+// #enddocregion v4
+*/
+// #docregion import-for-root
+ CoreModule.forRoot({userName: 'Miss Marple'}),
+// #docregion v4
+ AppRoutingModule
+ ],
+ // #enddocregion import-for-root
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion v4
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/awesome.pipe.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/awesome.pipe.ts
new file mode 100644
index 0000000000..d6dce99901
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/awesome.pipe.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'awesome' })
+/** Precede the input string with the word "Awesome " */
+export class AwesomePipe implements PipeTransform {
+ transform(phrase: string) {
+ return phrase ? 'Awesome ' + phrase : '';
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact-routing.module.3.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact-routing.module.3.ts
new file mode 100644
index 0000000000..27dfc232b7
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact-routing.module.3.ts
@@ -0,0 +1,12 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { ContactComponent } from './contact.component.3';
+
+@NgModule({
+ imports: [RouterModule.forChild([
+ { path: 'contact', component: ContactComponent}
+ ])],
+ exports: [RouterModule]
+})
+export class ContactRoutingModule {}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact-routing.module.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact-routing.module.ts
new file mode 100644
index 0000000000..2fa81af5a9
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact-routing.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { ContactComponent } from './contact.component';
+
+// #docregion routing
+@NgModule({
+ imports: [RouterModule.forChild([
+ { path: 'contact', component: ContactComponent }
+ ])],
+ exports: [RouterModule]
+})
+export class ContactRoutingModule {}
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.3.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.3.ts
new file mode 100644
index 0000000000..ea6e9868ba
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.3.ts
@@ -0,0 +1,53 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { Contact, ContactService } from './contact.service';
+import { UserService } from '../user.service';
+
+@Component({
+ selector: 'app-contact',
+ templateUrl: './contact.component.html',
+ styleUrls: [ './contact.component.css' ]
+})
+export class ContactComponent implements OnInit {
+ contact: Contact;
+ contacts: Contact[];
+
+ msg = 'Loading contacts ...';
+ userName = '';
+
+ constructor(private contactService: ContactService, userService: UserService) {
+ this.userName = userService.userName;
+ }
+
+ ngOnInit() {
+ this.contactService.getContacts().then(contacts => {
+ this.msg = '';
+ this.contacts = contacts;
+ this.contact = contacts[0];
+ });
+ }
+
+ next() {
+ let ix = 1 + this.contacts.indexOf(this.contact);
+ if (ix >= this.contacts.length) { ix = 0; }
+ this.contact = this.contacts[ix];
+ }
+
+ onSubmit() {
+ // POST-DEMO TODO: do something like save it
+ this.displayMessage('Saved ' + this.contact.name);
+ }
+
+ newContact() {
+ this.displayMessage('New contact');
+ this.contact = {id: 42, name: ''};
+ this.contacts.push(this.contact);
+ }
+
+ /** Display a message briefly, then remove it. */
+ displayMessage(msg: string) {
+ this.msg = msg;
+ setTimeout(() => this.msg = '', 1500);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.css b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.css
new file mode 100644
index 0000000000..45e8f6e76d
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.css
@@ -0,0 +1,29 @@
+/* #docregion */
+.ng-valid[required] {
+ border-left: 5px solid #42A948; /* green */
+}
+
+.ng-invalid {
+ border-left: 5px solid #a94442; /* red */
+}
+
+.alert {
+ padding: 15px;
+ margin: 8px 0;
+ border: 1px solid transparent;
+ border-radius: 4px;
+}
+.alert-danger {
+ color: #a94442;
+ background-color: #f2dede;
+ border-color: #ebccd1;
+}
+
+.msg {
+ color: blue;
+ background-color: whitesmoke;
+ border: 1px solid transparent;
+ border-radius: 4px;
+ margin-bottom: 20px;
+}
+
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.html b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.html
new file mode 100644
index 0000000000..483480571e
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.html
@@ -0,0 +1,23 @@
+
+Contact of {{userName}}
+{{msg}}
+
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.ts
new file mode 100644
index 0000000000..d6dcbbc53f
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.component.ts
@@ -0,0 +1,54 @@
+// Exact copy except import UserService from core
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { Contact, ContactService } from './contact.service';
+import { UserService } from '../core/user.service';
+
+@Component({
+ selector: 'app-contact',
+ templateUrl: './contact.component.html',
+ styleUrls: [ './contact.component.css' ]
+})
+export class ContactComponent implements OnInit {
+ contact: Contact;
+ contacts: Contact[];
+
+ msg = 'Loading contacts ...';
+ userName = '';
+
+ constructor(private contactService: ContactService, userService: UserService) {
+ this.userName = userService.userName;
+ }
+
+ ngOnInit() {
+ this.contactService.getContacts().then(contacts => {
+ this.msg = '';
+ this.contacts = contacts;
+ this.contact = contacts[0];
+ });
+ }
+
+ next() {
+ let ix = 1 + this.contacts.indexOf(this.contact);
+ if (ix >= this.contacts.length) { ix = 0; }
+ this.contact = this.contacts[ix];
+ }
+
+ onSubmit() {
+ // POST-DEMO TODO: do something like save it
+ this.displayMessage('Saved ' + this.contact.name);
+ }
+
+ newContact() {
+ this.displayMessage('New contact');
+ this.contact = {id: 42, name: ''};
+ this.contacts.push(this.contact);
+ }
+
+ /** Display a message briefly, then remove it. */
+ displayMessage(msg: string) {
+ this.msg = msg;
+ setTimeout(() => this.msg = '', 1500);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.2.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.2.ts
new file mode 100644
index 0000000000..f347bd3b51
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.2.ts
@@ -0,0 +1,30 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { AwesomePipe } from './awesome.pipe';
+
+import
+// #enddocregion
+ { ContactComponent } from './contact.component.3';
+/*
+// #docregion
+ { ContactComponent } from './contact.component';
+// #enddocregion
+*/
+// #docregion
+import { ContactService } from './contact.service';
+import { HighlightDirective } from './highlight.directive';
+
+// #docregion class
+@NgModule({
+ imports: [ CommonModule, FormsModule ],
+ declarations: [ ContactComponent, HighlightDirective, AwesomePipe ],
+ exports: [ ContactComponent ],
+ providers: [ ContactService ]
+})
+export class ContactModule { }
+// #enddocregion class
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.3.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.3.ts
new file mode 100644
index 0000000000..ff70721b2f
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.3.ts
@@ -0,0 +1,22 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { AwesomePipe } from './awesome.pipe';
+
+import { ContactComponent } from './contact.component.3';
+import { ContactService } from './contact.service';
+import { HighlightDirective } from './highlight.directive';
+
+import { ContactRoutingModule } from './contact-routing.module.3';
+
+// #docregion class
+@NgModule({
+ imports: [ CommonModule, FormsModule, ContactRoutingModule ],
+ declarations: [ ContactComponent, HighlightDirective, AwesomePipe ],
+ providers: [ ContactService ]
+})
+export class ContactModule { }
+// #enddocregion class
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.ts
new file mode 100644
index 0000000000..9456de7654
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.module.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { SharedModule } from '../shared/shared.module';
+
+import { ContactComponent } from './contact.component';
+import { ContactService } from './contact.service';
+import { ContactRoutingModule } from './contact-routing.module';
+
+// #docregion class
+@NgModule({
+ imports: [ SharedModule, ContactRoutingModule ],
+ declarations: [ ContactComponent ],
+ providers: [ ContactService ]
+})
+export class ContactModule { }
+// #enddocregion class
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/contact.service.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.service.ts
new file mode 100644
index 0000000000..28b18bd84a
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/contact.service.ts
@@ -0,0 +1,29 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+export class Contact {
+ constructor(public id: number, public name: string) { }
+}
+
+const CONTACTS: Contact[] = [
+ new Contact(21, 'Sam Spade'),
+ new Contact(22, 'Nick Danger'),
+ new Contact(23, 'Nancy Drew')
+];
+
+const FETCH_LATENCY = 500;
+
+@Injectable()
+export class ContactService {
+
+ getContacts() {
+ return new Promise(resolve => {
+ setTimeout(() => { resolve(CONTACTS); }, FETCH_LATENCY);
+ });
+ }
+
+ getContact(id: number | string) {
+ return this.getContacts()
+ .then(heroes => heroes.find(hero => hero.id === +id));
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/contact/highlight.directive.ts b/public/docs/_examples/ngmodule/ts/src/app/contact/highlight.directive.ts
new file mode 100644
index 0000000000..64338b1377
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/contact/highlight.directive.ts
@@ -0,0 +1,18 @@
+/* tslint:disable */
+// Same directive name and selector as
+// HighlightDirective in parent AppModule
+// It selects for both input boxes and 'highlight' attr
+// and it highlights in blue instead of gold
+
+// #docregion
+import { Directive, ElementRef } from '@angular/core';
+
+@Directive({ selector: '[highlight], input' })
+/** Highlight the attached element or an InputElement in blue */
+export class HighlightDirective {
+ constructor(el: ElementRef) {
+ el.nativeElement.style.backgroundColor = 'powderblue';
+ console.log(
+ `* Contact highlight called for ${el.nativeElement.tagName}`);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/core/core.module.ts b/public/docs/_examples/ngmodule/ts/src/app/core/core.module.ts
new file mode 100644
index 0000000000..27cb9a2193
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/core/core.module.ts
@@ -0,0 +1,48 @@
+/* tslint:disable:member-ordering no-unused-variable */
+// #docplaster
+// #docregion
+// #docregion v4
+import {
+ ModuleWithProviders, NgModule,
+ Optional, SkipSelf } from '@angular/core';
+
+import { CommonModule } from '@angular/common';
+
+import { TitleComponent } from './title.component';
+import { UserService } from './user.service';
+// #enddocregion
+import { UserServiceConfig } from './user.service';
+
+// #docregion v4
+@NgModule({
+ imports: [ CommonModule ],
+ declarations: [ TitleComponent ],
+ exports: [ TitleComponent ],
+ providers: [ UserService ]
+})
+export class CoreModule {
+// #enddocregion v4
+
+ // #docregion ctor
+ constructor (@Optional() @SkipSelf() parentModule: CoreModule) {
+ if (parentModule) {
+ throw new Error(
+ 'CoreModule is already loaded. Import it in the AppModule only');
+ }
+ }
+ // #enddocregion ctor
+
+ // #docregion for-root
+ static forRoot(config: UserServiceConfig): ModuleWithProviders {
+ return {
+ ngModule: CoreModule,
+ providers: [
+ {provide: UserServiceConfig, useValue: config }
+ ]
+ };
+ }
+ // #enddocregion for-root
+// #docregion v4
+}
+// #enddocregion v4
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/core/title.component.html b/public/docs/_examples/ngmodule/ts/src/app/core/title.component.html
new file mode 100644
index 0000000000..8ebd08ae43
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/core/title.component.html
@@ -0,0 +1,6 @@
+
+{{title}} {{subtitle}}
+
+ Welcome, {{user}}
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/app/core/title.component.ts b/public/docs/_examples/ngmodule/ts/src/app/core/title.component.ts
new file mode 100644
index 0000000000..b52a57aa13
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/core/title.component.ts
@@ -0,0 +1,17 @@
+// Exact copy of app/title.component.ts except import UserService from shared
+import { Component, Input } from '@angular/core';
+import { UserService } from '../core/user.service';
+
+@Component({
+ selector: 'app-title',
+ templateUrl: './title.component.html',
+})
+export class TitleComponent {
+ @Input() subtitle = '';
+ title = 'Angular Modules';
+ user = '';
+
+ constructor(userService: UserService) {
+ this.user = userService.userName;
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/core/user.service.ts b/public/docs/_examples/ngmodule/ts/src/app/core/user.service.ts
new file mode 100644
index 0000000000..8fe839075e
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/core/user.service.ts
@@ -0,0 +1,32 @@
+// Crazy copy of the app/user.service
+// Proves that UserService is an app-wide singleton and only instantiated once
+// IFF shared.module follows the `forRoot` pattern
+//
+// If it didn't, a new instance of UserService would be created
+// after each lazy load and the userName would double up.
+
+import { Injectable, Optional } from '@angular/core';
+
+let nextId = 1;
+
+export class UserServiceConfig {
+ userName = 'Philip Marlowe';
+}
+
+@Injectable()
+export class UserService {
+ id = nextId++;
+ private _userName = 'Sherlock Holmes';
+
+ // #docregion ctor
+ constructor(@Optional() config: UserServiceConfig) {
+ if (config) { this._userName = config.userName; }
+ }
+ // #enddocregion ctor
+
+ get userName() {
+ // Demo: add a suffix if this service has been created more than once
+ const suffix = this.id > 1 ? ` times ${this.id}` : '';
+ return this._userName + suffix;
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-detail.component.ts b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-detail.component.ts
new file mode 100644
index 0000000000..9749029d62
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-detail.component.ts
@@ -0,0 +1,19 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+@Component({
+ template: `
+
Crisis Detail
+ Crisis id: {{id}}
+
+ Crisis List
+ `
+})
+export class CrisisDetailComponent implements OnInit {
+ id: number;
+ constructor(private route: ActivatedRoute) { }
+
+ ngOnInit() {
+ this.id = parseInt(this.route.snapshot.params['id'], 10);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-list.component.ts b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-list.component.ts
new file mode 100644
index 0000000000..ae459cdf1b
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-list.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Crisis,
+ CrisisService } from './crisis.service';
+
+@Component({
+ template: `
+ Crisis List
+
+ `
+})
+export class CrisisListComponent implements OnInit {
+ crisises: Promise;
+
+ constructor(private crisisService: CrisisService) { }
+
+ ngOnInit() {
+ this.crisises = this.crisisService.getCrises();
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-routing.module.ts b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-routing.module.ts
new file mode 100644
index 0000000000..c60efa8cb4
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis-routing.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { Routes,
+ RouterModule } from '@angular/router';
+
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+const routes: Routes = [
+ { path: '', redirectTo: 'list', pathMatch: 'full'},
+ { path: 'list', component: CrisisListComponent },
+ { path: ':id', component: CrisisDetailComponent }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class CrisisRoutingModule {}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis.module.ts b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis.module.ts
new file mode 100644
index 0000000000..f557bd6423
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis.module.ts
@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+import { CrisisService } from './crisis.service';
+import { CrisisRoutingModule } from './crisis-routing.module';
+
+@NgModule({
+ imports: [ CommonModule, CrisisRoutingModule ],
+ declarations: [ CrisisDetailComponent, CrisisListComponent ],
+ providers: [ CrisisService ]
+})
+export class CrisisModule {}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis.service.ts b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis.service.ts
new file mode 100644
index 0000000000..419ee19b36
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/crisis/crisis.service.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+
+export class Crisis {
+ constructor(public id: number, public name: string) { }
+}
+
+const CRISES: Crisis[] = [
+ new Crisis(1, 'Dragon Burning Cities'),
+ new Crisis(2, 'Sky Rains Great White Sharks'),
+ new Crisis(3, 'Giant Asteroid Heading For Earth'),
+ new Crisis(4, 'Procrastinators Meeting Delayed Again'),
+];
+
+const FETCH_LATENCY = 500;
+
+@Injectable()
+export class CrisisService {
+
+ getCrises() {
+ return new Promise(resolve => {
+ setTimeout(() => { resolve(CRISES); }, FETCH_LATENCY);
+ });
+ }
+
+ getCrisis(id: number | string) {
+ return this.getCrises()
+ .then(heroes => heroes.find(hero => hero.id === +id));
+ }
+
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero-detail.component.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-detail.component.ts
new file mode 100644
index 0000000000..1478ad350c
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-detail.component.ts
@@ -0,0 +1,31 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+
+import { Hero,
+ HeroService } from './hero.service';
+
+@Component({
+ template: `
+ Hero Detail
+
+
Id: {{hero.id}}
+
Name:
+
+
+
+
+ Hero List
+ `
+})
+export class HeroDetailComponent implements OnInit {
+ hero: Hero;
+
+ constructor(
+ private route: ActivatedRoute,
+ private heroService: HeroService) { }
+
+ ngOnInit() {
+ let id = parseInt(this.route.snapshot.params['id'], 10);
+ this.heroService.getHero(id).then(hero => this.hero = hero);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero-list.component.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-list.component.ts
new file mode 100644
index 0000000000..5a4e9ef0c4
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-list.component.ts
@@ -0,0 +1,21 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Hero,
+ HeroService } from './hero.service';
+
+@Component({
+ template: `
+ Hero List
+
+ `
+})
+export class HeroListComponent implements OnInit {
+ heroes: Promise;
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroes = this.heroService.getHeroes();
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero-routing.module.3.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-routing.module.3.ts
new file mode 100644
index 0000000000..588ffd94be
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-routing.module.3.ts
@@ -0,0 +1,23 @@
+import { NgModule } from '@angular/core';
+import { Routes,
+ RouterModule } from '@angular/router';
+
+import { HeroComponent } from './hero.component.3';
+import { HeroListComponent } from './hero-list.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+const routes: Routes = [
+ { path: '',
+ component: HeroComponent,
+ children: [
+ { path: '', component: HeroListComponent },
+ { path: ':id', component: HeroDetailComponent }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class HeroRoutingModule {}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero-routing.module.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-routing.module.ts
new file mode 100644
index 0000000000..d97aab3beb
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero-routing.module.ts
@@ -0,0 +1,23 @@
+import { NgModule } from '@angular/core';
+import { Routes,
+ RouterModule } from '@angular/router';
+
+import { HeroComponent } from './hero.component';
+import { HeroListComponent } from './hero-list.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+const routes: Routes = [
+ { path: '',
+ component: HeroComponent,
+ children: [
+ { path: '', component: HeroListComponent },
+ { path: ':id', component: HeroDetailComponent }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class HeroRoutingModule {}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero.component.3.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.component.3.ts
new file mode 100644
index 0000000000..d52bc253df
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.component.3.ts
@@ -0,0 +1,18 @@
+import { Component } from '@angular/core';
+
+import { HeroService } from './hero.service';
+import { UserService } from '../user.service';
+
+@Component({
+ template: `
+ Heroes of {{userName}}
+
+ `,
+ providers: [ HeroService ]
+})
+export class HeroComponent {
+ userName = '';
+ constructor(userService: UserService) {
+ this.userName = userService.userName;
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero.component.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.component.ts
new file mode 100644
index 0000000000..86338fb0ae
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.component.ts
@@ -0,0 +1,19 @@
+// Exact copy except import UserService from core
+import { Component } from '@angular/core';
+
+import { HeroService } from './hero.service';
+import { UserService } from '../core/user.service';
+
+@Component({
+ template: `
+ Heroes of {{userName}}
+
+ `,
+ providers: [ HeroService ]
+})
+export class HeroComponent {
+ userName = '';
+ constructor(userService: UserService) {
+ this.userName = userService.userName;
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero.module.3.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.module.3.ts
new file mode 100644
index 0000000000..c00f4eedd5
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.module.3.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { HeroComponent } from './hero.component.3';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroListComponent } from './hero-list.component';
+import { HighlightDirective } from './highlight.directive';
+import { HeroRoutingModule } from './hero-routing.module.3';
+
+// #docregion class
+@NgModule({
+ imports: [ CommonModule, FormsModule, HeroRoutingModule ],
+ declarations: [
+ HeroComponent, HeroDetailComponent, HeroListComponent,
+ HighlightDirective
+ ]
+})
+export class HeroModule { }
+// #enddocregion class
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero.module.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.module.ts
new file mode 100644
index 0000000000..98d7b76b00
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+
+import { SharedModule } from '../shared/shared.module';
+
+import { HeroComponent } from './hero.component';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroListComponent } from './hero-list.component';
+import { HeroRoutingModule } from './hero-routing.module';
+
+@NgModule({
+ imports: [ SharedModule, HeroRoutingModule ],
+ declarations: [
+ HeroComponent, HeroDetailComponent, HeroListComponent,
+ ]
+})
+export class HeroModule { }
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/hero.service.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.service.ts
new file mode 100644
index 0000000000..bb7ff5fa5c
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/hero.service.ts
@@ -0,0 +1,32 @@
+import { Injectable } from '@angular/core';
+
+export class Hero {
+ constructor(public id: number, public name: string) { }
+}
+
+const HEROES: Hero[] = [
+ new Hero(11, 'Mr. Nice'),
+ new Hero(12, 'Narco'),
+ new Hero(13, 'Bombasto'),
+ new Hero(14, 'Celeritas'),
+ new Hero(15, 'Magneta'),
+ new Hero(16, 'RubberMan')
+];
+
+const FETCH_LATENCY = 500;
+
+@Injectable()
+export class HeroService {
+
+ getHeroes() {
+ return new Promise(resolve => {
+ setTimeout(() => { resolve(HEROES); }, FETCH_LATENCY);
+ });
+ }
+
+ getHero(id: number | string) {
+ return this.getHeroes()
+ .then(heroes => heroes.find(hero => hero.id === +id));
+ }
+
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/hero/highlight.directive.ts b/public/docs/_examples/ngmodule/ts/src/app/hero/highlight.directive.ts
new file mode 100644
index 0000000000..d7e39afd05
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/hero/highlight.directive.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Directive, ElementRef } from '@angular/core';
+
+// Same directive name and selector as
+// HighlightDirective in parent AppRootModule
+// It selects for both input boxes and 'highlight' attr
+// and it highlights in beige instead of yellow
+@Directive({ selector: '[highlight]' })
+export class HighlightDirective {
+ constructor(el: ElementRef) {
+ el.nativeElement.style.backgroundColor = 'beige';
+ console.log(`* Hero highlight called for ${el.nativeElement.tagName}`);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/highlight.directive.ts b/public/docs/_examples/ngmodule/ts/src/app/highlight.directive.ts
new file mode 100644
index 0000000000..df67a3ae89
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/highlight.directive.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Directive, ElementRef } from '@angular/core';
+
+@Directive({ selector: '[highlight]' })
+/** Highlight the attached element in gold */
+export class HighlightDirective {
+ constructor(el: ElementRef) {
+ el.nativeElement.style.backgroundColor = 'gold';
+ console.log(
+ `* AppRoot highlight called for ${el.nativeElement.tagName}`);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/shared/awesome.pipe.ts b/public/docs/_examples/ngmodule/ts/src/app/shared/awesome.pipe.ts
new file mode 100644
index 0000000000..a1a0001d24
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/shared/awesome.pipe.ts
@@ -0,0 +1,10 @@
+// Exact copy of contact.awesome.pipe
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'awesome' })
+/** Precede the input string with the word "Awesome " */
+export class AwesomePipe implements PipeTransform {
+ transform(phrase: string) {
+ return phrase ? 'Awesome ' + phrase : '';
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/shared/highlight.directive.ts b/public/docs/_examples/ngmodule/ts/src/app/shared/highlight.directive.ts
new file mode 100644
index 0000000000..63fbd4e488
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/shared/highlight.directive.ts
@@ -0,0 +1,13 @@
+/* tslint:disable */
+// Exact copy of contact/highlight.directive except for color and message
+import { Directive, ElementRef } from '@angular/core';
+
+@Directive({ selector: '[highlight], input' })
+/** Highlight the attached element or an InputElement in gray */
+export class HighlightDirective {
+ constructor(el: ElementRef) {
+ el.nativeElement.style.backgroundColor = 'lightgray';
+ console.log(
+ `* Shared highlight called for ${el.nativeElement.tagName}`);
+ }
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/app/shared/shared.module.ts b/public/docs/_examples/ngmodule/ts/src/app/shared/shared.module.ts
new file mode 100644
index 0000000000..2da7d7b2a5
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/shared/shared.module.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { AwesomePipe } from './awesome.pipe';
+import { HighlightDirective } from './highlight.directive';
+
+// #docregion module
+@NgModule({
+ imports: [ CommonModule ],
+ declarations: [ AwesomePipe, HighlightDirective ],
+ exports: [ AwesomePipe, HighlightDirective,
+ CommonModule, FormsModule ]
+})
+export class SharedModule { }
+// #enddocregion module
+// #enddocregion
diff --git a/public/docs/_examples/ngmodule/ts/src/app/title.component.html b/public/docs/_examples/ngmodule/ts/src/app/title.component.html
new file mode 100644
index 0000000000..3db364cd4b
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/title.component.html
@@ -0,0 +1,10 @@
+
+
+{{title}} {{subtitle}}
+
+
+
+ Welcome, {{user}}
+
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/app/title.component.ts b/public/docs/_examples/ngmodule/ts/src/app/title.component.ts
new file mode 100644
index 0000000000..9a2ef1c4b8
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/title.component.ts
@@ -0,0 +1,24 @@
+// #docplaster
+// #docregion
+// #docregion v1
+import { Component, Input } from '@angular/core';
+// #enddocregion v1
+import { UserService } from './user.service';
+// #docregion v1
+
+@Component({
+ selector: 'app-title',
+ templateUrl: './title.component.html',
+})
+export class TitleComponent {
+ @Input() subtitle = '';
+ title = 'Angular Modules';
+// #enddocregion v1
+ user = '';
+
+ constructor(userService: UserService) {
+ this.user = userService.userName;
+ }
+// #docregion v1
+}
+// #enddocregion v1
diff --git a/public/docs/_examples/ngmodule/ts/src/app/user.service.ts b/public/docs/_examples/ngmodule/ts/src/app/user.service.ts
new file mode 100644
index 0000000000..7d996b26fa
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/app/user.service.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+/** Dummy version of an authenticated user service */
+export class UserService {
+ userName = 'Sherlock Holmes';
+}
diff --git a/public/docs/_examples/ngmodule/ts/src/index.0.html b/public/docs/_examples/ngmodule/ts/src/index.0.html
new file mode 100644
index 0000000000..2281b9fecb
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/index.0.html
@@ -0,0 +1,24 @@
+
+
+
+
+ NgModule Minimal
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/index.1.html b/public/docs/_examples/ngmodule/ts/src/index.1.html
new file mode 100644
index 0000000000..65f7991a26
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/index.1.html
@@ -0,0 +1,24 @@
+
+
+
+
+ NgModule - Contact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/index.1b.html b/public/docs/_examples/ngmodule/ts/src/index.1b.html
new file mode 100644
index 0000000000..b4a89d6549
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/index.1b.html
@@ -0,0 +1,24 @@
+
+
+
+
+ NgModule - Contact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/index.2.html b/public/docs/_examples/ngmodule/ts/src/index.2.html
new file mode 100644
index 0000000000..72c545f1f3
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/index.2.html
@@ -0,0 +1,24 @@
+
+
+
+
+ NgModule - Contact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/index.3.html b/public/docs/_examples/ngmodule/ts/src/index.3.html
new file mode 100644
index 0000000000..ec55dd984e
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/index.3.html
@@ -0,0 +1,24 @@
+
+
+
+
+ NgModule - Contact
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/index.html b/public/docs/_examples/ngmodule/ts/src/index.html
new file mode 100644
index 0000000000..9fc50c02a5
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+ NgModule Deluxe
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/ngmodule/ts/src/main-static.ts b/public/docs/_examples/ngmodule/ts/src/main-static.ts
new file mode 100644
index 0000000000..a1bcf466d2
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main-static.ts
@@ -0,0 +1,13 @@
+// #docplaster
+/*
+// #docregion
+// The browser platform without a compiler
+import { platformBrowser } from '@angular/platform-browser';
+
+// The app module factory produced by the static offline compiler
+import { AppModuleNgFactory } from './app/app.module.ngfactory';
+
+// Launch with the app module factory.
+platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
+// #enddocregion
+*/
diff --git a/public/docs/_examples/ngmodule/ts/src/main.0.ts b/public/docs/_examples/ngmodule/ts/src/main.0.ts
new file mode 100644
index 0000000000..6d1d712f07
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main.0.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module.0';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/ngmodule/ts/src/main.1.ts b/public/docs/_examples/ngmodule/ts/src/main.1.ts
new file mode 100644
index 0000000000..48129f4cc9
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main.1.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module.1';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/ngmodule/ts/src/main.1b.ts b/public/docs/_examples/ngmodule/ts/src/main.1b.ts
new file mode 100644
index 0000000000..7a7bfae22f
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main.1b.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module.1b';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/ngmodule/ts/src/main.2.ts b/public/docs/_examples/ngmodule/ts/src/main.2.ts
new file mode 100644
index 0000000000..c535e2e775
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main.2.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module.2';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/ngmodule/ts/src/main.3.ts b/public/docs/_examples/ngmodule/ts/src/main.3.ts
new file mode 100644
index 0000000000..72f21dd142
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main.3.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module.3';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/ngmodule/ts/src/main.ts b/public/docs/_examples/ngmodule/ts/src/main.ts
new file mode 100644
index 0000000000..c8424d8c4c
--- /dev/null
+++ b/public/docs/_examples/ngmodule/ts/src/main.ts
@@ -0,0 +1,9 @@
+// #docregion
+// The browser platform with a compiler
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+// The app module
+import { AppModule } from './app/app.module';
+
+// Compile and launch the module
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json
new file mode 100644
index 0000000000..41dc5e5b56
--- /dev/null
+++ b/public/docs/_examples/package.json
@@ -0,0 +1,89 @@
+{
+ "name": "angular-examples-master",
+ "version": "1.0.0",
+ "private": true,
+ "description": "Master package.json, the superset of all dependencies for all of the _example package.json files. See _boilerplate/package.json for example npm scripts.",
+ "scripts": {
+ "http-server": "http-server",
+ "protractor": "protractor",
+ "webdriver:update": "webdriver-manager update --standalone false --gecko false"
+ },
+ "keywords": [],
+ "author": "",
+ "license": "MIT",
+ "dependencies": {
+ "@angular/animations": "~4.0.0",
+ "@angular/common": "~4.0.0",
+ "@angular/compiler": "~4.0.0",
+ "@angular/compiler-cli": "~4.0.0",
+ "@angular/core": "~4.0.0",
+ "@angular/forms": "~4.0.0",
+ "@angular/http": "~4.0.0",
+ "@angular/platform-browser": "~4.0.0",
+ "@angular/platform-browser-dynamic": "~4.0.0",
+ "@angular/platform-server": "~4.0.0",
+ "@angular/router": "~4.0.0",
+ "@angular/tsc-wrapped": "~4.0.0",
+ "@angular/upgrade": "~4.0.0",
+ "angular-in-memory-web-api": "~0.3.1",
+ "core-js": "^2.4.1",
+ "rxjs": "5.0.1",
+ "systemjs": "0.19.39",
+ "zone.js": "^0.8.4"
+ },
+ "devDependencies": {
+ "@angular/cli": "^1.0.0",
+ "@types/angular": "^1.5.16",
+ "@types/angular-animate": "^1.5.5",
+ "@types/angular-cookies": "^1.4.2",
+ "@types/angular-mocks": "^1.5.5",
+ "@types/angular-resource": "^1.5.6",
+ "@types/angular-route": "^1.3.2",
+ "@types/angular-sanitize": "^1.3.3",
+ "@types/jasmine": "2.5.36",
+ "@types/node": "^6.0.45",
+ "angular2-template-loader": "^0.6.0",
+ "awesome-typescript-loader": "^3.0.4",
+ "babel-cli": "^6.16.0",
+ "babel-preset-angular2": "^0.0.2",
+ "babel-preset-es2015": "^6.16.0",
+ "canonical-path": "0.0.2",
+ "concurrently": "^3.0.0",
+ "css-loader": "^0.26.1",
+ "extract-text-webpack-plugin": "2.0.0-beta.5",
+ "file-loader": "^0.9.0",
+ "html-loader": "^0.4.3",
+ "html-webpack-plugin": "^2.16.1",
+ "http-server": "^0.9.0",
+ "jasmine": "~2.4.1",
+ "jasmine-core": "~2.4.1",
+ "karma": "^1.3.0",
+ "karma-chrome-launcher": "^2.0.0",
+ "karma-cli": "^1.0.1",
+ "karma-jasmine": "^1.0.2",
+ "karma-jasmine-html-reporter": "^0.2.2",
+ "karma-phantomjs-launcher": "^1.0.2",
+ "karma-sourcemap-loader": "^0.3.7",
+ "karma-webpack": "^2.0.1",
+ "lite-server": "^2.2.2",
+ "lodash": "^4.16.2",
+ "null-loader": "^0.1.1",
+ "phantomjs-prebuilt": "^2.1.7",
+ "protractor": "~4.0.14",
+ "raw-loader": "^0.5.1",
+ "rimraf": "^2.5.4",
+ "rollup": "^0.41.6",
+ "rollup-plugin-commonjs": "^8.0.2",
+ "rollup-plugin-node-resolve": "2.0.0",
+ "rollup-plugin-uglify": "^1.0.1",
+ "source-map-explorer": "^1.3.2",
+ "style-loader": "^0.13.1",
+ "ts-node": "^1.3.0",
+ "tslint": "^3.15.1",
+ "typescript": "~2.2.0",
+ "webpack": "2.2.1",
+ "webpack-dev-server": "2.4.1",
+ "webpack-merge": "^3.0.0"
+ },
+ "repository": {}
+}
diff --git a/public/docs/_examples/pipes/e2e-spec.ts b/public/docs/_examples/pipes/e2e-spec.ts
new file mode 100644
index 0000000000..00277e3657
--- /dev/null
+++ b/public/docs/_examples/pipes/e2e-spec.ts
@@ -0,0 +1,111 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Pipes', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('should open correctly', function () {
+ expect(element.all(by.tagName('h1')).get(0).getText()).toEqual('Pipes');
+ expect(element(by.css('hero-birthday p')).getText()).toEqual(`The hero's birthday is Apr 15, 1988`);
+ });
+
+ it('should show 4 heroes', function () {
+ expect(element.all(by.css('hero-list div')).count()).toEqual(4);
+ });
+
+ it('should show a familiar hero in json', function () {
+ expect(element(by.cssContainingText('hero-list p', 'Heroes as JSON')).getText()).toContain('Bombasto');
+ });
+
+ it('should show alternate birthday formats', function () {
+ expect(element(by.cssContainingText('my-app > p', `The hero's birthday is Apr 15, 1988`)).isDisplayed()).toBe(true);
+ expect(element(by.cssContainingText('my-app > p', `The hero's birthday is 04/15/88`)).isDisplayed()).toBe(true);
+ });
+
+ it('should be able to toggle birthday formats', function () {
+ let birthDayEle = element(by.css('hero-birthday2 > p'));
+ expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/1988`);
+ let buttonEle = element(by.cssContainingText('hero-birthday2 > button', 'Toggle Format'));
+ expect(buttonEle.isDisplayed()).toBe(true);
+ buttonEle.click().then(function() {
+ expect(birthDayEle.getText()).toEqual(`The hero's birthday is Friday, April 15, 1988`);
+ });
+ });
+
+ it('should be able to chain and compose pipes', function () {
+ let chainedPipeEles = element.all(by.cssContainingText('my-app p', `The chained hero's`));
+ expect(chainedPipeEles.count()).toBe(3, 'should have 3 chained pipe examples');
+ expect(chainedPipeEles.get(0).getText()).toContain('APR 15, 1988');
+ expect(chainedPipeEles.get(1).getText()).toContain('FRIDAY, APRIL 15, 1988');
+ expect(chainedPipeEles.get(2).getText()).toContain('FRIDAY, APRIL 15, 1988');
+ });
+
+ it('should be able to use ExponentialStrengthPipe pipe', function () {
+ let ele = element(by.css('power-booster p'));
+ expect(ele.getText()).toContain('Super power boost: 1024');
+ });
+
+ it('should be able to use the exponential calculator', function () {
+ let eles = element.all(by.css('power-boost-calculator input'));
+ let baseInputEle = eles.get(0);
+ let factorInputEle = eles.get(1);
+ let outputEle = element(by.css('power-boost-calculator p'));
+ baseInputEle.clear().then(function() {
+ baseInputEle.sendKeys('7');
+ return factorInputEle.clear();
+ }).then(function() {
+ factorInputEle.sendKeys('3');
+ expect(outputEle.getText()).toContain('343');
+ });
+ });
+
+
+ it('should support flying heroes (pure) ', function () {
+ let nameEle = element(by.css('flying-heroes input[type="text"]'));
+ let canFlyCheckEle = element(by.css('flying-heroes #can-fly'));
+ let mutateCheckEle = element(by.css('flying-heroes #mutate'));
+ let resetEle = element(by.css('flying-heroes button'));
+ let flyingHeroesEle = element.all(by.css('flying-heroes #flyers div'));
+
+ expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
+ expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');
+ expect(flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly');
+
+ nameEle.sendKeys('test1\n');
+ expect(flyingHeroesEle.count()).toEqual(2, 'no change while mutating array');
+ mutateCheckEle.click().then(function() {
+ nameEle.sendKeys('test2\n');
+ expect(flyingHeroesEle.count()).toEqual(4, 'not mutating; should see both adds');
+ expect(flyingHeroesEle.get(2).getText()).toContain('test1');
+ expect(flyingHeroesEle.get(3).getText()).toContain('test2');
+ return resetEle.click();
+ })
+ .then(function() {
+ expect(flyingHeroesEle.count()).toEqual(2, 'reset should restore orginal flying heroes');
+ });
+ });
+
+
+ it('should support flying heroes (impure) ', function () {
+ let nameEle = element(by.css('flying-heroes-impure input[type="text"]'));
+ let canFlyCheckEle = element(by.css('flying-heroes-impure #can-fly'));
+ let mutateCheckEle = element(by.css('flying-heroes-impure #mutate'));
+ let flyingHeroesEle = element.all(by.css('flying-heroes-impure #flyers div'));
+
+ expect(canFlyCheckEle.getAttribute('checked')).toEqual('true', 'should default to "can fly"');
+ expect(mutateCheckEle.getAttribute('checked')).toEqual('true', 'should default to mutating array');
+ expect(flyingHeroesEle.count()).toEqual(2, 'only two of the original heroes can fly');
+
+ nameEle.sendKeys('test1\n');
+ expect(flyingHeroesEle.count()).toEqual(3, 'new flying hero should show in mutating array');
+ });
+
+ it('should show an async hero message', function () {
+ expect(element.all(by.tagName('hero-message')).get(0).getText()).toContain('hero');
+ });
+
+});
diff --git a/public/docs/_examples/pipes/ts/example-config.json b/public/docs/_examples/pipes/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/pipes/ts/plnkr.json b/public/docs/_examples/pipes/ts/plnkr.json
new file mode 100644
index 0000000000..3c07266109
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/plnkr.json
@@ -0,0 +1,8 @@
+{
+ "description": "Pipes",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js"],
+ "tags": ["pipe"]
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/app.component.html b/public/docs/_examples/pipes/ts/src/app/app.component.html
new file mode 100644
index 0000000000..a27d587fcd
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/app.component.html
@@ -0,0 +1,83 @@
+
+Pipes
+Happy Birthday v1
+Birthday DatePipe
+Happy Birthday v2
+Birthday Pipe Chaining
+Power Booster custom pipe
+Power Boost Calculator custom pipe with params
+Flying Heroes filter pipe (pure)
+Flying Heroes filter pipe (impure)
+Async Hero Message and AsyncPipe
+Hero List with caching FetchJsonPipe
+
+
+
+
+Hero Birthday v1
+
+
+
+
+Birthday DatePipe
+
+The hero's birthday is {{ birthday | date }}
+
+
+
+The hero's birthday is {{ birthday | date:"MM/dd/yy" }}
+
+
+
+
+Hero Birthday v2
+
+
+
+
+Birthday Pipe Chaining
+
+
+ The chained hero's birthday is
+ {{ birthday | date | uppercase}}
+
+
+
+
+
+ The chained hero's birthday is
+ {{ birthday | date:'fullDate' | uppercase}}
+
+
+
+
+ The chained hero's birthday is
+ {{ ( birthday | date:'fullDate' ) | uppercase}}
+
+
+
+
+
+
+
+
+loading
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/pipes/ts/src/app/app.component.ts b/public/docs/_examples/pipes/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..090eae7766
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/app.component.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent {
+ birthday = new Date(1988, 3, 15); // April 15, 1988
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/app.module.ts b/public/docs/_examples/pipes/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..89a3a29505
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/app.module.ts
@@ -0,0 +1,48 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { HttpModule } from '@angular/http';
+
+import { AppComponent } from './app.component';
+import {
+ FlyingHeroesComponent,
+ FlyingHeroesImpureComponent
+} from './flying-heroes.component';
+import { HeroAsyncMessageComponent } from './hero-async-message.component';
+import { HeroBirthdayComponent } from './hero-birthday1.component';
+import { HeroBirthday2Component } from './hero-birthday2.component';
+import { HeroListComponent } from './hero-list.component';
+import { PowerBoosterComponent } from './power-booster.component';
+import { PowerBoostCalculatorComponent } from './power-boost-calculator.component';
+import {
+ FlyingHeroesPipe,
+ FlyingHeroesImpurePipe
+} from './flying-heroes.pipe';
+import { FetchJsonPipe } from './fetch-json.pipe';
+import { ExponentialStrengthPipe } from './exponential-strength.pipe';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule
+ ],
+ declarations: [
+ AppComponent,
+ FlyingHeroesComponent,
+ FlyingHeroesImpureComponent,
+ HeroAsyncMessageComponent,
+ HeroBirthdayComponent,
+ HeroBirthday2Component,
+ HeroListComponent,
+ PowerBoosterComponent,
+ PowerBoostCalculatorComponent,
+ FlyingHeroesPipe,
+ FlyingHeroesImpurePipe,
+ FetchJsonPipe,
+ ExponentialStrengthPipe
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/pipes/ts/src/app/exponential-strength.pipe.ts b/public/docs/_examples/pipes/ts/src/app/exponential-strength.pipe.ts
new file mode 100644
index 0000000000..0a703d7016
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/exponential-strength.pipe.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { Pipe, PipeTransform } from '@angular/core';
+/*
+ * Raise the value exponentially
+ * Takes an exponent argument that defaults to 1.
+ * Usage:
+ * value | exponentialStrength:exponent
+ * Example:
+ * {{ 2 | exponentialStrength:10}}
+ * formats to: 1024
+*/
+@Pipe({name: 'exponentialStrength'})
+export class ExponentialStrengthPipe implements PipeTransform {
+ transform(value: number, exponent: string): number {
+ let exp = parseFloat(exponent);
+ return Math.pow(value, isNaN(exp) ? 1 : exp);
+ }
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/fetch-json.pipe.ts b/public/docs/_examples/pipes/ts/src/app/fetch-json.pipe.ts
new file mode 100644
index 0000000000..4d56e865f3
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/fetch-json.pipe.ts
@@ -0,0 +1,30 @@
+// #docregion
+import { Pipe, PipeTransform } from '@angular/core';
+import { Http } from '@angular/http';
+
+import 'rxjs/add/operator/map';
+
+// #docregion pipe-metadata
+@Pipe({
+ name: 'fetch',
+ pure: false
+})
+// #enddocregion pipe-metadata
+export class FetchJsonPipe implements PipeTransform {
+ private cachedData: any = null;
+ private cachedUrl = '';
+
+ constructor(private http: Http) { }
+
+ transform(url: string): any {
+ if (url !== this.cachedUrl) {
+ this.cachedData = null;
+ this.cachedUrl = url;
+ this.http.get(url)
+ .map( result => result.json() )
+ .subscribe( result => this.cachedData = result );
+ }
+
+ return this.cachedData;
+ }
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/flying-heroes-impure.component.html b/public/docs/_examples/pipes/ts/src/app/flying-heroes-impure.component.html
new file mode 100644
index 0000000000..66bd86f81c
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/flying-heroes-impure.component.html
@@ -0,0 +1,38 @@
+
+
+{{title}}
+
+
+New hero:
+
+
+ can fly
+
+
+ Mutate array
+
+ Reset
+
+
+
+Heroes who fly (piped)
+
+
+
+ {{hero.name}}
+
+
+
+
+All Heroes (no pipe)
+
+
+
+
+ {{hero.name}}
+
+
+
+
diff --git a/public/docs/_examples/pipes/ts/src/app/flying-heroes.component.html b/public/docs/_examples/pipes/ts/src/app/flying-heroes.component.html
new file mode 100644
index 0000000000..93e635b662
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/flying-heroes.component.html
@@ -0,0 +1,38 @@
+
+
+{{title}}
+
+
+New hero:
+
+
+ can fly
+
+
+ Mutate array
+
+ Reset
+
+
+
+Heroes who fly (piped)
+
+
+
+ {{hero.name}}
+
+
+
+
+All Heroes (no pipe)
+
+
+
+
+ {{hero.name}}
+
+
+
+
diff --git a/public/docs/_examples/pipes/ts/src/app/flying-heroes.component.ts b/public/docs/_examples/pipes/ts/src/app/flying-heroes.component.ts
new file mode 100644
index 0000000000..e4abb09a2f
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/flying-heroes.component.ts
@@ -0,0 +1,61 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+import { HEROES } from './heroes';
+
+@Component({
+ selector: 'flying-heroes',
+ templateUrl: './flying-heroes.component.html',
+ styles: ['#flyers, #all {font-style: italic}']
+})
+// #docregion v1
+export class FlyingHeroesComponent {
+ heroes: any[] = [];
+ canFly = true;
+// #enddocregion v1
+ mutate = true;
+ title = 'Flying Heroes (pure pipe)';
+
+// #docregion v1
+ constructor() { this.reset(); }
+
+ addHero(name: string) {
+ name = name.trim();
+ if (!name) { return; }
+ let hero = {name, canFly: this.canFly};
+// #enddocregion v1
+ if (this.mutate) {
+ // Pure pipe won't update display because heroes array reference is unchanged
+ // Impure pipe will display
+// #docregion v1
+// #docregion push
+ this.heroes.push(hero);
+// #enddocregion push
+// #enddocregion v1
+ } else {
+ // Pipe updates display because heroes array is a new object
+// #docregion concat
+ this.heroes = this.heroes.concat(hero);
+// #enddocregion concat
+ }
+// #docregion v1
+ }
+
+ reset() { this.heroes = HEROES.slice(); }
+}
+// #enddocregion v1
+
+////// Identical except for impure pipe //////
+// #docregion impure-component
+@Component({
+ selector: 'flying-heroes-impure',
+ templateUrl: './flying-heroes-impure.component.html',
+// #enddocregion impure-component
+ styles: ['.flyers, .all {font-style: italic}'],
+// #docregion impure-component
+})
+export class FlyingHeroesImpureComponent extends FlyingHeroesComponent {
+ title = 'Flying Heroes (impure pipe)';
+}
+// #enddocregion impure-component
diff --git a/public/docs/_examples/pipes/ts/src/app/flying-heroes.pipe.ts b/public/docs/_examples/pipes/ts/src/app/flying-heroes.pipe.ts
new file mode 100644
index 0000000000..87db9c277e
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/flying-heroes.pipe.ts
@@ -0,0 +1,27 @@
+/* tslint:disable use-pipe-transform-interface */
+// #docregion
+// #docregion pure
+import { Pipe, PipeTransform } from '@angular/core';
+
+import { Flyer } from './heroes';
+
+@Pipe({ name: 'flyingHeroes' })
+export class FlyingHeroesPipe implements PipeTransform {
+ transform(allHeroes: Flyer[]) {
+ // #docregion filter
+ return allHeroes.filter(hero => hero.canFly);
+ // #enddocregion filter
+ }
+}
+// #enddocregion pure
+
+/////// Identical except for the pure flag
+// #docregion impure
+// #docregion pipe-decorator
+@Pipe({
+ name: 'flyingHeroesImpure',
+ pure: false
+})
+// #enddocregion pipe-decorator
+export class FlyingHeroesImpurePipe extends FlyingHeroesPipe {}
+// #enddocregion impure
diff --git a/public/docs/_examples/pipes/ts/src/app/hero-async-message.component.ts b/public/docs/_examples/pipes/ts/src/app/hero-async-message.component.ts
new file mode 100644
index 0000000000..d5bbd9fb0e
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/hero-async-message.component.ts
@@ -0,0 +1,38 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/interval';
+import 'rxjs/add/operator/map';
+import 'rxjs/add/operator/take';
+
+@Component({
+ selector: 'hero-message',
+ template: `
+ Async Hero Message and AsyncPipe
+ Message: {{ message$ | async }}
+ Resend `,
+})
+export class HeroAsyncMessageComponent {
+ message$: Observable;
+
+ private messages = [
+ 'You are my hero!',
+ 'You are the best hero!',
+ 'Will you be my hero?'
+ ];
+
+ constructor() { this.resend(); }
+
+ resend() {
+ this.message$ = Observable.interval(500)
+ .map(i => this.messages[i])
+ .take(this.messages.length);
+ }
+}
+// #enddocregion
+
+// Alternative message$ formula:
+// this.message$ = Observable.fromArray(this.messages)
+// .map(message => Observable.timer(500).map(() => message))
+// .concatAll();
diff --git a/public/docs/_examples/pipes/ts/src/app/hero-birthday1.component.ts b/public/docs/_examples/pipes/ts/src/app/hero-birthday1.component.ts
new file mode 100644
index 0000000000..a670a807a8
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/hero-birthday1.component.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'hero-birthday',
+ // #docregion hero-birthday-template
+ template: `The hero's birthday is {{ birthday | date }}
`
+ // #enddocregion hero-birthday-template
+})
+export class HeroBirthdayComponent {
+ birthday = new Date(1988, 3, 15); // April 15, 1988
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/hero-birthday2.component.ts b/public/docs/_examples/pipes/ts/src/app/hero-birthday2.component.ts
new file mode 100644
index 0000000000..87481aa121
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/hero-birthday2.component.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'hero-birthday2',
+ // #docregion template
+ template: `
+ The hero's birthday is {{ birthday | date:format }}
+ Toggle Format
+ `
+ // #enddocregion template
+})
+// #docregion class
+export class HeroBirthday2Component {
+ birthday = new Date(1988, 3, 15); // April 15, 1988
+ toggle = true; // start with true == shortDate
+
+ get format() { return this.toggle ? 'shortDate' : 'fullDate'; }
+ toggleFormat() { this.toggle = !this.toggle; }
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/hero-list.component.ts b/public/docs/_examples/pipes/ts/src/app/hero-list.component.ts
new file mode 100644
index 0000000000..df231120ce
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/hero-list.component.ts
@@ -0,0 +1,17 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'hero-list',
+ template: `
+ Heroes from JSON File
+
+
+ {{hero.name}}
+
+
+ Heroes as JSON:
+ {{'heroes.json' | fetch | json}}
+
`
+})
+export class HeroListComponent { }
diff --git a/public/docs/_examples/pipes/ts/src/app/heroes.ts b/public/docs/_examples/pipes/ts/src/app/heroes.ts
new file mode 100644
index 0000000000..b2edabe0da
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/heroes.ts
@@ -0,0 +1,7 @@
+export interface Flyer { canFly: boolean; }
+export const HEROES = [
+ {name: 'Windstorm', canFly: true},
+ {name: 'Bombasto', canFly: false},
+ {name: 'Magneto', canFly: false},
+ {name: 'Tornado', canFly: true}
+];
diff --git a/public/docs/_examples/pipes/ts/src/app/power-boost-calculator.component.ts b/public/docs/_examples/pipes/ts/src/app/power-boost-calculator.component.ts
new file mode 100644
index 0000000000..e65e29ad4c
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/power-boost-calculator.component.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'power-boost-calculator',
+ template: `
+ Power Boost Calculator
+ Normal power:
+ Boost factor:
+
+ Super Hero Power: {{power | exponentialStrength: factor}}
+
+ `
+})
+export class PowerBoostCalculatorComponent {
+ power = 5;
+ factor = 1;
+}
diff --git a/public/docs/_examples/pipes/ts/src/app/power-booster.component.ts b/public/docs/_examples/pipes/ts/src/app/power-booster.component.ts
new file mode 100644
index 0000000000..08e3e24c7b
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/app/power-booster.component.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'power-booster',
+ template: `
+ Power Booster
+ Super power boost: {{2 | exponentialStrength: 10}}
+ `
+})
+export class PowerBoosterComponent { }
diff --git a/public/docs/_examples/pipes/ts/src/heroes.json b/public/docs/_examples/pipes/ts/src/heroes.json
new file mode 100644
index 0000000000..e5f0b77262
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/heroes.json
@@ -0,0 +1,6 @@
+[
+ {"name": "Windstorm", "canFly": true},
+ {"name": "Bombasto", "canFly": false},
+ {"name": "Magneto", "canFly": false},
+ {"name": "Tornado", "canFly": true}
+]
diff --git a/public/docs/_examples/pipes/ts/src/index.html b/public/docs/_examples/pipes/ts/src/index.html
new file mode 100644
index 0000000000..fedb5c2e5e
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/index.html
@@ -0,0 +1,26 @@
+
+
+
+ Pipes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ my-app loading ...
+
+
+
diff --git a/public/docs/_examples/pipes/ts/src/main.ts b/public/docs/_examples/pipes/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/pipes/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/protractor-conf.js b/public/docs/_examples/protractor-conf.js
deleted file mode 100644
index d341244fa6..0000000000
--- a/public/docs/_examples/protractor-conf.js
+++ /dev/null
@@ -1,25 +0,0 @@
-exports.config = {
- onPrepare: function() {
- patchProtractorWait(browser);
- },
- seleniumAddress: 'http://localhost:4444/wd/hub',
- baseUrl: 'http://localhost:8080/',
- specs: [
- 'quickstart/protractor-spec.js',
- 'gettingstarted/protractor-spec.js'
- ]
-};
-
-// Disable waiting for Angular as we don't have an integration layer yet...
-// TODO(tbosch): Implement a proper debugging API for Ng2.0, remove this here
-// and the sleeps in all tests.
-function patchProtractorWait(browser) {
- browser.ignoreSynchronization = true;
- var _get = browser.get;
- var sleepInterval = process.env.TRAVIS || process.env.JENKINS_URL ? 14000 : 8000;
- browser.get = function() {
- var result = _get.apply(this, arguments);
- browser.sleep(sleepInterval);
- return result;
- }
-}
\ No newline at end of file
diff --git a/public/docs/_examples/protractor-helpers.ts b/public/docs/_examples/protractor-helpers.ts
new file mode 100644
index 0000000000..61af74ba30
--- /dev/null
+++ b/public/docs/_examples/protractor-helpers.ts
@@ -0,0 +1,35 @@
+import { browser } from 'protractor';
+
+export var appLang = {
+ appIsTs: false,
+ appIsJs: false,
+ appIsDart: false,
+ appIsUnknown: false
+};
+
+export function describeIf(cond: boolean, name: string, func: () => void): void {
+ if (cond) {
+ describe(name, func);
+ } else {
+ xdescribe(name, func);
+ }
+}
+
+export function itIf(cond: boolean, name: string, func: (done: DoneFn) => void): void {
+ if (cond) {
+ it(name, func);
+ } else {
+ xit(name, func);
+ }
+}
+
+// protractor.config.js is set to ng2 mode by default, so we must manually
+// change it for upgradeAdapter tests
+export function setProtractorToNg1Mode(): void {
+ browser.rootEl = 'body';
+}
+
+export function setProtractorToHybridMode() {
+ setProtractorToNg1Mode();
+ browser.ng12Hybrid = true;
+}
diff --git a/public/docs/_examples/protractor.config.js b/public/docs/_examples/protractor.config.js
new file mode 100644
index 0000000000..92b5e1fda5
--- /dev/null
+++ b/public/docs/_examples/protractor.config.js
@@ -0,0 +1,175 @@
+// FIRST TIME ONLY- run:
+// ./node_modules/.bin/webdriver-manager update
+//
+// Try: `npm run webdriver:update`
+//
+// AND THEN EVERYTIME ...
+// 1. Compile with `tsc`
+// 2. Make sure the test server (e.g., http-server: localhost:8080) is running.
+// 3. ./node_modules/.bin/protractor protractor.config.js
+//
+// To do all steps, try: `npm run e2e`
+
+var fs = require('fs');
+var path = require('canonical-path');
+var _ = require('lodash');
+
+
+exports.config = {
+ directConnect: true,
+
+ // Capabilities to be passed to the webdriver instance.
+ capabilities: {
+ 'browserName': 'chrome'
+ },
+
+ // Framework to use. Jasmine is recommended.
+ framework: 'jasmine',
+
+ // For angular tests
+ useAllAngular2AppRoots: true,
+
+ // Base URL for application server
+ baseUrl: 'http://localhost:8080',
+
+ // doesn't seem to work.
+ // resultJsonOutputFile: "foo.json",
+
+ onPrepare: function() {
+ //// SpecReporter
+ //var SpecReporter = require('jasmine-spec-reporter');
+ //jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'none'}));
+ //// jasmine.getEnv().addReporter(new SpecReporter({displayStacktrace: 'all'}));
+
+ // debugging
+ // console.log('browser.params:' + JSON.stringify(browser.params));
+ var protractorHelpers = require('./protractor-helpers.ts');
+
+ var appDir = browser.params.appDir;
+ if (appDir) {
+ if (appDir.match('/ts') != null) {
+ protractorHelpers.appLang.appIsTs = true;
+ } else if (appDir.match('/js') != null) {
+ protractorHelpers.appLang.appIsJs = true;
+ } else if (appDir.match('/dart') != null) {
+ protractorHelpers.appLang.appIsDart = true;
+ } else {
+ protractorHelpers.appLang.appIsUnknown = true;
+ }
+ } else {
+ protractorHelpers.appLang.appIsUnknown = true;
+ }
+ jasmine.getEnv().addReporter(new Reporter( browser.params ));
+ },
+
+ jasmineNodeOpts: {
+ // defaultTimeoutInterval: 60000,
+ defaultTimeoutInterval: 10000,
+ showTiming: true,
+ print: function() {}
+ },
+
+ beforeLaunch: function() {
+ // add TS support for specs
+ require('ts-node').register({
+ project: './tsconfig.json'
+ });
+ }
+};
+
+// See http://jasmine.github.io/2.1/custom_reporter.html
+function Reporter(options) {
+ var _defaultOutputFile = path.resolve(process.cwd(), "../../../", 'protractor-results.txt');
+ options.outputFile = options.outputFile || _defaultOutputFile;
+
+ var _root = { appDir: options.appDir, suites: [] };
+ log('AppDir: ' + options.appDir, +1);
+ var _currentSuite;
+
+ this.suiteStarted = function(suite) {
+ _currentSuite = { description: suite.description, status: null, specs: [] };
+ _root.suites.push(_currentSuite);
+ log('Suite: ' + suite.description, +1);
+ };
+
+ this.suiteDone = function(suite) {
+ var statuses = _currentSuite.specs.map(function(spec) {
+ return spec.status;
+ });
+ statuses = _.uniq(statuses);
+ var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', ');
+ _currentSuite.status = status;
+ log('Suite ' + _currentSuite.status + ': ' + suite.description, -1);
+ };
+
+ this.specStarted = function(spec) {
+
+ };
+
+ this.specDone = function(spec) {
+ var currentSpec = {
+ description: spec.description,
+ status: spec.status
+ };
+ if (spec.failedExpectations.length > 0) {
+ currentSpec.failedExpectations = spec.failedExpectations;
+ }
+
+ _currentSuite.specs.push(currentSpec);
+ log(spec.status + ' - ' + spec.description);
+ if (spec.status === 'failed') {
+ spec.failedExpectations.forEach(function(err) {
+ log(err.message);
+ });
+ }
+ };
+
+ this.jasmineDone = function() {
+ outputFile = options.outputFile;
+ //// Alternate approach - just stringify the _root - not as pretty
+ //// but might be more useful for automation.
+ // var output = JSON.stringify(_root, null, 2);
+ var output = formatOutput(_root);
+ fs.appendFileSync(outputFile, output);
+ };
+
+ // for output file output
+ function formatOutput(output) {
+ var indent = ' ';
+ var pad = ' ';
+ var results = [];
+ results.push('AppDir:' + output.appDir);
+ output.suites.forEach(function(suite) {
+ results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status);
+ pad+=indent;
+ suite.specs.forEach(function(spec) {
+ results.push(pad + spec.status + ' - ' + spec.description);
+ if (spec.failedExpectations) {
+ pad+=indent;
+ spec.failedExpectations.forEach(function (fe) {
+ results.push(pad + 'message: ' + fe.message);
+ });
+ pad=pad.substr(2);
+ }
+ });
+ pad = pad.substr(2);
+ results.push('');
+ });
+ results.push('');
+ return results.join('\n');
+ }
+
+ // for console output
+ var _pad;
+ function log(str, indent) {
+ _pad = _pad || '';
+ if (indent == -1) {
+ _pad = _pad.substr(2);
+ }
+ console.log(_pad + str);
+ if (indent == 1) {
+ _pad = _pad + ' ';
+ }
+ }
+
+}
diff --git a/public/docs/_examples/quickstart/app.js b/public/docs/_examples/quickstart/app.js
deleted file mode 100644
index 47783479c3..0000000000
--- a/public/docs/_examples/quickstart/app.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") return Reflect.decorate(decorators, target, key, desc);
- switch (arguments.length) {
- case 2: return decorators.reduceRight(function(o, d) { return (d && d(o)) || o; }, target);
- case 3: return decorators.reduceRight(function(o, d) { return (d && d(target, key)), void 0; }, void 0);
- case 4: return decorators.reduceRight(function(o, d) { return (d && d(target, key, o)) || o; }, desc);
- }
-};
-var __metadata = (this && this.__metadata) || function (k, v) {
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
-};
-// #docregion
-// #docregion import
-var angular2_1 = require('angular2/angular2');
-// #enddocregion
-var MyAppComponent = (function () {
- function MyAppComponent() {
- this.name = 'Alice';
- }
- MyAppComponent = __decorate([
- angular2_1.Component({
- selector: 'my-app'
- }),
- angular2_1.View({
- template: 'Hello {{ name }} '
- }),
- __metadata('design:paramtypes', [])
- ], MyAppComponent);
- return MyAppComponent;
-})();
-// #docregion bootstrap
-angular2_1.bootstrap(MyAppComponent);
-// #enddocregion
-//# sourceMappingURL=app.js.map
\ No newline at end of file
diff --git a/public/docs/_examples/quickstart/app.js.map b/public/docs/_examples/quickstart/app.js.map
deleted file mode 100644
index 879e6ea4cd..0000000000
--- a/public/docs/_examples/quickstart/app.js.map
+++ /dev/null
@@ -1 +0,0 @@
-{"version":3,"file":"app.js","sourceRoot":"","sources":["app.ts"],"names":["MyAppComponent","MyAppComponent.constructor"],"mappings":";;;;;;;;;;;AAEA,AAFA,UAAU;AACV,iBAAiB;AACjB,yBAAyC,mBAAmB,CAAC,CAAA;AAG7D,AAFA,aAAa;;IAWZA;QACCC,IAAIA,CAACA,IAAIA,GAAGA,OAAOA,CAACA;IACrBA,CAACA;IAXFD;QAACA,oBAASA,CAACA;YACVA,QAAQA,EAAEA,QAAQA;SAClBA,CAACA;QACDA,eAAIA,CAACA;YACLA,QAAQA,EAAEA,uCAAuCA;SACjDA,CAACA;;uBAODA;IAADA,qBAACA;AAADA,CAACA,AAZD,IAYC;AAGD,AADA,oBAAoB;AACpB,oBAAS,CAAC,cAAc,CAAC,CAAC;AAC1B,aAAa"}
\ No newline at end of file
diff --git a/public/docs/_examples/quickstart/app.ts b/public/docs/_examples/quickstart/app.ts
deleted file mode 100644
index 19bdebff7e..0000000000
--- a/public/docs/_examples/quickstart/app.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-// #docregion
-// #docregion import
-import {Component, View, bootstrap} from 'angular2/angular2';
-// #enddocregion
-
-@Component({
- selector: 'my-app'
-})
-@View({
- template: 'Hello {{ name }} '
-})
-class MyAppComponent {
- name : string;
-
- constructor() {
- this.name = 'Alice';
- }
-}
-
-// #docregion bootstrap
-bootstrap(MyAppComponent);
-// #enddocregion
diff --git a/public/docs/_examples/quickstart/e2e-spec.ts b/public/docs/_examples/quickstart/e2e-spec.ts
new file mode 100644
index 0000000000..73921707ee
--- /dev/null
+++ b/public/docs/_examples/quickstart/e2e-spec.ts
@@ -0,0 +1,17 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('QuickStart E2E Tests', function () {
+
+ let expectedMsg = 'Hello Angular';
+
+ beforeEach(function () {
+ browser.get('');
+ });
+
+ it(`should display: ${expectedMsg}`, function () {
+ expect(element(by.css('h1')).getText()).toEqual(expectedMsg);
+ });
+
+});
diff --git a/public/docs/_examples/quickstart/index.html b/public/docs/_examples/quickstart/index.html
deleted file mode 100644
index 9cb6fd586b..0000000000
--- a/public/docs/_examples/quickstart/index.html
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
- Angular 2 Quickstart
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/public/docs/_examples/quickstart/js/bs-config.1.json b/public/docs/_examples/quickstart/js/bs-config.1.json
new file mode 100644
index 0000000000..4e58595267
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/bs-config.1.json
@@ -0,0 +1,8 @@
+{
+ "server": {
+ "baseDir": "src",
+ "routes": {
+ "/node_modules": "node_modules"
+ }
+ }
+}
diff --git a/public/docs/_examples/quickstart/js/example-config.json b/public/docs/_examples/quickstart/js/example-config.json
new file mode 100644
index 0000000000..81f31aaf0d
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/example-config.json
@@ -0,0 +1,3 @@
+{
+ "build": "build:babel"
+}
diff --git a/public/docs/_examples/quickstart/js/package.1.json b/public/docs/_examples/quickstart/js/package.1.json
new file mode 100644
index 0000000000..28aee2b92e
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/package.1.json
@@ -0,0 +1,29 @@
+{
+ "name": "angular-quickstart",
+ "version": "1.0.0",
+ "scripts": {
+ "start": "npm run lite",
+ "lite": "lite-server"
+ },
+ "license": "MIT",
+ "dependencies": {
+ "@angular/common": "~2.4.0",
+ "@angular/compiler": "~2.4.0",
+ "@angular/core": "~2.4.0",
+ "@angular/forms": "~2.4.0",
+ "@angular/http": "~2.4.0",
+ "@angular/platform-browser": "~2.4.0",
+ "@angular/platform-browser-dynamic": "~2.4.0",
+ "@angular/router": "~3.4.0",
+ "@angular/upgrade": "~2.4.0",
+
+ "angular-in-memory-web-api": "~0.3.1",
+ "core-js": "^2.4.1",
+ "rxjs": "5.0.1",
+ "zone.js": "^0.8.4"
+ },
+ "devDependencies": {
+ "concurrently": "^3.0.0",
+ "lite-server": "^2.2.2"
+ }
+}
diff --git a/public/docs/_examples/quickstart/js/plnkr.json b/public/docs/_examples/quickstart/js/plnkr.json
new file mode 100644
index 0000000000..5e24ad9790
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/plnkr.json
@@ -0,0 +1,8 @@
+{
+ "description": "QuickStart",
+ "basePath": "src/",
+ "files": [
+ "!**/*.[1].*"
+ ],
+ "tags": ["quickstart"]
+}
diff --git a/public/docs/_examples/quickstart/js/src/app/app.component.js b/public/docs/_examples/quickstart/js/src/app/app.component.js
new file mode 100644
index 0000000000..26ba116efe
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/src/app/app.component.js
@@ -0,0 +1,27 @@
+// #docplaster
+// #docregion
+// #docregion iife
+(function(app) {
+ // #enddocregion iife
+ // #docregion ng-namespace-funcs, export
+ app.AppComponent =
+ // #enddocregion export
+ // #docregion component
+ ng.core.Component({
+ // #enddocregion ng-namespace-funcs
+ selector: 'my-app',
+ template: 'Hello Angular '
+ // #docregion ng-namespace-funcs
+ })
+ // #enddocregion component
+ // #docregion class
+ .Class({
+ // #enddocregion ng-namespace-funcs
+ constructor: function() {}
+ // #docregion ng-namespace-funcs
+ });
+ // #enddocregion class
+ // #enddocregion ng-namespace-funcs
+// #docregion iife
+})(window.app || (window.app = {}));
+// #enddocregion iife
diff --git a/public/docs/_examples/quickstart/js/src/app/app.module.js b/public/docs/_examples/quickstart/js/src/app/app.module.js
new file mode 100644
index 0000000000..0f3d5f82cf
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/src/app/app.module.js
@@ -0,0 +1,15 @@
+// #docplaster
+// #docregion
+(function(app) {
+ app.AppModule =
+ ng.core.NgModule({
+ imports: [ ng.platformBrowser.BrowserModule ],
+ // #docregion import
+ declarations: [ app.AppComponent ],
+ // #enddocregion import
+ bootstrap: [ app.AppComponent ]
+ })
+ .Class({
+ constructor: function() {}
+ });
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/quickstart/js/src/index.html b/public/docs/_examples/quickstart/js/src/index.html
new file mode 100644
index 0000000000..43d63e9c83
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/src/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+ Angular QuickStart JS
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
+
diff --git a/public/docs/_examples/quickstart/js/src/main.js b/public/docs/_examples/quickstart/js/src/main.js
new file mode 100644
index 0000000000..785823fa84
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/src/main.js
@@ -0,0 +1,8 @@
+// #docregion
+(function(app) {
+ document.addEventListener('DOMContentLoaded', function() {
+ ng.platformBrowserDynamic
+ .platformBrowserDynamic()
+ .bootstrapModule(app.AppModule);
+ });
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/quickstart/js/src/styles.1.css b/public/docs/_examples/quickstart/js/src/styles.1.css
new file mode 100644
index 0000000000..fbc30e2c9e
--- /dev/null
+++ b/public/docs/_examples/quickstart/js/src/styles.1.css
@@ -0,0 +1,14 @@
+/* #docregion */
+h1 {
+ color: #369;
+ font-family: Arial, Helvetica, sans-serif;
+ font-size: 250%;
+}
+body {
+ margin: 2em;
+}
+
+ /*
+ * See https://github.com/angular/angular.io/blob/master/public/docs/_examples/styles.css
+ * for the full set of master styles used by the documentation samples
+ */
diff --git a/public/docs/_examples/quickstart/protractor-spec.js b/public/docs/_examples/quickstart/protractor-spec.js
deleted file mode 100644
index 4d6e58fedd..0000000000
--- a/public/docs/_examples/quickstart/protractor-spec.js
+++ /dev/null
@@ -1,13 +0,0 @@
-// protractor-spec.js
-describe('Protractor quick start test', function() {
- beforeEach(function() {
- browser.get('quickstart/index.html');
- });
-
- // #docregion test
- it('should display Alice', function() {
- expect(element(by.id('output')).getText()).toEqual('Hello Alice');
- });
- // #enddocregion
-});
-
diff --git a/public/docs/_examples/quickstart/ts/bs-config.1.json b/public/docs/_examples/quickstart/ts/bs-config.1.json
new file mode 100644
index 0000000000..4e58595267
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/bs-config.1.json
@@ -0,0 +1,8 @@
+{
+ "server": {
+ "baseDir": "src",
+ "routes": {
+ "/node_modules": "node_modules"
+ }
+ }
+}
diff --git a/public/docs/_examples/quickstart/ts/example-config.json b/public/docs/_examples/quickstart/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/quickstart/ts/plnkr.json b/public/docs/_examples/quickstart/ts/plnkr.json
new file mode 100644
index 0000000000..8edf7c25ea
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/plnkr.json
@@ -0,0 +1,12 @@
+{
+ "description": "QuickStart",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.ts",
+ "app/app.module.ts",
+ "main.ts",
+ "index.html"
+ ],
+ "open": "app/app.component.ts",
+ "tags": ["quickstart"]
+}
diff --git a/public/docs/_examples/quickstart/ts/src/app/app.component.ts b/public/docs/_examples/quickstart/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..1ef28fc5c4
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/src/app/app.component.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `Hello {{name}} `
+})
+export class AppComponent { name = 'Angular'; }
diff --git a/public/docs/_examples/quickstart/ts/src/app/app.module.ts b/public/docs/_examples/quickstart/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..50a0e6eb47
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/src/app/app.module.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/quickstart/ts/src/index.html b/public/docs/_examples/quickstart/ts/src/index.html
new file mode 100644
index 0000000000..21fb56edb9
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/src/index.html
@@ -0,0 +1,31 @@
+
+
+
+ Angular Quickstart
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading AppComponent content here ...
+
+
+
+
diff --git a/public/docs/_examples/quickstart/ts/src/main.ts b/public/docs/_examples/quickstart/ts/src/main.ts
new file mode 100644
index 0000000000..311c44b76d
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/src/main.ts
@@ -0,0 +1,5 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/quickstart/ts/src/tsconfig.1.json b/public/docs/_examples/quickstart/ts/src/tsconfig.1.json
new file mode 100644
index 0000000000..2c7260d1bc
--- /dev/null
+++ b/public/docs/_examples/quickstart/ts/src/tsconfig.1.json
@@ -0,0 +1,13 @@
+{
+ "compilerOptions": {
+ "target": "es5",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "lib": [ "es2015", "dom" ],
+ "noImplicitAny": true,
+ "suppressImplicitAnyIndexErrors": true
+ }
+}
diff --git a/public/docs/_examples/reactive-forms/e2e-spec.ts b/public/docs/_examples/reactive-forms/e2e-spec.ts
new file mode 100644
index 0000000000..bb788397ef
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/e2e-spec.ts
@@ -0,0 +1,1020 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+function finalDemoAddressForm(element: any, index: number) {
+ let form = {
+ street: element.all(by.css('input[formcontrolname=street]')).get(index).getAttribute('value'),
+ city: element.all(by.css('input[formcontrolname=city]')).get(index).getAttribute('value'),
+ state: element.all(by.css('select[formcontrolname=state]')).get(index).getAttribute('value'),
+ zip: element.all(by.css('input[formcontrolname=zip]')).get(index).getAttribute('value')
+ };
+ return form;
+}
+
+describe('Reactive forms', function() {
+ let select: any;
+
+ beforeEach(function() {
+ browser.get('');
+ select = element(by.css('.container > h4 > select'));
+ });
+
+ describe('navigation', function() {
+ it('should display the title', function() {
+ let title = element(by.css('.container > h1'));
+ expect(title.getText()).toBe('Reactive Forms');
+ });
+
+ it('should contain a dropdown with each example', function() {
+ expect(select.isDisplayed()).toBe(true);
+ });
+
+ it('should have 9 options for different demos', function() {
+ let options = select.all(by.tagName('option'));
+ expect(options.count()).toBe(9);
+ });
+
+ it('should start with Final Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('Final Demo');
+ });
+ });
+ });
+
+// *************Begin Final Demo test*******************************
+
+ describe('final demo', function() {
+ it('does not select any hero by default', function() {
+ let heroSection = element(by.css('hero-list > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ it('refreshes the page upon button click', function() {
+ // We move to another page...
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ let refresh = element(by.css('button'));
+ refresh.click();
+ let heroSection = element(by.css('hero-list > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ describe('Whirlwind form', function() {
+ beforeEach(function() {
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Whirlwind');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Whirlwind');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('123 Main');
+ expect(address1.state).toBe('CA');
+ expect(address1.zip).toBe('94801');
+ expect(address1.city).toBe('Anywhere');
+ let address2 = finalDemoAddressForm(element, 1);
+ expect(address2.street).toBe('456 Maple');
+ expect(address2.state).toBe('VA');
+ expect(address2.zip).toBe('23226');
+ expect(address2.city).toBe('Somewhere');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail > p'));
+ expect(json.getText()).toContain('Whirlwind');
+ expect(json.getText()).toContain('Anywhere');
+ expect(json.getText()).toContain('Somewhere');
+ expect(json.getText()).toContain('VA');
+ });
+
+ it('has two disabled buttons by default', function() {
+ let buttons = element.all(by.css('hero-detail > form > div > button'));
+ expect(buttons.get(0).getAttribute('disabled')).toBe('true');
+ expect(buttons.get(1).getAttribute('disabled')).toBe('true');
+ });
+
+ it('enables the buttons after we edit the form', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let buttons = element.all(by.css('hero-detail > form > div > button'));
+ expect(buttons.get(0).getAttribute('disabled')).toBeNull();
+ expect(buttons.get(1).getAttribute('disabled')).toBeNull();
+ });
+
+ it('saves the changes when the save button is clicked', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let save = element.all(by.css('hero-detail > form > div > button')).get(0);
+ save.click();
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Whirlwinda');
+ });
+
+ it('reverts the changes when the revert button is clicked', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let revert = element.all(by.css('hero-detail > form > div > button')).get(1);
+ revert.click();
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Whirlwind');
+ expect(nameInput.getAttribute('value')).toBe('Whirlwind');
+ });
+
+ it('is able to add a new empty address', function() {
+ let newLairButton = element.all(by.css('button')).get(3);
+ newLairButton.click();
+ let address3 = finalDemoAddressForm(element, 2);
+ expect(address3.street).toBe('');
+ expect(address3.state).toBe('');
+ expect(address3.zip).toBe('');
+ expect(address3.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+ });
+
+ describe('Bombastic form', function() {
+ beforeEach(function() {
+ let bombastaButton = element.all(by.css('nav a')).get(1);
+ bombastaButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Bombastic');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Bombastic');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('789 Elm');
+ // expect(address1.state).toBe('OH');
+ expect(address1.zip).toBe('04501');
+ expect(address1.city).toBe('Smallville');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail > p'));
+ expect(json.getText()).toContain('Bombastic');
+ expect(json.getText()).toContain('Smallville');
+ expect(json.getText()).toContain('OH');
+ expect(json.getText()).toContain('04501');
+ });
+
+ it('has two disabled buttons by default', function() {
+ let buttons = element.all(by.css('hero-detail > form > div > button'));
+ expect(buttons.get(0).getAttribute('disabled')).toBe('true');
+ expect(buttons.get(1).getAttribute('disabled')).toBe('true');
+ });
+
+ it('enables the buttons after we edit the form', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let buttons = element.all(by.css('hero-detail > form > div > button'));
+ expect(buttons.get(0).getAttribute('disabled')).toBeNull();
+ expect(buttons.get(1).getAttribute('disabled')).toBeNull();
+ });
+
+ it('saves the changes when the save button is clicked', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let save = element.all(by.css('hero-detail > form > div > button')).get(0);
+ save.click();
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Bombastica');
+ });
+
+ it('reverts the changes when the revert button is clicked', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let revert = element.all(by.css('hero-detail > form > div > button')).get(1);
+ revert.click();
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Bombastic');
+ expect(nameInput.getAttribute('value')).toBe('Bombastic');
+ });
+
+ it('is able to add a new empty address', function() {
+ let newLairButton = element.all(by.css('button')).get(3);
+ newLairButton.click();
+ let address2 = finalDemoAddressForm(element, 1);
+ expect(address2.street).toBe('');
+ expect(address2.state).toBe('');
+ expect(address2.zip).toBe('');
+ expect(address2.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+ });
+
+ describe('Magneta form', function() {
+
+ beforeEach(function() {
+ let magnetaButton = element.all(by.css('nav a')).get(2);
+ magnetaButton.click();
+ });
+
+ it('should show hero information when the button is clicked', function() {
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Magneta');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Magneta');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail > p'));
+ expect(json.getText()).toContain('Magneta');
+ });
+
+ it('has two disabled buttons by default', function() {
+ let buttons = element.all(by.css('hero-detail > form > div > button'));
+ expect(buttons.get(0).getAttribute('disabled')).toBe('true');
+ expect(buttons.get(1).getAttribute('disabled')).toBe('true');
+ });
+
+ it('enables the buttons after we edit the form', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let buttons = element.all(by.css('hero-detail > form > div > button'));
+ expect(buttons.get(0).getAttribute('disabled')).toBeNull();
+ expect(buttons.get(1).getAttribute('disabled')).toBeNull();
+ });
+
+ it('saves the changes when the save button is clicked', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let save = element.all(by.css('hero-detail > form > div > button')).get(0);
+ save.click();
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Magnetaa');
+ });
+
+ it('reverts the changes when the revert button is clicked', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ nameInput.sendKeys('a');
+ let revert = element.all(by.css('hero-detail > form > div > button')).get(1);
+ revert.click();
+ let editMessage = element(by.css('hero-list > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Magneta');
+ expect(nameInput.getAttribute('value')).toBe('Magneta');
+ });
+
+ it('is able to add a new empty address', function() {
+ let newLairButton = element.all(by.css('button')).get(3);
+ newLairButton.click();
+ let address = finalDemoAddressForm(element, 0);
+ expect(address.street).toBe('');
+ expect(address.state).toBe('');
+ expect(address.zip).toBe('');
+ expect(address.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+ });
+ }); // final demo
+
+// *************Begin FormArray Demo test*******************************
+
+
+ describe('formArray demo', function() {
+ beforeEach(function() {
+ let FormArrayOption = element.all(by.css('select option')).get(7);
+ FormArrayOption.click();
+ });
+
+ it('should show FormArray Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('FormArray Demo');
+ });
+ });
+
+ it('does not select any hero by default', function() {
+ let heroSection = element(by.css('hero-list > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ it('refreshes the page upon button click', function() {
+ // We move to another page...
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ let refresh = element(by.css('button'));
+ refresh.click();
+ let heroSection = element(by.css('hero-list > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ describe('Whirlwind form', function() {
+ beforeEach(function() {
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ });
+
+ it('should show hero information when the button is clicked', function() {
+ let editMessage = element(by.css('div.demo > div > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Whirlwind');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Whirlwind');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('123 Main');
+ expect(address1.state).toBe('CA');
+ expect(address1.zip).toBe('94801');
+ expect(address1.city).toBe('Anywhere');
+ let address2 = finalDemoAddressForm(element, 1);
+ expect(address2.street).toBe('456 Maple');
+ expect(address2.state).toBe('VA');
+ expect(address2.zip).toBe('23226');
+ expect(address2.city).toBe('Somewhere');
+ });
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-8 > p'));
+ expect(json.getText()).toContain('Whirlwind');
+ expect(json.getText()).toContain('Anywhere');
+ expect(json.getText()).toContain('Somewhere');
+ expect(json.getText()).toContain('VA');
+ });
+
+ it('is able to add a new empty address', function() {
+ let newLairButton = element.all(by.css('button')).get(1);
+ newLairButton.click();
+ let address2 = finalDemoAddressForm(element, 2);
+ expect(address2.street).toBe('');
+ expect(address2.state).toBe('');
+ expect(address2.zip).toBe('');
+ expect(address2.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ }); // Whirlwind form
+
+ describe('Bombastic FormArray form', function() {
+ beforeEach(function() {
+ let bombasticButton = element.all(by.css('nav a')).get(1);
+ bombasticButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('div.demo > div > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Bombastic');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ // nameInput.getAttribute('value').then(function(name: string) {
+ // expect(name).toBe('Whirlwind');
+ // });
+ expect(nameInput.getAttribute('value')).toBe('Bombastic');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('789 Elm');
+ // expect(address1.state).toBe('OH');
+ // This select should be OH not CA, which it shows in the UI, the JSON shows OH.
+ expect(address1.zip).toBe('04501');
+ expect(address1.city).toBe('Smallville');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-8 > p'));
+ expect(json.getText()).toContain('Bombastic');
+ expect(json.getText()).toContain('Smallville');
+ expect(json.getText()).toContain('04501');
+ expect(json.getText()).toContain('789 Elm');
+ });
+
+ it('is able to add a new empty address', function() {
+ let newLairButton = element.all(by.css('button')).get(1);
+ newLairButton.click();
+ let address1 = finalDemoAddressForm(element, 1);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ }); // Bombastic FormArray form
+
+ describe('Magneta FormArray form', function() {
+ beforeEach(function() {
+ let magnetaButton = element.all(by.css('nav a')).get(2);
+ magnetaButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('div.demo > div > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Magneta');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Magneta');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-8 > p'));
+ expect(json.getText()).toContain('Magneta');
+ });
+
+ it('is able to add a new empty address', function() {
+ let newLairButton = element.all(by.css('button')).get(1);
+ newLairButton.click();
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ }); // Magneta FormArray form
+
+ }); // formArray demo
+
+
+// *************Begin SetValue Demo test*******************************
+
+ describe('SetValue demo', function() {
+ beforeEach(function() {
+ let SetValueOption = element.all(by.css('select option')).get(6);
+ SetValueOption.click();
+ });
+
+ it('should show SetValue Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('SetValue Demo');
+ });
+ });
+
+ it('does not select any hero by default', function() {
+ let heroSection = element(by.css('hero-list > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ it('refreshes the page upon button click', function() {
+ // We move to another page...
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ let refresh = element(by.css('button'));
+ refresh.click();
+ let heroSection = element(by.css('hero-list > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ describe('Whirlwind setValue form', function() {
+ beforeEach(function() {
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('.demo > div > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Whirlwind');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Whirlwind');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('123 Main');
+ expect(address1.state).toBe('CA');
+ expect(address1.zip).toBe('94801');
+ expect(address1.city).toBe('Anywhere');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-7 > p'));
+ expect(json.getText()).toContain('Whirlwind');
+ expect(json.getText()).toContain('Anywhere');
+ let nameOutput = element(by.css('hero-detail-7 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value: Whirlwind');
+ let streetOutput = element(by.css('hero-detail-7 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value: 123 Main');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ }); // Whirlwind setValue form
+
+ describe('Bombastic setValue form', function() {
+ beforeEach(function() {
+ let bombasticButton = element.all(by.css('nav a')).get(1);
+ bombasticButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('.demo > div > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Bombastic');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Bombastic');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('789 Elm');
+ expect(address1.state).toBe('OH');
+ expect(address1.zip).toBe('04501');
+ expect(address1.city).toBe('Smallville');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-7 > p'));
+ expect(json.getText()).toContain('Bombastic');
+ expect(json.getText()).toContain('Smallville');
+ expect(json.getText()).toContain('04501');
+ expect(json.getText()).toContain('789 Elm');
+ let nameOutput = element(by.css('hero-detail-7 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value: Bombastic');
+ let streetOutput = element(by.css('hero-detail-7 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value: 789 Elm');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ }); // Bombastic setValue form
+
+ describe('Magneta setValue form', function() {
+ beforeEach(function() {
+ let magnetaButton = element.all(by.css('nav a')).get(2);
+ magnetaButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('.demo > div > div > h3'));
+ expect(editMessage.getText()).toBe('Editing: Magneta');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Magneta');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-7 > p'));
+ expect(json.getText()).toContain('Magneta');
+ let nameOutput = element(by.css('hero-detail-7 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value: Magneta');
+ let streetOutput = element(by.css('hero-detail-7 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value:');
+ });
+
+ }); // Magneta setValue form
+ }); // SetValue demo
+
+// *************Begin patchValue Demo test*******************************
+
+ describe('patchValue demo', function() {
+ beforeEach(function() {
+ let SetValueOption = element.all(by.css('select option')).get(5);
+ SetValueOption.click();
+ });
+
+ it('should show patchValue Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('PatchValue Demo');
+ });
+ });
+
+ it('does not select any hero by default', function() {
+ let heroSection = element(by.css('.demo > div > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ it('refreshes the page upon button click', function() {
+ // We move to another page...
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ let refresh = element(by.css('button'));
+ refresh.click();
+ let heroSection = element(by.css('.demo > div > div'));
+ expect(heroSection.isPresent()).toBe(false);
+ });
+
+ describe('Whirlwind patchValue form', function() {
+ beforeEach(function() {
+ let whirlwindButton = element.all(by.css('nav a')).get(0);
+ whirlwindButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('h2 ~ h3'));
+ expect(editMessage.getText()).toBe('Editing: Whirlwind');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Whirlwind');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-6 > p'));
+ expect(json.getText()).toContain('Whirlwind');
+ let nameOutput = element(by.css('hero-detail-6 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value: Whirlwind');
+ let streetOutput = element(by.css('hero-detail-6 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value:');
+ });
+
+
+ }); // Bombastic patchValue form
+ describe('Bombastic patchValue form', function() {
+ beforeEach(function() {
+ let bombasticButton = element.all(by.css('nav a')).get(1);
+ bombasticButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('h2 ~ h3'));
+ expect(editMessage.getText()).toBe('Editing: Bombastic');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Bombastic');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-6 > p'));
+ expect(json.getText()).toContain('Bombastic');
+ let nameOutput = element(by.css('hero-detail-6 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value: Bombastic');
+ let streetOutput = element(by.css('hero-detail-6 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value:');
+ });
+ }); // Bombastic patchValue form
+
+ describe('Magneta patchValue form', function() {
+ beforeEach(function() {
+ let magnetaButton = element.all(by.css('nav a')).get(2);
+ magnetaButton.click();
+ });
+
+ it('should show a hero information when the button is clicked', function() {
+ let editMessage = element(by.css('h2 ~ h3'));
+ expect(editMessage.getText()).toBe('Editing: Magneta');
+ });
+
+ it('should show a form with the selected hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('Magneta');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-6 > p'));
+ expect(json.getText()).toContain('Magneta');
+ let nameOutput = element(by.css('hero-detail-6 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value: Magneta');
+ let streetOutput = element(by.css('hero-detail-6 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value:');
+ });
+
+ }); // Magneta patchValue form
+ }); // PatchValue demo
+
+
+
+// *************Begin Nested FormBuilder Demo test*******************************
+
+ describe('Nested FormBuilder demo', function() {
+ beforeEach(function() {
+ let NestedFormBuilderOption = element.all(by.css('select option')).get(4);
+ NestedFormBuilderOption.click();
+ });
+
+ it('should show Nested FormBuilder Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('Nested FormBuilder group Demo');
+ });
+ });
+
+ it('should show a form for hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-5 > p'));
+ expect(json.getText()).toContain('address');
+ let nameOutput = element(by.css('hero-detail-5 > p ~ p'));
+ expect(nameOutput.getText()).toContain('Name value:');
+ let streetOutput = element(by.css('hero-detail-5 > p ~ p ~ p'));
+ expect(streetOutput.getText()).toContain('Street value:');
+ });
+
+ }); // Nested FormBuilder demo
+
+// *************Begin Group with multiple controls Demo test*******************************
+
+ describe('Group with multiple controls demo', function() {
+ beforeEach(function() {
+ let NestedFormBuilderOption = element.all(by.css('select option')).get(3);
+ NestedFormBuilderOption.click();
+ });
+
+ it('should show Group with multiple controls Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('Group with multiple controls Demo');
+ });
+ });
+
+ it('should show header', function() {
+ let header = element(by.css('hero-detail-4 > h3'));
+ expect(header.getText()).toBe('A FormGroup with multiple FormControls');
+ });
+
+ it('should show a form for hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('');
+ let address1 = finalDemoAddressForm(element, 0);
+ expect(address1.street).toBe('');
+ expect(address1.state).toBe('');
+ expect(address1.zip).toBe('');
+ expect(address1.city).toBe('');
+ });
+
+ it('should show three radio buttons', function() {
+ let radioButtons = element.all(by.css('input[formcontrolname=power]'));
+ expect(radioButtons.get(0).getAttribute('value')).toBe('flight');
+ expect(radioButtons.get(1).getAttribute('value')).toBe('x-ray vision');
+ expect(radioButtons.get(2).getAttribute('value')).toBe('strength');
+ });
+ it('should show a checkbox', function() {
+ let checkbox = element(by.css('input[formcontrolname=sidekick]'));
+ expect(checkbox.getAttribute('checked')).toBe(null);
+ });
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-4 > p'));
+ expect(json.getText()).toContain('power');
+ });
+
+}); // Group with multiple controls demo
+
+
+
+// *************Begin Group with multiple controls Demo test*******************************
+
+ describe('Simple FormBuilder Group demo', function() {
+ beforeEach(function() {
+ let SimpleFormBuilderOption = element.all(by.css('select option')).get(2);
+ SimpleFormBuilderOption.click();
+ });
+
+ it('should show Simple FormBuilder group Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('Simple FormBuilder group Demo');
+ });
+ });
+
+ it('should show header', function() {
+ let header = element(by.css('hero-detail-3 > h3'));
+ expect(header.getText()).toBe('A FormGroup with a single FormControl using FormBuilder');
+ });
+
+ it('should show a form for hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-3 > p'));
+ expect(json.getText()).toContain('name');
+ let validStatus = element(by.css('hero-detail-3 > p ~ p'));
+ expect(validStatus.getText()).toContain('INVALID');
+ });
+
+}); // Group with multiple controls demo
+
+
+// *************Begin FormControl in a FormGroup Demo test*******************************
+
+ describe('FormControl in a FormGroup demo', function() {
+ beforeEach(function() {
+ let SimpleFormBuilderOption = element.all(by.css('select option')).get(1);
+ SimpleFormBuilderOption.click();
+ });
+
+ it('should show FormControl in a FormGroup Demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('FormControl in a FormGroup Demo');
+ });
+ });
+
+ it('should show header', function() {
+ let header = element(by.css('hero-detail-2 > h3'));
+ expect(header.getText()).toBe('FormControl in a FormGroup');
+ });
+
+ it('should show a form for hero information', function() {
+ let nameInput = element(by.css('input[formcontrolname=name]'));
+ expect(nameInput.getAttribute('value')).toBe('');
+ });
+
+ it('shows a json output from the form', function() {
+ let json = element(by.css('hero-detail-2 > p'));
+ expect(json.getText()).toContain('name');
+ });
+
+}); // Group with multiple controls demo
+
+// *************Begin Just A FormControl Demo test*******************************
+
+ describe('Just a FormControl demo', function() {
+ beforeEach(function() {
+ let FormControlOption = element.all(by.css('select option')).get(0);
+ FormControlOption.click();
+ });
+
+ it('should show Just a FormControl demo', function() {
+ select.getAttribute('value').then(function(demo: string) {
+ expect(demo).toBe('Just a FormControl Demo');
+ });
+ });
+
+ it('should show header', function() {
+ let header = element(by.css('hero-detail-1 > h3'));
+ expect(header.getText()).toBe('Just a FormControl');
+ });
+
+ it('should show a form for hero information', function() {
+ let nameInput = element(by.css('input'));
+ expect(nameInput.getAttribute('value')).toBe('');
+ });
+
+ }); // Just a FormControl demo test
+
+
+}); // reactive forms
diff --git a/public/docs/_examples/reactive-forms/ts/example-config.json b/public/docs/_examples/reactive-forms/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/reactive-forms/ts/final.plnkr.json b/public/docs/_examples/reactive-forms/ts/final.plnkr.json
new file mode 100644
index 0000000000..41481acc99
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/final.plnkr.json
@@ -0,0 +1,21 @@
+{
+ "description": "Angular Reactive Forms (final)",
+ "basePath": "src/",
+ "files":[
+ "styles.css",
+
+ "app/app.component.ts",
+ "app/app.module.ts",
+ "app/data-model.ts",
+ "app/hero.service.ts",
+ "app/hero-detail.component.html",
+ "app/hero-detail.component.ts",
+ "app/hero-list.component.html",
+ "app/hero-list.component.ts",
+
+ "main-final.ts",
+ "index-final.html"
+ ],
+ "main": "index-final.html",
+ "tags": ["reactive", "forms"]
+}
diff --git a/public/docs/_examples/reactive-forms/ts/plnkr.json b/public/docs/_examples/reactive-forms/ts/plnkr.json
new file mode 100644
index 0000000000..f0daeb4aad
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/plnkr.json
@@ -0,0 +1,15 @@
+{
+ "description": "Angular Reactive Forms (Demo runner)",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+
+ "!app/app.component.1.ts",
+ "!app/hero-list.component.1.html",
+
+ "!app/main-final.ts",
+ "!index-final.html"
+ ],
+ "tags": ["reactive", "forms"]
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/app.component.1.ts b/public/docs/_examples/reactive-forms/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..3023618334
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/app.component.1.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
Reactive Forms
+
+
`
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/app.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..d30a38e979
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/app.component.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
Reactive Forms
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/app.module.ts b/public/docs/_examples/reactive-forms/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..b9f2ea8f99
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/app.module.ts
@@ -0,0 +1,39 @@
+// #docplaster
+// #docregion
+// #docregion v1
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module
+
+import { AppComponent } from './app.component';
+import { HeroDetailComponent } from './hero-detail.component'; // <-- #1 import component
+// #enddocregion v1
+import { HeroListComponent } from './hero-list.component';
+
+import { HeroService } from './hero.service'; // <-- #1 import service
+// #docregion v1
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ ReactiveFormsModule // <-- #2 add to Angular module imports
+ ],
+ declarations: [
+ AppComponent,
+ HeroDetailComponent, // <-- #3 declare app component
+// #enddocregion v1
+ HeroListComponent
+// #docregion v1
+ ],
+// #enddocregion v1
+ exports: [ // export for the DemoModule
+ AppComponent,
+ HeroDetailComponent,
+ HeroListComponent
+ ],
+ providers: [ HeroService ], // <-- #4 provide HeroService
+// #docregion v1
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion v1
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/data-model.ts b/public/docs/_examples/reactive-forms/ts/src/app/data-model.ts
new file mode 100644
index 0000000000..ad01ddee56
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/data-model.ts
@@ -0,0 +1,40 @@
+// #docregion
+// #docregion model-classes
+export class Hero {
+ id = 0;
+ name = '';
+ addresses: Address[];
+}
+
+export class Address {
+ street = '';
+ city = '';
+ state = '';
+ zip = '';
+}
+// #enddocregion model-classes
+
+export const heroes: Hero[] = [
+ {
+ id: 1,
+ name: 'Whirlwind',
+ addresses: [
+ {street: '123 Main', city: 'Anywhere', state: 'CA', zip: '94801'},
+ {street: '456 Maple', city: 'Somewhere', state: 'VA', zip: '23226'},
+ ]
+ },
+ {
+ id: 2,
+ name: 'Bombastic',
+ addresses: [
+ {street: '789 Elm', city: 'Smallville', state: 'OH', zip: '04501'},
+ ]
+ },
+ {
+ id: 3,
+ name: 'Magneta',
+ addresses: [ ]
+ },
+];
+
+export const states = ['CA', 'MD', 'OH', 'VA'];
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/demo.component.html b/public/docs/_examples/reactive-forms/ts/src/app/demo.component.html
new file mode 100644
index 0000000000..1caae127af
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/demo.component.html
@@ -0,0 +1,40 @@
+
+
Reactive Forms
+
Pick a demo:
+
+ {{demo}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
= 6 && demo !== final" >
+
+
Loading heroes ...
+
Select a hero:
+
+
+ Refresh
+ {{hero.name}}
+
+
+
+
+ Hero Detail
+ Editing: {{selectedHero.name}}
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/demo.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/demo.component.ts
new file mode 100644
index 0000000000..181726eb66
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/demo.component.ts
@@ -0,0 +1,47 @@
+/* tslint:disable:member-ordering */
+import { Component } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { Hero } from './data-model';
+import { HeroService } from './hero.service';
+
+@Component({
+ selector: 'my-app',
+ templateUrl: './demo.component.html'
+})
+export class DemoComponent {
+
+ demos: string[] = [
+ 'Just a FormControl',
+ 'FormControl in a FormGroup',
+ 'Simple FormBuilder group',
+ 'Group with multiple controls',
+ 'Nested FormBuilder group',
+ 'PatchValue',
+ 'SetValue',
+ 'FormArray',
+ 'Final'].map(n => n + ' Demo');
+
+ final = this.demos.length;
+ demo = this.final; // current demo
+
+ heroes: Observable;
+ isLoading = false;
+ selectedHero: Hero;
+
+ constructor(private heroService: HeroService) { }
+
+ getHeroes() {
+ this.isLoading = true;
+ this.heroes = this.heroService.getHeroes()
+ .finally(() => this.isLoading = false);
+ this.selectedHero = undefined;
+ }
+
+ select(hero: Hero) { this.selectedHero = hero; }
+
+ selectDemo(demo: number) {
+ this.demo = demo + 1;
+ this.getHeroes();
+ }
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/demo.module.ts b/public/docs/_examples/reactive-forms/ts/src/app/demo.module.ts
new file mode 100644
index 0000000000..dac9145ca9
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/demo.module.ts
@@ -0,0 +1,33 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import { AppModule } from './app.module';
+import { DemoComponent } from './demo.component';
+import { HeroDetailComponent1 } from './hero-detail-1.component';
+import { HeroDetailComponent2 } from './hero-detail-2.component';
+import { HeroDetailComponent3 } from './hero-detail-3.component';
+import { HeroDetailComponent4 } from './hero-detail-4.component';
+import { HeroDetailComponent5 } from './hero-detail-5.component';
+import { HeroDetailComponent6 } from './hero-detail-6.component';
+import { HeroDetailComponent7 } from './hero-detail-7.component';
+import { HeroDetailComponent8 } from './hero-detail-8.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ ReactiveFormsModule,
+ AppModule,
+ ],
+ declarations: [ DemoComponent,
+ HeroDetailComponent1,
+ HeroDetailComponent2,
+ HeroDetailComponent3,
+ HeroDetailComponent4,
+ HeroDetailComponent5,
+ HeroDetailComponent6,
+ HeroDetailComponent7,
+ HeroDetailComponent8],
+ bootstrap: [ DemoComponent ]
+})
+export class DemoModule { }
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-1.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-1.component.html
new file mode 100644
index 0000000000..7217708d22
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-1.component.html
@@ -0,0 +1,8 @@
+
+Hero Detail
+Just a FormControl
+Name:
+
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-1.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-1.component.ts
new file mode 100644
index 0000000000..318dfff145
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-1.component.ts
@@ -0,0 +1,14 @@
+/* tslint:disable:component-class-suffix */
+// #docregion imports
+import { Component } from '@angular/core';
+import { FormControl } from '@angular/forms';
+// #enddocregion
+
+@Component({
+ selector: 'hero-detail-1',
+ templateUrl: './hero-detail-1.component.html'
+})
+// #docregion v1
+export class HeroDetailComponent1 {
+ name = new FormControl();
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-2.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-2.component.html
new file mode 100644
index 0000000000..1e98354842
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-2.component.html
@@ -0,0 +1,18 @@
+
+Hero Detail
+FormControl in a FormGroup
+
+
+
+
+Form value: {{ heroForm.value | json }}
+
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-2.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-2.component.ts
new file mode 100644
index 0000000000..6aa3c1ed3a
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-2.component.ts
@@ -0,0 +1,17 @@
+/* tslint:disable:component-class-suffix */
+// #docregion imports
+import { Component } from '@angular/core';
+import { FormControl, FormGroup } from '@angular/forms';
+// #enddocregion imports
+
+@Component({
+ selector: 'hero-detail-2',
+ templateUrl: './hero-detail-2.component.html'
+})
+// #docregion v2
+export class HeroDetailComponent2 {
+ heroForm = new FormGroup ({
+ name: new FormControl()
+ });
+}
+// #enddocregion v2
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3.component.html
new file mode 100644
index 0000000000..8edc544dd4
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3.component.html
@@ -0,0 +1,16 @@
+
+Hero Detail
+A FormGroup with a single FormControl using FormBuilder
+
+
+
+
+Form value: {{ heroForm.value | json }}
+Form status: {{ heroForm.status | json }}
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3.component.ts
new file mode 100644
index 0000000000..54a4e93361
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3.component.ts
@@ -0,0 +1,27 @@
+/* tslint:disable:component-class-suffix */
+// #docregion imports
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+// #enddocregion imports
+
+@Component({
+ selector: 'hero-detail-3',
+ templateUrl: './hero-detail-3.component.html'
+})
+// #docregion v3
+export class HeroDetailComponent3 {
+ heroForm: FormGroup; // <--- heroForm is of type FormGroup
+
+ constructor(private fb: FormBuilder) { // <--- inject FormBuilder
+ this.createForm();
+ }
+
+ createForm() {
+ // #docregion required
+ this.heroForm = this.fb.group({
+ name: ['', Validators.required ],
+ });
+ // #enddocregion required
+ }
+}
+// #enddocregion v3
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3a.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3a.component.ts
new file mode 100644
index 0000000000..2347869967
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-3a.component.ts
@@ -0,0 +1,25 @@
+/* tslint:disable:component-class-suffix */
+// #docregion imports
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup } from '@angular/forms';
+// #enddocregion imports
+
+@Component({
+ selector: 'hero-detail-3',
+ templateUrl: './hero-detail-3.component.html'
+})
+// #docregion v3a
+export class HeroDetailComponent3 {
+ heroForm: FormGroup; // <--- heroForm is of type FormGroup
+
+ constructor(private fb: FormBuilder) { // <--- inject FormBuilder
+ this.createForm();
+ }
+
+ createForm() {
+ this.heroForm = this.fb.group({
+ name: '', // <--- the FormControl called "name"
+ });
+ }
+}
+// #enddocregion v3a
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-4.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-4.component.html
new file mode 100644
index 0000000000..30529868e9
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-4.component.html
@@ -0,0 +1,46 @@
+
+Hero Detail
+A FormGroup with multiple FormControls
+
+
+
+Form value: {{ heroForm.value | json }}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-4.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-4.component.ts
new file mode 100644
index 0000000000..d2f1ea2f96
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-4.component.ts
@@ -0,0 +1,34 @@
+/* tslint:disable:component-class-suffix */
+// #docregion imports
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+import { states } from './data-model';
+// #enddocregion imports
+
+@Component({
+ selector: 'hero-detail-4',
+ templateUrl: './hero-detail-4.component.html'
+})
+// #docregion v4
+export class HeroDetailComponent4 {
+ heroForm: FormGroup;
+ states = states;
+
+ constructor(private fb: FormBuilder) {
+ this.createForm();
+ }
+
+ createForm() {
+ this.heroForm = this.fb.group({
+ name: ['', Validators.required ],
+ street: '',
+ city: '',
+ state: '',
+ zip: '',
+ power: '',
+ sidekick: ''
+ });
+ }
+}
+// #enddocregion v4
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-5.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-5.component.html
new file mode 100644
index 0000000000..2a41b4f2f9
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-5.component.html
@@ -0,0 +1,56 @@
+
+
+
+heroForm value: {{ heroForm.value | json}}
+Extra info for the curious:
+
+Name value: {{ heroForm.get('name').value }}
+
+
+
+Street value: {{ heroForm.get('address.street').value}}
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-5.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-5.component.ts
new file mode 100644
index 0000000000..f4be33389f
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-5.component.ts
@@ -0,0 +1,35 @@
+/* tslint:disable:component-class-suffix */
+import { Component } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+import { states } from './data-model';
+
+@Component({
+ selector: 'hero-detail-5',
+ templateUrl: './hero-detail-5.component.html'
+})
+// #docregion v5
+export class HeroDetailComponent5 {
+ heroForm: FormGroup;
+ states = states;
+
+ constructor(private fb: FormBuilder) {
+ this.createForm();
+ }
+
+ createForm() {
+ this.heroForm = this.fb.group({ // <-- the parent FormGroup
+ name: ['', Validators.required ],
+ address: this.fb.group({ // <-- the child FormGroup
+ street: '',
+ city: '',
+ state: '',
+ zip: ''
+ }),
+ power: '',
+ sidekick: ''
+ });
+ }
+}
+// #enddocregion v5
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-6.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-6.component.html
new file mode 100644
index 0000000000..4c8b5c9e05
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-6.component.html
@@ -0,0 +1,46 @@
+
+Hero Detail
+PatchValue to initialize a value
+
+
+
+Form value: {{ heroForm.value | json }}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-6.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-6.component.ts
new file mode 100644
index 0000000000..d5136822d1
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-6.component.ts
@@ -0,0 +1,58 @@
+/* tslint:disable:component-class-suffix */
+// #docregion import-input
+import { Component, Input, OnChanges } from '@angular/core';
+// #enddocregion import-input
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+// #docregion import-hero
+import { Hero, states } from './data-model';
+// #enddocregion import-hero
+
+////////// 6 ////////////////////
+
+@Component({
+ selector: 'hero-detail-6',
+ templateUrl: './hero-detail-5.component.html'
+})
+// #docregion v6
+export class HeroDetailComponent6 implements OnChanges {
+ // #docregion hero
+ @Input() hero: Hero;
+ // #enddocregion hero
+
+ heroForm: FormGroup;
+ states = states;
+
+ constructor(private fb: FormBuilder) {
+ this.createForm();
+ }
+
+ createForm() {
+ // #docregion hero-form-model
+ this.heroForm = this.fb.group({
+ name: ['', Validators.required ],
+ address: this.fb.group({
+ street: '',
+ city: '',
+ state: '',
+ zip: ''
+ }),
+ power: '',
+ sidekick: ''
+ });
+ // #enddocregion hero-form-model
+ }
+
+ // #docregion patch-value-on-changes
+ ngOnChanges() { // <-- wrap patchValue in ngOnChanges
+ this.heroForm.reset();
+ // #docregion patch-value
+ this.heroForm.patchValue({
+ name: this.hero.name
+ });
+ // #enddocregion patch-value
+ }
+ // #enddocregion patch-value-on-changes
+}
+
+// #enddocregion v6
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-7.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-7.component.html
new file mode 100644
index 0000000000..6d68b49b4d
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-7.component.html
@@ -0,0 +1,46 @@
+
+Hero Detail
+A FormGroup with multiple FormControls
+
+
+
+Form value: {{ heroForm.value | json }}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-7.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-7.component.ts
new file mode 100644
index 0000000000..497c7f6a56
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-7.component.ts
@@ -0,0 +1,69 @@
+/* tslint:disable:component-class-suffix */
+// #docplaster
+// #docregion imports
+import { Component, Input, OnChanges } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+// #docregion import-address
+import { Address, Hero, states } from './data-model';
+// #enddocregion import-address
+
+// #enddocregion imports
+
+@Component({
+ selector: 'hero-detail-7',
+ templateUrl: './hero-detail-5.component.html'
+})
+// #docregion v7
+export class HeroDetailComponent7 implements OnChanges {
+ @Input() hero: Hero;
+
+ heroForm: FormGroup;
+ states = states;
+
+ constructor(private fb: FormBuilder) {
+ this.createForm();
+ }
+
+ createForm() {
+ // #docregion address-form-group
+ this.heroForm = this.fb.group({
+ name: ['', Validators.required ],
+ address: this.fb.group(new Address()), // <-- a FormGroup with a new address
+ power: '',
+ sidekick: ''
+ });
+ // #enddocregion address-form-group
+ }
+
+ // #docregion ngOnChanges
+ ngOnChanges() {
+ this.heroForm.reset({
+ name: this.hero.name,
+ address: this.hero.addresses[0] || new Address()
+ });
+ }
+ // #enddocregion ngOnChanges
+
+ /* First version of ngOnChanges
+ // #docregion ngOnChanges-1
+ ngOnChanges()
+ // #enddocregion ngOnChanges-1
+ */
+ ngOnChanges1() {
+ // #docregion reset
+ this.heroForm.reset();
+ // #enddocregion reset
+ // #docregion ngOnChanges-1
+ // #docregion set-value
+ this.heroForm.setValue({
+ name: this.hero.name,
+ // #docregion set-value-address
+ address: this.hero.addresses[0] || new Address()
+ // #enddocregion set-value-address
+ });
+ // #enddocregion set-value
+ }
+ // #enddocregion ngOnChanges-1
+}
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-8.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-8.component.html
new file mode 100644
index 0000000000..d8e47ae798
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-8.component.html
@@ -0,0 +1,70 @@
+
+Using FormArray to add groups
+
+
+
+heroForm value: {{ heroForm.value | json}}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-8.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-8.component.ts
new file mode 100644
index 0000000000..1141c836a7
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail-8.component.ts
@@ -0,0 +1,68 @@
+/* tslint:disable:component-class-suffix */
+// #docregion imports
+import { Component, Input, OnChanges } from '@angular/core';
+import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
+
+import { Address, Hero, states } from './data-model';
+// #enddocregion imports
+
+@Component({
+ selector: 'hero-detail-8',
+ templateUrl: './hero-detail-8.component.html'
+})
+// #docregion v8
+export class HeroDetailComponent8 implements OnChanges {
+ @Input() hero: Hero;
+
+ heroForm: FormGroup;
+ states = states;
+
+ // #docregion ctor
+ constructor(private fb: FormBuilder) {
+ this.createForm();
+ this.logNameChange();
+ }
+ // #enddocregion ctor
+
+ createForm() {
+ // #docregion secretLairs-form-array
+ this.heroForm = this.fb.group({
+ name: ['', Validators.required ],
+ secretLairs: this.fb.array([]), // <-- secretLairs as an empty FormArray
+ power: '',
+ sidekick: ''
+ });
+ // #enddocregion secretLairs-form-array
+ }
+
+ logNameChange() {/* Coming soon */}
+
+ // #docregion onchanges
+ ngOnChanges() {
+ this.heroForm.reset({
+ name: this.hero.name
+ });
+ this.setAddresses(this.hero.addresses);
+ }
+ // #enddocregion onchanges
+
+ // #docregion get-secret-lairs
+ get secretLairs(): FormArray {
+ return this.heroForm.get('secretLairs') as FormArray;
+ };
+ // #enddocregion get-secret-lairs
+
+ // #docregion set-addresses
+ setAddresses(addresses: Address[]) {
+ const addressFGs = addresses.map(address => this.fb.group(address));
+ const addressFormArray = this.fb.array(addressFGs);
+ this.heroForm.setControl('secretLairs', addressFormArray);
+ }
+ // #enddocregion set-addresses
+
+ // #docregion add-lair
+ addLair() {
+ this.secretLairs.push(this.fb.group(new Address()));
+ }
+ // #enddocregion add-lair
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail.component.html
new file mode 100644
index 0000000000..8f5b8bf2c8
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail.component.html
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+heroForm value: {{ heroForm.value | json}}
+
+
+Name change log
+{{name}}
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-detail.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail.component.ts
new file mode 100644
index 0000000000..4cb69aaedf
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-detail.component.ts
@@ -0,0 +1,107 @@
+// #docplaster
+// #docregion
+import { Component, Input, OnChanges } from '@angular/core';
+import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
+
+import { Address, Hero, states } from './data-model';
+// #docregion import-service
+import { HeroService } from './hero.service';
+// #enddocregion import-service
+
+// #docregion metadata
+@Component({
+ selector: 'hero-detail',
+ templateUrl: './hero-detail.component.html'
+})
+// #enddocregion metadata
+export class HeroDetailComponent implements OnChanges {
+ @Input() hero: Hero;
+
+ heroForm: FormGroup;
+ // #docregion log-name-change
+ nameChangeLog: string[] = [];
+ // #enddocregion log-name-change
+ states = states;
+
+ // #docregion ctor
+ constructor(
+ private fb: FormBuilder,
+ private heroService: HeroService) {
+
+ this.createForm();
+ this.logNameChange();
+ }
+ // #enddocregion ctor
+
+ createForm() {
+ this.heroForm = this.fb.group({
+ name: '',
+ secretLairs: this.fb.array([]),
+ power: '',
+ sidekick: ''
+ });
+ }
+
+ ngOnChanges() {
+ this.heroForm.reset({
+ name: this.hero.name
+ });
+ this.setAddresses(this.hero.addresses);
+ }
+
+ get secretLairs(): FormArray {
+ return this.heroForm.get('secretLairs') as FormArray;
+ };
+
+ setAddresses(addresses: Address[]) {
+ const addressFGs = addresses.map(address => this.fb.group(address));
+ const addressFormArray = this.fb.array(addressFGs);
+ this.heroForm.setControl('secretLairs', addressFormArray);
+ }
+
+ addLair() {
+ this.secretLairs.push(this.fb.group(new Address()));
+ }
+
+ // #docregion on-submit
+ onSubmit() {
+ this.hero = this.prepareSaveHero();
+ this.heroService.updateHero(this.hero).subscribe(/* error handling */);
+ this.ngOnChanges();
+ }
+ // #enddocregion on-submit
+
+ // #docregion prepare-save-hero
+ prepareSaveHero(): Hero {
+ const formModel = this.heroForm.value;
+
+ // deep copy of form model lairs
+ const secretLairsDeepCopy: Address[] = formModel.secretLairs.map(
+ (address: Address) => Object.assign({}, address)
+ );
+
+ // return new `Hero` object containing a combination of original hero value(s)
+ // and deep copies of changed form model values
+ const saveHero: Hero = {
+ id: this.hero.id,
+ name: formModel.name as string,
+ // addresses: formModel.secretLairs // <-- bad!
+ addresses: secretLairsDeepCopy
+ };
+ return saveHero;
+ }
+ // #enddocregion prepare-save-hero
+
+ // #docregion revert
+ revert() { this.ngOnChanges(); }
+ // #enddocregion revert
+
+ // #docregion log-name-change
+ logNameChange() {
+ const nameControl = this.heroForm.get('name');
+ nameControl.valueChanges.forEach(
+ (value: string) => this.nameChangeLog.push(value)
+ );
+ }
+ // #enddocregion log-name-change
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.1.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.1.html
new file mode 100644
index 0000000000..fa76c84f54
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.1.html
@@ -0,0 +1,8 @@
+
+
+ {{hero.name}}
+
+
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.html b/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.html
new file mode 100644
index 0000000000..d0fb2ee920
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.html
@@ -0,0 +1,17 @@
+
+Loading heroes ...
+Select a hero:
+
+
+ Refresh
+ {{hero.name}}
+
+
+
+
+ Hero Detail
+ Editing: {{selectedHero.name}}
+
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.ts
new file mode 100644
index 0000000000..98092adcdb
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero-list.component.ts
@@ -0,0 +1,31 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/finally';
+
+import { Hero } from './data-model';
+import { HeroService } from './hero.service';
+
+@Component({
+ selector: 'hero-list',
+ templateUrl: './hero-list.component.html'
+})
+export class HeroListComponent implements OnInit {
+ heroes: Observable;
+ isLoading = false;
+ selectedHero: Hero;
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() { this.getHeroes(); }
+
+ getHeroes() {
+ this.isLoading = true;
+ this.heroes = this.heroService.getHeroes()
+ // Todo: error handling
+ .finally(() => this.isLoading = false);
+ this.selectedHero = undefined;
+ }
+
+ select(hero: Hero) { this.selectedHero = hero; }
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/app/hero.service.ts b/public/docs/_examples/reactive-forms/ts/src/app/hero.service.ts
new file mode 100644
index 0000000000..6600586de7
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/app/hero.service.ts
@@ -0,0 +1,26 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import { of } from 'rxjs/observable/of';
+import 'rxjs/add/operator/delay';
+
+import { Hero, heroes } from './data-model';
+
+@Injectable()
+export class HeroService {
+
+ delayMs = 500;
+
+ // Fake server get; assume nothing can go wrong
+ getHeroes(): Observable {
+ return of(heroes).delay(this.delayMs); // simulate latency with delay
+ }
+
+ // Fake server update; assume nothing can go wrong
+ updateHero(hero: Hero): Observable {
+ const oldHero = heroes.find(h => h.id === hero.id);
+ const newHero = Object.assign(oldHero, hero); // Demo: mutate cached hero
+ return of(newHero).delay(this.delayMs); // simulate latency with delay
+ }
+}
diff --git a/public/docs/_examples/reactive-forms/ts/src/index-final.html b/public/docs/_examples/reactive-forms/ts/src/index-final.html
new file mode 100644
index 0000000000..9ef0a379e0
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/index-final.html
@@ -0,0 +1,31 @@
+
+
+
+
+ Hero Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/index.html b/public/docs/_examples/reactive-forms/ts/src/index.html
new file mode 100644
index 0000000000..802d12f211
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/index.html
@@ -0,0 +1,31 @@
+
+
+
+
+ Hero Form
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/reactive-forms/ts/src/main-final.ts b/public/docs/_examples/reactive-forms/ts/src/main-final.ts
new file mode 100644
index 0000000000..7572d1f1d3
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/main-final.ts
@@ -0,0 +1,5 @@
+// tslint:disable:no-unused-variable
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/reactive-forms/ts/src/main.ts b/public/docs/_examples/reactive-forms/ts/src/main.ts
new file mode 100644
index 0000000000..f415a71708
--- /dev/null
+++ b/public/docs/_examples/reactive-forms/ts/src/main.ts
@@ -0,0 +1,6 @@
+// tslint:disable:no-unused-variable
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module'; // just the final version
+import { DemoModule } from './app/demo.module'; // demo picker
+
+platformBrowserDynamic().bootstrapModule(DemoModule); // (AppModule);
diff --git a/public/docs/_examples/router/e2e-spec.ts b/public/docs/_examples/router/e2e-spec.ts
new file mode 100644
index 0000000000..2e9c6bafeb
--- /dev/null
+++ b/public/docs/_examples/router/e2e-spec.ts
@@ -0,0 +1,162 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ExpectedConditions } from 'protractor';
+
+const numDashboardTabs = 5;
+const numCrises = 4;
+const numHeroes = 6;
+const EC = ExpectedConditions;
+
+describe('Router', () => {
+
+ beforeAll(() => browser.get(''));
+
+ function getPageStruct() {
+ const hrefEles = element.all(by.css('my-app a'));
+ const crisisDetail = element.all(by.css('my-app > ng-component > ng-component > ng-component > div')).first();
+ const heroDetail = element(by.css('my-app > ng-component > div'));
+
+ return {
+ hrefs: hrefEles,
+ activeHref: element(by.css('my-app a.active')),
+
+ crisisHref: hrefEles.get(0),
+ crisisList: element.all(by.css('my-app > ng-component > ng-component li')),
+ crisisDetail: crisisDetail,
+ crisisDetailTitle: crisisDetail.element(by.xpath('*[1]')),
+
+ heroesHref: hrefEles.get(1),
+ heroesList: element.all(by.css('my-app > ng-component li')),
+ heroDetail: heroDetail,
+ heroDetailTitle: heroDetail.element(by.xpath('*[1]')),
+
+ adminHref: hrefEles.get(2),
+ adminPreloadList: element.all(by.css('my-app > ng-component > ng-component > ul > li')),
+
+ loginHref: hrefEles.get(3),
+ loginButton: element.all(by.css('my-app > ng-component > p > button')),
+
+ contactHref: hrefEles.get(4),
+ contactCancelButton: element.all(by.buttonText('Cancel')),
+
+ outletComponents: element.all(by.css('my-app > ng-component'))
+ };
+ }
+
+ it('has expected dashboard tabs', () => {
+ const page = getPageStruct();
+ expect(page.hrefs.count()).toEqual(numDashboardTabs, 'dashboard tab count');
+ expect(page.crisisHref.getText()).toEqual('Crisis Center');
+ expect(page.heroesHref.getText()).toEqual('Heroes');
+ expect(page.adminHref.getText()).toEqual('Admin');
+ expect(page.loginHref.getText()).toEqual('Login');
+ expect(page.contactHref.getText()).toEqual('Contact');
+ });
+
+ it('has heroes selected as opening tab', () => {
+ const page = getPageStruct();
+ expect(page.activeHref.getText()).toEqual('Heroes');
+ });
+
+ it('has crises center items', async () => {
+ const page = getPageStruct();
+ await page.crisisHref.click();
+ expect(page.activeHref.getText()).toEqual('Crisis Center');
+ expect(page.crisisList.count()).toBe(numCrises, 'crisis list count');
+ });
+
+ it('has hero items', async () => {
+ const page = getPageStruct();
+ await page.heroesHref.click();
+ expect(page.activeHref.getText()).toEqual('Heroes');
+ expect(page.heroesList.count()).toBe(numHeroes, 'hero list count');
+ });
+
+ it('toggles views', async () => {
+ const page = getPageStruct();
+ await page.crisisHref.click();
+ expect(page.activeHref.getText()).toEqual('Crisis Center');
+ expect(page.crisisList.count()).toBe(numCrises, 'crisis list count');
+ await page.heroesHref.click();
+ expect(page.activeHref.getText()).toEqual('Heroes');
+ expect(page.heroesList.count()).toBe(numHeroes, 'hero list count');
+ });
+
+ it('saves changed crisis details', async () => {
+ const page = getPageStruct();
+ await page.crisisHref.click();
+ await crisisCenterEdit(2, true);
+ });
+
+ it('can cancel changed crisis details', async () => {
+ const page = getPageStruct();
+ await page.crisisHref.click();
+ await crisisCenterEdit(3, false);
+ });
+
+ it('saves changed hero details', async () => {
+ const page = getPageStruct();
+ await page.heroesHref.click();
+ const heroEle = page.heroesList.get(4);
+ let text = await heroEle.getText();
+ expect(text.length).toBeGreaterThan(0, 'hero item text length');
+ // remove leading id from text
+ const heroText = text.substr(text.indexOf(' ')).trim();
+
+ await heroEle.click();
+ expect(page.heroesList.count()).toBe(0, 'hero list count');
+ expect(page.heroDetail.isPresent()).toBe(true, 'hero detail');
+ expect(page.heroDetailTitle.getText()).toContain(heroText);
+ let inputEle = page.heroDetail.element(by.css('input'));
+ await inputEle.sendKeys('-foo');
+ expect(page.heroDetailTitle.getText()).toContain(heroText + '-foo');
+
+ let buttonEle = page.heroDetail.element(by.css('button'));
+ await buttonEle.click();
+ expect(heroEle.getText()).toContain(heroText + '-foo');
+ });
+
+ it('sees preloaded modules', async () => {
+ const page = getPageStruct();
+ await page.loginHref.click();
+ await page.loginButton.click();
+ const list = page.adminPreloadList;
+ expect(list.count()).toBe(1, 'preloaded module');
+ expect(await list.first().getText()).toBe('crisis-center', 'first preloaded module');
+ });
+
+ it('sees the secondary route', async () => {
+ const page = getPageStruct();
+ await page.heroesHref.click();
+ await page.contactHref.click();
+ expect(page.outletComponents.count()).toBe(2, 'route count');
+ });
+
+ async function crisisCenterEdit(index: number, save: boolean) {
+ const page = getPageStruct();
+ await page.crisisHref.click();
+ let crisisEle = page.crisisList.get(index);
+ let text = await crisisEle.getText();
+ expect(text.length).toBeGreaterThan(0, 'crisis item text length');
+ // remove leading id from text
+ const crisisText = text.substr(text.indexOf(' ')).trim();
+
+ await crisisEle.click();
+ expect(page.crisisDetail.isPresent()).toBe(true, 'crisis detail present');
+ expect(page.crisisDetailTitle.getText()).toContain(crisisText);
+ let inputEle = page.crisisDetail.element(by.css('input'));
+ await inputEle.sendKeys('-foo');
+
+ let buttonEle = page.crisisDetail.element(by.buttonText(save ? 'Save' : 'Cancel'));
+ await buttonEle.click();
+ crisisEle = page.crisisList.get(index);
+ if (save) {
+ expect(crisisEle.getText()).toEqual(crisisText + '-foo');
+ } else {
+ await browser.wait(EC.alertIsPresent(), 4000);
+ await browser.switchTo().alert().accept();
+ expect(crisisEle.getText()).toEqual(crisisText);
+ }
+ }
+
+});
diff --git a/public/docs/_examples/router/ts/example-config.json b/public/docs/_examples/router/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/router/ts/plnkr.json b/public/docs/_examples/router/ts/plnkr.json
new file mode 100644
index 0000000000..23ed1a1606
--- /dev/null
+++ b/public/docs/_examples/router/ts/plnkr.json
@@ -0,0 +1,12 @@
+{
+ "description": "Router",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[0-9].*",
+ "!app/crisis-list.component.ts",
+ "!app/hero-list.component.ts"
+ ],
+ "tags": ["router"]
+}
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.1.ts b/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.1.ts
new file mode 100644
index 0000000000..ffa3e3cb8f
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.1.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ Dashboard
+ `
+})
+export class AdminDashboardComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.2.ts b/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.2.ts
new file mode 100644
index 0000000000..8c8e481643
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.2.ts
@@ -0,0 +1,33 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+
+@Component({
+ template: `
+ Dashboard
+
+ Session ID: {{ sessionId | async }}
+
+ Token: {{ token | async }}
+ `
+})
+export class AdminDashboardComponent implements OnInit {
+ sessionId: Observable;
+ token: Observable;
+
+ constructor(private route: ActivatedRoute) {}
+
+ ngOnInit() {
+ // Capture the session ID if available
+ this.sessionId = this.route
+ .queryParams
+ .map(params => params['session_id'] || 'None');
+
+ // Capture the fragment if available
+ this.token = this.route
+ .fragment
+ .map(fragment => fragment || 'None');
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.ts b/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.ts
new file mode 100644
index 0000000000..b3fc839616
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-dashboard.component.ts
@@ -0,0 +1,47 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+
+import { SelectivePreloadingStrategy } from '../selective-preloading-strategy';
+
+import 'rxjs/add/operator/map';
+
+@Component({
+ template: `
+ Dashboard
+
+ Session ID: {{ sessionId | async }}
+
+ Token: {{ token | async }}
+
+ Preloaded Modules
+
+ `
+})
+export class AdminDashboardComponent implements OnInit {
+ sessionId: Observable;
+ token: Observable;
+ modules: string[];
+
+ constructor(
+ private route: ActivatedRoute,
+ private preloadStrategy: SelectivePreloadingStrategy
+ ) {
+ this.modules = preloadStrategy.preloadedModules;
+ }
+
+ ngOnInit() {
+ // Capture the session ID if available
+ this.sessionId = this.route
+ .queryParams
+ .map(params => params['session_id'] || 'None');
+
+ // Capture the fragment if available
+ this.token = this.route
+ .fragment
+ .map(fragment => fragment || 'None');
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.1.ts b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.1.ts
new file mode 100644
index 0000000000..e7d83f113f
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.1.ts
@@ -0,0 +1,39 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { AdminComponent } from './admin.component';
+import { AdminDashboardComponent } from './admin-dashboard.component';
+import { ManageCrisesComponent } from './manage-crises.component';
+import { ManageHeroesComponent } from './manage-heroes.component';
+
+// #docregion admin-routes
+const adminRoutes: Routes = [
+ {
+ path: 'admin',
+ component: AdminComponent,
+ children: [
+ {
+ path: '',
+ children: [
+ { path: 'crises', component: ManageCrisesComponent },
+ { path: 'heroes', component: ManageHeroesComponent },
+ { path: '', component: AdminDashboardComponent }
+ ]
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(adminRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AdminRoutingModule {}
+// #enddocregion admin-routes
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.2.ts b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.2.ts
new file mode 100644
index 0000000000..d945201afe
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.2.ts
@@ -0,0 +1,44 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { AdminComponent } from './admin.component';
+import { AdminDashboardComponent } from './admin-dashboard.component';
+import { ManageCrisesComponent } from './manage-crises.component';
+import { ManageHeroesComponent } from './manage-heroes.component';
+
+// #docregion admin-route
+import { AuthGuard } from '../auth-guard.service';
+
+const adminRoutes: Routes = [
+ {
+ path: 'admin',
+ component: AdminComponent,
+ canActivate: [AuthGuard],
+ children: [
+ {
+ path: '',
+ children: [
+ { path: 'crises', component: ManageCrisesComponent },
+ { path: 'heroes', component: ManageHeroesComponent },
+ { path: '', component: AdminDashboardComponent }
+ ],
+ // #enddocregion admin-route
+ canActivateChild: [AuthGuard]
+ // #docregion admin-route
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(adminRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AdminRoutingModule {}
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.3.ts b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.3.ts
new file mode 100644
index 0000000000..63f1c9aaf4
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.3.ts
@@ -0,0 +1,43 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { AdminComponent } from './admin.component';
+import { AdminDashboardComponent } from './admin-dashboard.component';
+import { ManageCrisesComponent } from './manage-crises.component';
+import { ManageHeroesComponent } from './manage-heroes.component';
+
+// #docregion admin-route
+import { AuthGuard } from '../auth-guard.service';
+
+// #docregion can-activate-child
+const adminRoutes: Routes = [
+ {
+ path: 'admin',
+ component: AdminComponent,
+ canActivate: [AuthGuard],
+ children: [
+ {
+ path: '',
+ canActivateChild: [AuthGuard],
+ children: [
+ { path: 'crises', component: ManageCrisesComponent },
+ { path: 'heroes', component: ManageHeroesComponent },
+ { path: '', component: AdminDashboardComponent }
+ ]
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(adminRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AdminRoutingModule {}
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.ts b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.ts
new file mode 100644
index 0000000000..2b1048d110
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin-routing.module.ts
@@ -0,0 +1,41 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { AdminComponent } from './admin.component';
+import { AdminDashboardComponent } from './admin-dashboard.component';
+import { ManageCrisesComponent } from './manage-crises.component';
+import { ManageHeroesComponent } from './manage-heroes.component';
+
+import { AuthGuard } from '../auth-guard.service';
+
+const adminRoutes: Routes = [
+ {
+ path: '',
+ component: AdminComponent,
+ canActivate: [AuthGuard],
+ children: [
+ {
+ path: '',
+ canActivateChild: [AuthGuard],
+ children: [
+ { path: 'crises', component: ManageCrisesComponent },
+ { path: 'heroes', component: ManageHeroesComponent },
+ { path: '', component: AdminDashboardComponent }
+ ]
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(adminRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AdminRoutingModule {}
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin.component.ts b/public/docs/_examples/router/ts/src/app/admin/admin.component.ts
new file mode 100644
index 0000000000..30abfa4524
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin.component.ts
@@ -0,0 +1,17 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ ADMIN
+
+ Dashboard
+ Manage Crises
+ Manage Heroes
+
+
+ `
+})
+export class AdminComponent {
+}
diff --git a/public/docs/_examples/router/ts/src/app/admin/admin.module.ts b/public/docs/_examples/router/ts/src/app/admin/admin.module.ts
new file mode 100644
index 0000000000..2736f00e1d
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/admin.module.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { AdminComponent } from './admin.component';
+import { AdminDashboardComponent } from './admin-dashboard.component';
+import { ManageCrisesComponent } from './manage-crises.component';
+import { ManageHeroesComponent } from './manage-heroes.component';
+
+import { AdminRoutingModule } from './admin-routing.module';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ AdminRoutingModule
+ ],
+ declarations: [
+ AdminComponent,
+ AdminDashboardComponent,
+ ManageCrisesComponent,
+ ManageHeroesComponent
+ ]
+})
+export class AdminModule {}
diff --git a/public/docs/_examples/router/ts/src/app/admin/manage-crises.component.ts b/public/docs/_examples/router/ts/src/app/admin/manage-crises.component.ts
new file mode 100644
index 0000000000..d3176563eb
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/manage-crises.component.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ Manage your crises here
+ `
+})
+export class ManageCrisesComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/admin/manage-heroes.component.ts b/public/docs/_examples/router/ts/src/app/admin/manage-heroes.component.ts
new file mode 100644
index 0000000000..7f3a39893d
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/admin/manage-heroes.component.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ Manage your heroes here
+ `
+})
+export class ManageHeroesComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/animations.ts b/public/docs/_examples/router/ts/src/app/animations.ts
new file mode 100644
index 0000000000..d7a55d721f
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/animations.ts
@@ -0,0 +1,26 @@
+// #docregion
+import { animate, AnimationEntryMetadata, state, style, transition, trigger } from '@angular/core';
+
+// Component transition animations
+export const slideInDownAnimation: AnimationEntryMetadata =
+ trigger('routeAnimation', [
+ state('*',
+ style({
+ opacity: 1,
+ transform: 'translateX(0)'
+ })
+ ),
+ transition(':enter', [
+ style({
+ opacity: 0,
+ transform: 'translateX(-100%)'
+ }),
+ animate('0.2s ease-in')
+ ]),
+ transition(':leave', [
+ animate('0.5s ease-out', style({
+ opacity: 0,
+ transform: 'translateY(100%)'
+ }))
+ ])
+ ]);
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.1.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.1.ts
new file mode 100644
index 0000000000..8146e54671
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.1.ts
@@ -0,0 +1,26 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisListComponent } from './crisis-list.component';
+import { HeroListComponent } from './hero-list.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+// #docregion appRoutes
+const appRoutes: Routes = [
+ { path: 'crisis-center', component: CrisisListComponent },
+ { path: 'heroes', component: HeroListComponent },
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+// #enddocregion appRoutes
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(appRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.2.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.2.ts
new file mode 100644
index 0000000000..42ac84a481
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.2.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisListComponent } from './crisis-list.component';
+// import { HeroListComponent } from './hero-list.component'; // <-- delete this line
+import { PageNotFoundComponent } from './not-found.component';
+
+const appRoutes: Routes = [
+ { path: 'crisis-center', component: CrisisListComponent },
+ // { path: 'heroes', component: HeroListComponent }, // <-- delete this line
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(appRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.3.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.3.ts
new file mode 100644
index 0000000000..538ff9aafc
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.3.ts
@@ -0,0 +1,31 @@
+// #docplaster
+// #docregion , v3
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { ComposeMessageComponent } from './compose-message.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+const appRoutes: Routes = [
+// #enddocregion v3
+// #docregion compose
+ {
+ path: 'compose',
+ component: ComposeMessageComponent,
+ outlet: 'popup'
+ },
+// #enddocregion compose
+// #docregion v3
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(appRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.4.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.4.ts
new file mode 100644
index 0000000000..6835d24a85
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.4.ts
@@ -0,0 +1,30 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { ComposeMessageComponent } from './compose-message.component';
+import { CanDeactivateGuard } from './can-deactivate-guard.service';
+import { PageNotFoundComponent } from './not-found.component';
+
+const appRoutes: Routes = [
+ {
+ path: 'compose',
+ component: ComposeMessageComponent,
+ outlet: 'popup'
+ },
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(appRoutes)
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ CanDeactivateGuard
+ ]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.5.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.5.ts
new file mode 100644
index 0000000000..2badf7f593
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.5.ts
@@ -0,0 +1,45 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+// #docregion import-router
+import { RouterModule, Routes } from '@angular/router';
+// #enddocregion import-router
+
+import { ComposeMessageComponent } from './compose-message.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { CanDeactivateGuard } from './can-deactivate-guard.service';
+import { AuthGuard } from './auth-guard.service';
+
+
+const appRoutes: Routes = [
+ {
+ path: 'compose',
+ component: ComposeMessageComponent,
+ outlet: 'popup'
+ },
+// #docregion admin, admin-1
+ {
+ path: 'admin',
+ loadChildren: 'app/admin/admin.module#AdminModule',
+// #enddocregion admin-1
+ canLoad: [AuthGuard]
+// #docregion admin-1
+ },
+// #enddocregion admin, admin-1
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(appRoutes)
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ CanDeactivateGuard
+ ]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.6.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.6.ts
new file mode 100644
index 0000000000..df2c8c097d
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.6.ts
@@ -0,0 +1,54 @@
+// #docplaster
+// #docregion, preload-v1
+import { NgModule } from '@angular/core';
+import {
+ RouterModule, Routes,
+// #enddocregion preload-v1
+ PreloadAllModules
+// #docregion preload-v1
+} from '@angular/router';
+
+import { ComposeMessageComponent } from './compose-message.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { CanDeactivateGuard } from './can-deactivate-guard.service';
+import { AuthGuard } from './auth-guard.service';
+
+const appRoutes: Routes = [
+ {
+ path: 'compose',
+ component: ComposeMessageComponent,
+ outlet: 'popup'
+ },
+ {
+ path: 'admin',
+ loadChildren: 'app/admin/admin.module#AdminModule',
+ canLoad: [AuthGuard]
+ },
+ {
+ path: 'crisis-center',
+ loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule'
+ },
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ // #docregion forRoot
+ RouterModule.forRoot(
+ appRoutes
+ // #enddocregion preload-v1
+ , { preloadingStrategy: PreloadAllModules }
+ // #docregion preload-v1
+ )
+ // #enddocregion forRoot
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ CanDeactivateGuard
+ ]
+})
+export class AppRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/app-routing.module.ts b/public/docs/_examples/router/ts/src/app/app-routing.module.ts
new file mode 100644
index 0000000000..cc01ced890
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app-routing.module.ts
@@ -0,0 +1,50 @@
+// #docplaster
+// #docregion, preload-v1
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { ComposeMessageComponent } from './compose-message.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { CanDeactivateGuard } from './can-deactivate-guard.service';
+import { AuthGuard } from './auth-guard.service';
+import { SelectivePreloadingStrategy } from './selective-preloading-strategy';
+
+const appRoutes: Routes = [
+ {
+ path: 'compose',
+ component: ComposeMessageComponent,
+ outlet: 'popup'
+ },
+ {
+ path: 'admin',
+ loadChildren: 'app/admin/admin.module#AdminModule',
+ canLoad: [AuthGuard]
+ },
+ // #docregion preload-v2
+ {
+ path: 'crisis-center',
+ loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule',
+ data: { preload: true }
+ },
+ // #enddocregion preload-v2
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(
+ appRoutes,
+ { preloadingStrategy: SelectivePreloadingStrategy }
+ )
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ CanDeactivateGuard,
+ SelectivePreloadingStrategy
+ ]
+})
+export class AppRoutingModule { }
diff --git a/public/docs/_examples/router/ts/src/app/app.component.1.ts b/public/docs/_examples/router/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..21e9aa417d
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.component.1.ts
@@ -0,0 +1,18 @@
+/* First version */
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ Angular Router
+
+ Crisis Center
+ Heroes
+
+
+ `
+ // #enddocregion template
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/app.component.2.ts b/public/docs/_examples/router/ts/src/app/app.component.2.ts
new file mode 100644
index 0000000000..ffd4d8dfae
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.component.2.ts
@@ -0,0 +1,16 @@
+/* Second Heroes version */
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+ Angular Router
+
+ Crisis Center
+ Heroes
+
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/app.component.3.ts b/public/docs/_examples/router/ts/src/app/app.component.3.ts
new file mode 100644
index 0000000000..6013df6321
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.component.3.ts
@@ -0,0 +1,48 @@
+/* tslint:disable:no-unused-variable */
+// #docplaster
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+
+@Component({
+ selector: 'my-app',
+ /* Typical link
+ // #docregion h-anchor
+ Heroes
+ // #enddocregion h-anchor
+ */
+ /* Incomplete Crisis Center link when CC lacks a default
+ // #docregion cc-anchor-fail
+ // The link now fails with a "non-terminal link" error
+ // #docregion cc-anchor-w-default
+ Crisis Center
+ // #enddocregion cc-anchor-w-default
+ // #enddocregion cc-anchor-fail
+ */
+ /* Crisis Center link when CC lacks a default
+ // #docregion cc-anchor-no-default
+ Crisis Center
+ // #enddocregion cc-anchor-no-default
+ */
+ /* Crisis Center Detail link
+ // #docregion Dragon-anchor
+ Dragon Crisis
+ // #enddocregion Dragon-anchor
+ */
+ /* Crisis Center link with optional query params
+ // #docregion cc-query-params
+ Crisis Center
+ // #enddocregion cc-query-params
+ */
+// #docregion template
+ template: `
+ Angular Router
+
+ Crisis Center
+ Dragon Crisis
+ Shark Crisis
+
+
+ `
+// #enddocregion template
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/app.component.4.ts b/public/docs/_examples/router/ts/src/app/app.component.4.ts
new file mode 100644
index 0000000000..a630703c28
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.component.4.ts
@@ -0,0 +1,23 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ Angular Router
+
+ Crisis Center
+ Heroes
+ // #docregion contact-link
+ Contact
+ // #enddocregion contact-link
+
+ // #docregion outlets
+
+
+ // #enddocregion outlets
+ `
+ // #enddocregion template
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/app.component.5.ts b/public/docs/_examples/router/ts/src/app/app.component.5.ts
new file mode 100644
index 0000000000..24162c6136
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.component.5.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ Angular Router
+
+ Crisis Center
+ Heroes
+ Admin
+ Contact
+
+
+
+ `
+ // #enddocregion template
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/app.component.ts b/public/docs/_examples/router/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..a479680cbe
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.component.ts
@@ -0,0 +1,23 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ Angular Router
+
+ Crisis Center
+ Heroes
+ Admin
+ Login
+ Contact
+
+
+
+ `
+ // #enddocregion template
+})
+export class AppComponent {
+}
diff --git a/public/docs/_examples/router/ts/src/app/app.module.0.ts b/public/docs/_examples/router/ts/src/app/app.module.0.ts
new file mode 100644
index 0000000000..a195dbdd7a
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.0.ts
@@ -0,0 +1,41 @@
+// NEVER USED. For docs only. Should compile though
+// #docplaster
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { HeroListComponent } from './hero-list.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { PageNotFoundComponent } from './not-found.component';
+import { PageNotFoundComponent as HeroDetailComponent } from './not-found.component';
+
+// #docregion
+const appRoutes: Routes = [
+ { path: 'crisis-center', component: CrisisListComponent },
+ { path: 'hero/:id', component: HeroDetailComponent },
+ {
+ path: 'heroes',
+ component: HeroListComponent,
+ data: { title: 'Heroes List' }
+ },
+ { path: '',
+ redirectTo: '/heroes',
+ pathMatch: 'full'
+ },
+ { path: '**', component: PageNotFoundComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot(appRoutes)
+ // other imports here
+ ],
+// #enddocregion
+/*
+// #docregion
+ ...
+})
+export class AppModule { }
+// #enddocregion
+*/
+})
+export class AppModule0 { }
diff --git a/public/docs/_examples/router/ts/src/app/app.module.1.ts b/public/docs/_examples/router/ts/src/app/app.module.1.ts
new file mode 100644
index 0000000000..32f93b8f79
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.1.ts
@@ -0,0 +1,49 @@
+// #docplaster
+// #docregion
+// #docregion first-config
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+// #docregion import-router
+import { RouterModule, Routes } from '@angular/router';
+// #enddocregion import-router
+
+import { AppComponent } from './app.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { HeroListComponent } from './hero-list.component';
+// #enddocregion first-config
+import { PageNotFoundComponent } from './not-found.component';
+// #docregion first-config
+
+// #docregion appRoutes
+const appRoutes: Routes = [
+ { path: 'crisis-center', component: CrisisListComponent },
+ { path: 'heroes', component: HeroListComponent },
+// #enddocregion first-config
+
+ { path: '', redirectTo: '/heroes', pathMatch: 'full' },
+// #docregion wildcard
+ { path: '**', component: PageNotFoundComponent }
+// #enddocregion wildcard
+// #docregion first-config
+];
+// #enddocregion appRoutes
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ RouterModule.forRoot(appRoutes)
+ ],
+ declarations: [
+ AppComponent,
+ HeroListComponent,
+ CrisisListComponent,
+// #enddocregion first-config
+ PageNotFoundComponent
+// #docregion first-config
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/app.module.2.ts b/public/docs/_examples/router/ts/src/app/app.module.2.ts
new file mode 100644
index 0000000000..2ba739168c
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.2.ts
@@ -0,0 +1,31 @@
+// #docplaster
+// #docregion
+// #docregion hero-import
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+
+import { CrisisListComponent } from './crisis-list.component';
+import { HeroListComponent } from './hero-list.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ AppRoutingModule
+ ],
+ declarations: [
+ AppComponent,
+ HeroListComponent,
+ CrisisListComponent,
+ PageNotFoundComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+// #enddocregion hero-import
+export class AppModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/app.module.3.ts b/public/docs/_examples/router/ts/src/app/app.module.3.ts
new file mode 100644
index 0000000000..862faf1c51
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.3.ts
@@ -0,0 +1,29 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+import { HeroesModule } from './heroes/heroes.module';
+
+import { CrisisListComponent } from './crisis-list.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+@NgModule({
+// #docregion module-imports
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HeroesModule,
+ AppRoutingModule
+ ],
+// #enddocregion module-imports
+ declarations: [
+ AppComponent,
+ CrisisListComponent,
+ PageNotFoundComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/router/ts/src/app/app.module.4.ts b/public/docs/_examples/router/ts/src/app/app.module.4.ts
new file mode 100644
index 0000000000..4825572361
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.4.ts
@@ -0,0 +1,46 @@
+// #docplaster
+// #docregion
+// #docregion crisis-center-module, admin-module
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { AppRoutingModule } from './app-routing.module';
+import { HeroesModule } from './heroes/heroes.module';
+import { CrisisCenterModule } from './crisis-center/crisis-center.module';
+// #enddocregion crisis-center-module, admin-module
+import { ComposeMessageComponent } from './compose-message.component';
+// #docregion admin-module
+import { AdminModule } from './admin/admin.module';
+// #docregion crisis-center-module
+
+import { DialogService } from './dialog.service';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ HeroesModule,
+ CrisisCenterModule,
+// #enddocregion crisis-center-module
+ AdminModule,
+// #docregion crisis-center-module
+ AppRoutingModule
+ ],
+ declarations: [
+ AppComponent,
+// #enddocregion admin-module, crisis-center-module
+ ComposeMessageComponent,
+// #docregion admin-module, crisis-center-module
+ PageNotFoundComponent
+ ],
+ providers: [
+ DialogService
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/app.module.5.ts b/public/docs/_examples/router/ts/src/app/app.module.5.ts
new file mode 100644
index 0000000000..ad34668cea
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.5.ts
@@ -0,0 +1,38 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+
+import { HeroesModule } from './heroes/heroes.module';
+import { CrisisCenterModule } from './crisis-center/crisis-center.module';
+
+import { ComposeMessageComponent } from './compose-message.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { AdminModule } from './admin/admin.module';
+import { DialogService } from './dialog.service';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ HeroesModule,
+ CrisisCenterModule,
+ AdminModule,
+ AppRoutingModule
+ ],
+ declarations: [
+ AppComponent,
+ ComposeMessageComponent,
+ PageNotFoundComponent
+ ],
+ providers: [
+ DialogService
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/router/ts/src/app/app.module.6.ts b/public/docs/_examples/router/ts/src/app/app.module.6.ts
new file mode 100644
index 0000000000..4cb0b1fdd5
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.6.ts
@@ -0,0 +1,29 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { Routes, RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+const routes: Routes = [
+
+];
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ RouterModule.forRoot(routes, { useHash: true }) // .../#/crisis-center/
+ ],
+ declarations: [
+ AppComponent,
+ PageNotFoundComponent
+ ],
+ providers: [
+
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/router/ts/src/app/app.module.7.ts b/public/docs/_examples/router/ts/src/app/app.module.7.ts
new file mode 100644
index 0000000000..b6ca81ddea
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.7.ts
@@ -0,0 +1,38 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+
+import { HeroesModule } from './heroes/heroes.module';
+import { CrisisCenterModule } from './crisis-center/crisis-center.module';
+import { ComposeMessageComponent } from './compose-message.component';
+import { LoginRoutingModule } from './login-routing.module';
+import { LoginComponent } from './login.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { DialogService } from './dialog.service';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HeroesModule,
+ CrisisCenterModule,
+ LoginRoutingModule,
+ AppRoutingModule
+ ],
+ declarations: [
+ AppComponent,
+ ComposeMessageComponent,
+ LoginComponent,
+ PageNotFoundComponent
+ ],
+ providers: [
+ DialogService
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/router/ts/src/app/app.module.ts b/public/docs/_examples/router/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..dcf3401ded
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/app.module.ts
@@ -0,0 +1,56 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+// #docregion animations-module
+import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
+
+// #enddocregion animations-module
+// #docregion inspect-config
+import { Router } from '@angular/router';
+
+// #enddocregion inspect-config
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+
+import { HeroesModule } from './heroes/heroes.module';
+import { ComposeMessageComponent } from './compose-message.component';
+import { LoginRoutingModule } from './login-routing.module';
+import { LoginComponent } from './login.component';
+import { PageNotFoundComponent } from './not-found.component';
+
+import { DialogService } from './dialog.service';
+
+// #docregion animations-module
+@NgModule({
+ imports: [
+ // #enddocregion animations-module
+ BrowserModule,
+ FormsModule,
+ HeroesModule,
+ LoginRoutingModule,
+ AppRoutingModule,
+ // #docregion animations-module
+ BrowserAnimationsModule
+ // #enddocregion animations-module
+ ],
+ declarations: [
+ AppComponent,
+ ComposeMessageComponent,
+ LoginComponent,
+ PageNotFoundComponent
+ ],
+ providers: [
+ DialogService
+ ],
+ bootstrap: [ AppComponent ]
+})
+// #docregion inspect-config
+export class AppModule {
+ // Diagnostic only: inspect router configuration
+ constructor(router: Router) {
+ console.log('Routes: ', JSON.stringify(router.config, undefined, 2));
+ }
+}
+// #enddocregion inspect-config
diff --git a/public/docs/_examples/router/ts/src/app/auth-guard.service.1.ts b/public/docs/_examples/router/ts/src/app/auth-guard.service.1.ts
new file mode 100644
index 0000000000..c824bcb208
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/auth-guard.service.1.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { CanActivate } from '@angular/router';
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+ canActivate() {
+ console.log('AuthGuard#canActivate called');
+ return true;
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/auth-guard.service.2.ts b/public/docs/_examples/router/ts/src/app/auth-guard.service.2.ts
new file mode 100644
index 0000000000..8fd00e151a
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/auth-guard.service.2.ts
@@ -0,0 +1,37 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import {
+ CanActivate, Router,
+ ActivatedRouteSnapshot,
+ RouterStateSnapshot
+} from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Injectable()
+export class AuthGuard implements CanActivate {
+ constructor(private authService: AuthService, private router: Router) {}
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ let url: string = state.url;
+
+ return this.checkLogin(url);
+ }
+
+ checkLogin(url: string): boolean {
+ if (this.authService.isLoggedIn) { return true; }
+
+ // Store the attempted URL for redirecting
+ this.authService.redirectUrl = url;
+
+ // Navigate to the login page with extras
+ this.router.navigate(['/login']);
+ return false;
+ }
+}
+// #enddocregion
+
+/*
+// #docregion can-load-interface
+export class AuthGuard implements CanActivate, CanLoad {
+// #enddocregion can-load-interface
+*/
diff --git a/public/docs/_examples/router/ts/src/app/auth-guard.service.3.ts b/public/docs/_examples/router/ts/src/app/auth-guard.service.3.ts
new file mode 100644
index 0000000000..dd89006411
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/auth-guard.service.3.ts
@@ -0,0 +1,39 @@
+// #docregion
+// #docregion can-activate-child
+import { Injectable } from '@angular/core';
+import {
+ CanActivate, Router,
+ ActivatedRouteSnapshot,
+ RouterStateSnapshot,
+ CanActivateChild
+} from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Injectable()
+export class AuthGuard implements CanActivate, CanActivateChild {
+ constructor(private authService: AuthService, private router: Router) {}
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ let url: string = state.url;
+
+ return this.checkLogin(url);
+ }
+
+ canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ return this.canActivate(route, state);
+ }
+
+// #enddocregion can-activate-child
+ checkLogin(url: string): boolean {
+ if (this.authService.isLoggedIn) { return true; }
+
+ // Store the attempted URL for redirecting
+ this.authService.redirectUrl = url;
+
+ // Navigate to the login page
+ this.router.navigate(['/login']);
+ return false;
+ }
+// #docregion can-activate-child
+}
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/auth-guard.service.4.ts b/public/docs/_examples/router/ts/src/app/auth-guard.service.4.ts
new file mode 100644
index 0000000000..5d239a8432
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/auth-guard.service.4.ts
@@ -0,0 +1,47 @@
+// #docplaster
+// #docregion
+import { Injectable } from '@angular/core';
+import {
+ CanActivate, Router,
+ ActivatedRouteSnapshot,
+ RouterStateSnapshot,
+ CanActivateChild,
+ NavigationExtras
+} from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Injectable()
+export class AuthGuard implements CanActivate, CanActivateChild {
+ constructor(private authService: AuthService, private router: Router) {}
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ let url: string = state.url;
+
+ return this.checkLogin(url);
+ }
+
+ canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ return this.canActivate(route, state);
+ }
+
+ checkLogin(url: string): boolean {
+ if (this.authService.isLoggedIn) { return true; }
+
+ // Store the attempted URL for redirecting
+ this.authService.redirectUrl = url;
+
+ // Create a dummy session id
+ let sessionId = 123456789;
+
+ // Set our navigation extras object
+ // that contains our global query params and fragment
+ let navigationExtras: NavigationExtras = {
+ queryParams: { 'session_id': sessionId },
+ fragment: 'anchor'
+ };
+
+ // Navigate to the login page with extras
+ this.router.navigate(['/login'], navigationExtras);
+ return false;
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/auth-guard.service.ts b/public/docs/_examples/router/ts/src/app/auth-guard.service.ts
new file mode 100644
index 0000000000..a32b5cc2b8
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/auth-guard.service.ts
@@ -0,0 +1,56 @@
+// #docplaster
+import { Injectable } from '@angular/core';
+import {
+ CanActivate, Router,
+ ActivatedRouteSnapshot,
+ RouterStateSnapshot,
+ CanActivateChild,
+ NavigationExtras,
+ CanLoad, Route
+} from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Injectable()
+export class AuthGuard implements CanActivate, CanActivateChild, CanLoad {
+ constructor(private authService: AuthService, private router: Router) {}
+
+ canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ let url: string = state.url;
+
+ return this.checkLogin(url);
+ }
+
+ canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
+ return this.canActivate(route, state);
+ }
+
+// #docregion, canLoad
+ canLoad(route: Route): boolean {
+ let url = `/${route.path}`;
+
+ return this.checkLogin(url);
+ }
+// #enddocregion canLoad
+
+ checkLogin(url: string): boolean {
+ if (this.authService.isLoggedIn) { return true; }
+
+ // Store the attempted URL for redirecting
+ this.authService.redirectUrl = url;
+
+ // Create a dummy session id
+ let sessionId = 123456789;
+
+ // Set our navigation extras object
+ // that contains our global query params and fragment
+ let navigationExtras: NavigationExtras = {
+ queryParams: { 'session_id': sessionId },
+ fragment: 'anchor'
+ };
+
+ // Navigate to the login page with extras
+ this.router.navigate(['/login'], navigationExtras);
+ return false;
+ }
+// #docregion admin-can-load
+}
diff --git a/public/docs/_examples/router/ts/src/app/auth.service.ts b/public/docs/_examples/router/ts/src/app/auth.service.ts
new file mode 100644
index 0000000000..f86a80ebfe
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/auth.service.ts
@@ -0,0 +1,23 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+import 'rxjs/add/operator/do';
+import 'rxjs/add/operator/delay';
+
+@Injectable()
+export class AuthService {
+ isLoggedIn: boolean = false;
+
+ // store the URL so we can redirect after logging in
+ redirectUrl: string;
+
+ login(): Observable {
+ return Observable.of(true).delay(1000).do(val => this.isLoggedIn = true);
+ }
+
+ logout(): void {
+ this.isLoggedIn = false;
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/can-deactivate-guard.service.1.ts b/public/docs/_examples/router/ts/src/app/can-deactivate-guard.service.1.ts
new file mode 100644
index 0000000000..0b7c8247cf
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/can-deactivate-guard.service.1.ts
@@ -0,0 +1,31 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { CanDeactivate,
+ ActivatedRouteSnapshot,
+ RouterStateSnapshot } from '@angular/router';
+
+import { CrisisDetailComponent } from './crisis-center/crisis-detail.component';
+
+@Injectable()
+export class CanDeactivateGuard implements CanDeactivate {
+
+ canDeactivate(
+ component: CrisisDetailComponent,
+ route: ActivatedRouteSnapshot,
+ state: RouterStateSnapshot
+ ): Promise | boolean {
+ // Get the Crisis Center ID
+ console.log(route.params['id']);
+
+ // Get the current URL
+ console.log(state.url);
+
+ // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
+ if (!component.crisis || component.crisis.name === component.editName) {
+ return true;
+ }
+ // Otherwise ask the user with the dialog service and return its
+ // promise which resolves to true or false when the user decides
+ return component.dialogService.confirm('Discard changes?');
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/can-deactivate-guard.service.ts b/public/docs/_examples/router/ts/src/app/can-deactivate-guard.service.ts
new file mode 100644
index 0000000000..44da69f9c7
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/can-deactivate-guard.service.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { CanDeactivate } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+
+export interface CanComponentDeactivate {
+ canDeactivate: () => Observable | Promise | boolean;
+}
+
+@Injectable()
+export class CanDeactivateGuard implements CanDeactivate {
+ canDeactivate(component: CanComponentDeactivate) {
+ return component.canDeactivate ? component.canDeactivate() : true;
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/compose-message.component.html b/public/docs/_examples/router/ts/src/app/compose-message.component.html
new file mode 100644
index 0000000000..f0b964e6ac
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/compose-message.component.html
@@ -0,0 +1,17 @@
+
+Contact Crisis Center
+
+ {{ details }}
+
+
+
+ Send
+ Cancel
+
diff --git a/public/docs/_examples/router/ts/src/app/compose-message.component.ts b/public/docs/_examples/router/ts/src/app/compose-message.component.ts
new file mode 100644
index 0000000000..17a0953378
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/compose-message.component.ts
@@ -0,0 +1,43 @@
+// #docregion
+import { Component, HostBinding } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { slideInDownAnimation } from './animations';
+
+@Component({
+ templateUrl: './compose-message.component.html',
+ styles: [ ':host { position: relative; bottom: 10%; }' ],
+ animations: [ slideInDownAnimation ]
+})
+export class ComposeMessageComponent {
+ @HostBinding('@routeAnimation') routeAnimation = true;
+ @HostBinding('style.display') display = 'block';
+ @HostBinding('style.position') position = 'absolute';
+
+ details: string;
+ sending: boolean = false;
+
+ constructor(private router: Router) {}
+
+ send() {
+ this.sending = true;
+ this.details = 'Sending Message...';
+
+ setTimeout(() => {
+ this.sending = false;
+ this.closePopup();
+ }, 1000);
+ }
+
+ cancel() {
+ this.closePopup();
+ }
+
+ // #docregion closePopup
+ closePopup() {
+ // Providing a `null` value to the named outlet
+ // clears the contents of the named outlet
+ this.router.navigate([{ outlets: { popup: null }}]);
+ }
+ // #enddocregion closePopup
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-home.component.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-home.component.ts
new file mode 100644
index 0000000000..a71d485c02
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-home.component.ts
@@ -0,0 +1,13 @@
+// #docregion
+// #docplaster
+import { Component } from '@angular/core';
+
+// #docregion minus-imports
+@Component({
+ template: `
+ Welcome to the Crisis Center
+ `
+})
+export class CrisisCenterHomeComponent { }
+// #enddocregion minus-imports
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.1.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.1.ts
new file mode 100644
index 0000000000..e646f467d1
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.1.ts
@@ -0,0 +1,44 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+// #docregion routes
+const crisisCenterRoutes: Routes = [
+ {
+ path: 'crisis-center',
+ component: CrisisCenterComponent,
+ children: [
+ {
+ path: '',
+ component: CrisisListComponent,
+ children: [
+ {
+ path: ':id',
+ component: CrisisDetailComponent
+ },
+ {
+ path: '',
+ component: CrisisCenterHomeComponent
+ }
+ ]
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(crisisCenterRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class CrisisCenterRoutingModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.2.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.2.ts
new file mode 100644
index 0000000000..9e9b514968
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.2.ts
@@ -0,0 +1,72 @@
+// #docplaster
+// #docregion routes
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+// #enddocregion routes
+
+// #docregion can-deactivate-guard
+import { CanDeactivateGuard } from '../can-deactivate-guard.service';
+// #enddocregion can-deactivate-guard
+// #docregion crisis-detail-resolver
+import { CrisisDetailResolver } from './crisis-detail-resolver.service';
+
+// #enddocregion crisis-detail-resolver
+// #docregion routes
+
+const crisisCenterRoutes: Routes = [
+// #enddocregion routes
+ // #docregion redirect, routes
+ {
+ path: '',
+ redirectTo: '/crisis-center',
+ pathMatch: 'full'
+ },
+ // #enddocregion redirect, routes
+ // #docregion routes
+ {
+ path: 'crisis-center',
+ component: CrisisCenterComponent,
+ children: [
+ {
+ path: '',
+ component: CrisisListComponent,
+ children: [
+ {
+ path: ':id',
+ component: CrisisDetailComponent,
+ // #enddocregion routes
+ // #docregion can-deactivate-guard
+ canDeactivate: [CanDeactivateGuard],
+ // #enddocregion can-deactivate-guard
+ // #docregion crisis-detail-resolver
+ resolve: {
+ crisis: CrisisDetailResolver
+ }
+ // #enddocregion crisis-detail-resolver
+ // #docregion routes
+ },
+ {
+ path: '',
+ component: CrisisCenterHomeComponent
+ }
+ ]
+ }
+ ]
+ }
+ // #enddocregion routes
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(crisisCenterRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class CrisisCenterRoutingModule { }
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.3.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.3.ts
new file mode 100644
index 0000000000..6d605dbe84
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.3.ts
@@ -0,0 +1,52 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+// #docregion can-deactivate-guard
+import { CanDeactivateGuard } from '../can-deactivate-guard.service';
+
+const crisisCenterRoutes: Routes = [
+ {
+ path: '',
+ redirectTo: '/crisis-center',
+ pathMatch: 'full'
+ },
+ {
+ path: 'crisis-center',
+ component: CrisisCenterComponent,
+ children: [
+ {
+ path: '',
+ component: CrisisListComponent,
+ children: [
+ {
+ path: ':id',
+ component: CrisisDetailComponent,
+ canDeactivate: [CanDeactivateGuard]
+ },
+ {
+ path: '',
+ component: CrisisCenterHomeComponent
+ }
+ ]
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(crisisCenterRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class CrisisCenterRoutingModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.4.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.4.ts
new file mode 100644
index 0000000000..b7ac88e852
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.4.ts
@@ -0,0 +1,65 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+import { CanDeactivateGuard } from '../can-deactivate-guard.service';
+
+// #docregion crisis-detail-resolver
+import { CrisisDetailResolver } from './crisis-detail-resolver.service';
+
+// #enddocregion crisis-detail-resolver
+const crisisCenterRoutes: Routes = [
+ // #docregion redirect
+ {
+ path: '',
+ redirectTo: '/crisis-center',
+ pathMatch: 'full'
+ },
+ // #enddocregion redirect
+ {
+ path: 'crisis-center',
+ component: CrisisCenterComponent,
+ children: [
+ {
+ path: '',
+ component: CrisisListComponent,
+ children: [
+ {
+ path: ':id',
+ component: CrisisDetailComponent,
+ canDeactivate: [CanDeactivateGuard],
+ resolve: {
+ crisis: CrisisDetailResolver
+ }
+ },
+ {
+ path: '',
+ component: CrisisCenterHomeComponent
+ }
+ ]
+ }
+ ]
+ }
+];
+
+// #docregion crisis-detail-resolver
+@NgModule({
+ imports: [
+ RouterModule.forChild(crisisCenterRoutes)
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ CrisisDetailResolver
+ ]
+})
+export class CrisisCenterRoutingModule { }
+// #enddocregion crisis-detail-resolver
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.ts
new file mode 100644
index 0000000000..c01d592455
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center-routing.module.ts
@@ -0,0 +1,53 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+import { CanDeactivateGuard } from '../can-deactivate-guard.service';
+import { CrisisDetailResolver } from './crisis-detail-resolver.service';
+
+const crisisCenterRoutes: Routes = [
+ {
+ path: '',
+ component: CrisisCenterComponent,
+ children: [
+ {
+ path: '',
+ component: CrisisListComponent,
+ children: [
+ {
+ path: ':id',
+ component: CrisisDetailComponent,
+ canDeactivate: [CanDeactivateGuard],
+ resolve: {
+ crisis: CrisisDetailResolver
+ }
+ },
+ {
+ path: '',
+ component: CrisisCenterHomeComponent
+ }
+ ]
+ }
+ ]
+ }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(crisisCenterRoutes)
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ CrisisDetailResolver
+ ]
+})
+export class CrisisCenterRoutingModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.component.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.component.ts
new file mode 100644
index 0000000000..31d1790f45
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+// #docplaster
+import { Component } from '@angular/core';
+
+// #docregion minus-imports
+@Component({
+ template: `
+ CRISIS CENTER
+
+ `
+})
+export class CrisisCenterComponent { }
+// #enddocregion minus-imports
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.module.1.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.module.1.ts
new file mode 100644
index 0000000000..5a3e45f58f
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.module.1.ts
@@ -0,0 +1,36 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+
+import { CrisisService } from './crisis.service';
+
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+import { CrisisCenterRoutingModule } from './crisis-center-routing.module';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ CrisisCenterRoutingModule
+ ],
+ declarations: [
+ CrisisCenterComponent,
+ CrisisListComponent,
+ CrisisCenterHomeComponent,
+ CrisisDetailComponent
+ ],
+
+ // #docregion providers
+ providers: [
+ CrisisService
+ ]
+ // #enddocregion providers
+})
+export class CrisisCenterModule {}
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.module.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.module.ts
new file mode 100644
index 0000000000..4061ceac60
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-center.module.ts
@@ -0,0 +1,35 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+
+import { CrisisService } from './crisis.service';
+
+import { CrisisCenterComponent } from './crisis-center.component';
+import { CrisisListComponent } from './crisis-list.component';
+import { CrisisCenterHomeComponent } from './crisis-center-home.component';
+import { CrisisDetailComponent } from './crisis-detail.component';
+
+import { CrisisCenterRoutingModule } from './crisis-center-routing.module';
+
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+ CrisisCenterRoutingModule
+ ],
+ declarations: [
+ CrisisCenterComponent,
+ CrisisListComponent,
+ CrisisCenterHomeComponent,
+ CrisisDetailComponent
+ ],
+ providers: [
+ CrisisService
+ ]
+})
+// #docregion crisis-center-module-export
+export class CrisisCenterModule {}
+// #enddocregion crisis-center-module-export
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail-resolver.service.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail-resolver.service.ts
new file mode 100644
index 0000000000..94b4cd33e7
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail-resolver.service.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { Router, Resolve, RouterStateSnapshot,
+ ActivatedRouteSnapshot } from '@angular/router';
+
+import { Crisis, CrisisService } from './crisis.service';
+
+@Injectable()
+export class CrisisDetailResolver implements Resolve {
+ constructor(private cs: CrisisService, private router: Router) {}
+
+ resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise {
+ let id = route.params['id'];
+
+ return this.cs.getCrisis(id).then(crisis => {
+ if (crisis) {
+ return crisis;
+ } else { // id not found
+ this.router.navigate(['/crisis-center']);
+ return null;
+ }
+ });
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail.component.1.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail.component.1.ts
new file mode 100644
index 0000000000..d6fa27f629
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail.component.1.ts
@@ -0,0 +1,87 @@
+// #docplaster
+// #docregion
+import 'rxjs/add/operator/switchMap';
+import { Component, OnInit, HostBinding } from '@angular/core';
+import { ActivatedRoute, Router, Params } from '@angular/router';
+
+import { slideInDownAnimation } from '../animations';
+import { Crisis, CrisisService } from './crisis.service';
+import { DialogService } from '../dialog.service';
+
+@Component({
+ template: `
+
+
"{{ editName }}"
+
+ Id: {{ crisis.id }}
+
+ Name:
+
+
+
+ Save
+ Cancel
+
+
+ `,
+ styles: ['input {width: 20em}'],
+ animations: [ slideInDownAnimation ]
+})
+export class CrisisDetailComponent implements OnInit {
+ @HostBinding('@routeAnimation') routeAnimation = true;
+ @HostBinding('style.display') display = 'block';
+ @HostBinding('style.position') position = 'absolute';
+
+ crisis: Crisis;
+ editName: string;
+
+ constructor(
+ private service: CrisisService,
+ private router: Router,
+ private route: ActivatedRoute,
+ public dialogService: DialogService
+ ) {}
+
+ // #docregion ngOnInit
+ ngOnInit() {
+ this.route.params
+ .switchMap((params: Params) => this.service.getCrisis(params['id']))
+ .subscribe((crisis: Crisis) => {
+ if (crisis) {
+ this.editName = crisis.name;
+ this.crisis = crisis;
+ } else { // id not found
+ this.gotoCrises();
+ }
+ });
+ }
+ // #enddocregion ngOnInit
+
+ cancel() {
+ this.gotoCrises();
+ }
+
+ save() {
+ this.crisis.name = this.editName;
+ this.gotoCrises();
+ }
+
+ canDeactivate(): Promise | boolean {
+ // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
+ if (!this.crisis || this.crisis.name === this.editName) {
+ return true;
+ }
+ // Otherwise ask the user with the dialog service and return its
+ // promise which resolves to true or false when the user decides
+ return this.dialogService.confirm('Discard changes?');
+ }
+
+ gotoCrises() {
+ let crisisId = this.crisis ? this.crisis.id : null;
+ // Pass along the crisis id if available
+ // so that the CrisisListComponent can select that crisis.
+ // Add a totally useless `foo` parameter for kicks.
+ // Relative navigation back to the crises
+ this.router.navigate(['../', { id: crisisId, foo: 'foo' }], { relativeTo: this.route });
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail.component.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail.component.ts
new file mode 100644
index 0000000000..f0939b47a8
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-detail.component.ts
@@ -0,0 +1,86 @@
+// #docplaster
+// #docregion
+import { Component, OnInit, HostBinding } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { slideInDownAnimation } from '../animations';
+import { Crisis } from './crisis.service';
+import { DialogService } from '../dialog.service';
+
+@Component({
+ template: `
+
+
"{{ editName }}"
+
+ Id: {{ crisis.id }}
+
+ Name:
+
+
+
+ Save
+ Cancel
+
+
+ `,
+ styles: ['input {width: 20em}'],
+ animations: [ slideInDownAnimation ]
+})
+export class CrisisDetailComponent implements OnInit {
+ @HostBinding('@routeAnimation') routeAnimation = true;
+ @HostBinding('style.display') display = 'block';
+ @HostBinding('style.position') position = 'absolute';
+
+ crisis: Crisis;
+ editName: string;
+
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ public dialogService: DialogService
+ ) {}
+
+// #docregion ngOnInit
+ ngOnInit() {
+ this.route.data
+ .subscribe((data: { crisis: Crisis }) => {
+ this.editName = data.crisis.name;
+ this.crisis = data.crisis;
+ });
+ }
+// #enddocregion ngOnInit
+
+ // #docregion cancel-save
+ cancel() {
+ this.gotoCrises();
+ }
+
+ save() {
+ this.crisis.name = this.editName;
+ this.gotoCrises();
+ }
+ // #enddocregion cancel-save
+
+ // #docregion canDeactivate
+ canDeactivate(): Promise | boolean {
+ // Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
+ if (!this.crisis || this.crisis.name === this.editName) {
+ return true;
+ }
+ // Otherwise ask the user with the dialog service and return its
+ // promise which resolves to true or false when the user decides
+ return this.dialogService.confirm('Discard changes?');
+ }
+ // #enddocregion canDeactivate
+
+ gotoCrises() {
+ let crisisId = this.crisis ? this.crisis.id : null;
+ // Pass along the crisis id if available
+ // so that the CrisisListComponent can select that crisis.
+ // Add a totally useless `foo` parameter for kicks.
+ // #docregion gotoCrises-navigate
+ // Relative navigation back to the crises
+ this.router.navigate(['../', { id: crisisId, foo: 'foo' }], { relativeTo: this.route });
+ // #enddocregion gotoCrises-navigate
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-list.component.1.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-list.component.1.ts
new file mode 100644
index 0000000000..0000dde082
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-list.component.1.ts
@@ -0,0 +1,44 @@
+import 'rxjs/add/operator/do';
+import 'rxjs/add/operator/switchMap';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router, Params } from '@angular/router';
+
+import { Crisis, CrisisService } from './crisis.service';
+import { Observable } from 'rxjs/Observable';
+
+@Component({
+ // #docregion relative-navigation-router-link
+ template: `
+ `
+ // #enddocregion relative-navigation-router-link
+})
+export class CrisisListComponent implements OnInit {
+ crises: Observable;
+ selectedId: number;
+
+ constructor(
+ private service: CrisisService,
+ private route: ActivatedRoute,
+ private router: Router
+ ) {}
+
+ ngOnInit() {
+ this.crises = this.route.params
+ .switchMap((params: Params) => {
+ this.selectedId = +params['id'];
+ return this.service.getCrises();
+ });
+ }
+
+ isSelected(crisis: Crisis) {
+ return crisis.id === this.selectedId;
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis-list.component.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-list.component.ts
new file mode 100644
index 0000000000..4498a55c0f
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis-list.component.ts
@@ -0,0 +1,56 @@
+// #docregion
+import 'rxjs/add/operator/switchMap';
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router, Params } from '@angular/router';
+
+import { Observable } from 'rxjs/Observable';
+
+import { Crisis, CrisisService } from './crisis.service';
+
+@Component({
+ template: `
+
+
+ {{ crisis.id }}
+ {{ crisis.name }}
+
+
+
+
+ `
+})
+export class CrisisListComponent implements OnInit {
+ crises: Observable;
+ selectedId: number;
+
+ // #docregion ctor
+ constructor(
+ private service: CrisisService,
+ private route: ActivatedRoute,
+ private router: Router
+ ) {}
+ // #enddocregion ctor
+
+ isSelected(crisis: Crisis) {
+ return crisis.id === this.selectedId;
+ }
+
+ ngOnInit() {
+ this.crises = this.route.params
+ .switchMap((params: Params) => {
+ this.selectedId = +params['id'];
+ return this.service.getCrises();
+ });
+ }
+
+ // #docregion onSelect
+ onSelect(crisis: Crisis) {
+ this.selectedId = crisis.id;
+
+ // Navigate with relative link
+ this.router.navigate([crisis.id], { relativeTo: this.route });
+ }
+ // #enddocregion onSelect
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-center/crisis.service.ts b/public/docs/_examples/router/ts/src/app/crisis-center/crisis.service.ts
new file mode 100644
index 0000000000..e7fd34d387
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-center/crisis.service.ts
@@ -0,0 +1,40 @@
+// #docplaster
+// #docregion , mock-crises
+export class Crisis {
+ constructor(public id: number, public name: string) { }
+}
+
+const CRISES = [
+ new Crisis(1, 'Dragon Burning Cities'),
+ new Crisis(2, 'Sky Rains Great White Sharks'),
+ new Crisis(3, 'Giant Asteroid Heading For Earth'),
+ new Crisis(4, 'Procrastinators Meeting Delayed Again'),
+];
+// #enddocregion mock-crises
+
+let crisesPromise = Promise.resolve(CRISES);
+
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class CrisisService {
+
+ static nextCrisisId = 100;
+
+ getCrises() { return crisesPromise; }
+
+ getCrisis(id: number | string) {
+ return crisesPromise
+ .then(crises => crises.find(crisis => crisis.id === +id));
+ }
+
+ // #enddocregion
+ addCrisis(name: string) {
+ name = name.trim();
+ if (name) {
+ let crisis = new Crisis(CrisisService.nextCrisisId++, name);
+ crisesPromise.then(crises => crises.push(crisis));
+ }
+ }
+ // #docregion
+}
diff --git a/public/docs/_examples/router/ts/src/app/crisis-list.component.ts b/public/docs/_examples/router/ts/src/app/crisis-list.component.ts
new file mode 100644
index 0000000000..6caa3653b5
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/crisis-list.component.ts
@@ -0,0 +1,10 @@
+// Initial empty version
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ CRISIS CENTER
+ Get your crisis here
`
+})
+export class CrisisListComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/dialog.service.ts b/public/docs/_examples/router/ts/src/app/dialog.service.ts
new file mode 100644
index 0000000000..0f09e4936d
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/dialog.service.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { Injectable } from '@angular/core';
+/**
+ * Async modal dialog service
+ * DialogService makes this app easier to test by faking this service.
+ * TODO: better modal implementation that doesn't use window.confirm
+ */
+@Injectable()
+export class DialogService {
+ /**
+ * Ask user to confirm an action. `message` explains the action and choices.
+ * Returns promise resolving to `true`=confirm or `false`=cancel
+ */
+ confirm(message?: string) {
+ return new Promise(resolve => {
+ return resolve(window.confirm(message || 'Is it OK?'));
+ });
+ };
+}
diff --git a/public/docs/_examples/router/ts/src/app/hero-list.component.ts b/public/docs/_examples/router/ts/src/app/hero-list.component.ts
new file mode 100644
index 0000000000..7a8f97ca1e
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/hero-list.component.ts
@@ -0,0 +1,13 @@
+/// Initial empty version
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: `
+ HEROES
+ Get your heroes here
+
+ Go to sidekicks
+ `
+})
+export class HeroListComponent { }
diff --git a/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.1.ts b/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.1.ts
new file mode 100644
index 0000000000..93f0efaf0b
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.1.ts
@@ -0,0 +1,55 @@
+// #docplaster
+// #docregion
+// #docregion rxjs-operator-import
+import 'rxjs/add/operator/switchMap';
+// #enddocregion rxjs-operator-import
+import { Component, OnInit } from '@angular/core';
+// #docregion imports
+import { Router, ActivatedRoute, Params } from '@angular/router';
+// #enddocregion imports
+
+import { Hero, HeroService } from './hero.service';
+
+@Component({
+ template: `
+ HEROES
+
+
"{{ hero.name }}"
+
+ Id: {{ hero.id }}
+
+ Name:
+
+
+
+ Back
+
+
+ `
+})
+export class HeroDetailComponent implements OnInit {
+ hero: Hero;
+
+ // #docregion ctor
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private service: HeroService
+ ) {}
+ // #enddocregion ctor
+
+ // #docregion ngOnInit
+ ngOnInit() {
+ this.route.params
+ // (+) converts string 'id' to a number
+ .switchMap((params: Params) => this.service.getHero(+params['id']))
+ .subscribe((hero: Hero) => this.hero = hero);
+ }
+ // #enddocregion ngOnInit
+
+ // #docregion gotoHeroes
+ gotoHeroes() {
+ this.router.navigate(['/heroes']);
+ }
+ // #enddocregion gotoHeroes
+}
diff --git a/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.2.ts b/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.2.ts
new file mode 100644
index 0000000000..c3b69be965
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.2.ts
@@ -0,0 +1,47 @@
+// Snapshot version
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+
+import { Hero, HeroService } from './hero.service';
+
+@Component({
+ template: `
+ HEROES
+
+
"{{ hero.name }}"
+
+ Id: {{ hero.id }}
+
+ Name:
+
+
+
+ Back
+
+
+ `
+})
+export class HeroDetailComponent implements OnInit {
+ hero: Hero;
+
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private service: HeroService
+ ) {}
+
+ // #docregion snapshot
+ ngOnInit() {
+ // (+) converts string 'id' to a number
+ let id = +this.route.snapshot.params['id'];
+
+ this.service.getHero(id)
+ .then((hero: Hero) => this.hero = hero);
+ }
+ // #enddocregion snapshot
+
+ gotoHeroes() {
+ this.router.navigate(['/heroes']);
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.ts b/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.ts
new file mode 100644
index 0000000000..8135d37d32
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/hero-detail.component.ts
@@ -0,0 +1,66 @@
+// #docplaster
+// #docregion
+// #docregion rxjs-operator-import
+import 'rxjs/add/operator/switchMap';
+// #enddocregion rxjs-operator-import
+import { Component, OnInit, HostBinding } from '@angular/core';
+import { Router, ActivatedRoute, Params } from '@angular/router';
+
+import { slideInDownAnimation } from '../animations';
+
+import { Hero, HeroService } from './hero.service';
+
+@Component({
+ template: `
+ HEROES
+
+
"{{ hero.name }}"
+
+ Id: {{ hero.id }}
+
+ Name:
+
+
+
+ Back
+
+
+ `,
+ animations: [ slideInDownAnimation ]
+})
+export class HeroDetailComponent implements OnInit {
+// #docregion host-bindings
+ @HostBinding('@routeAnimation') routeAnimation = true;
+ @HostBinding('style.display') display = 'block';
+ @HostBinding('style.position') position = 'absolute';
+// #enddocregion host-bindings
+
+ hero: Hero;
+
+ // #docregion ctor
+ constructor(
+ private route: ActivatedRoute,
+ private router: Router,
+ private service: HeroService
+ ) {}
+ // #enddocregion ctor
+
+ // #docregion ngOnInit
+ ngOnInit() {
+ this.route.params
+ // (+) converts string 'id' to a number
+ .switchMap((params: Params) => this.service.getHero(+params['id']))
+ .subscribe((hero: Hero) => this.hero = hero);
+ }
+ // #enddocregion ngOnInit
+
+ // #docregion gotoHeroes
+ gotoHeroes() {
+ let heroId = this.hero ? this.hero.id : null;
+ // Pass along the hero id if available
+ // so that the HeroList component can select that hero.
+ // Include a junk 'foo' property for fun.
+ this.router.navigate(['/heroes', { id: heroId, foo: 'foo' }]);
+ }
+ // #enddocregion gotoHeroes
+}
diff --git a/public/docs/_examples/router/ts/src/app/heroes/hero-list.component.1.ts b/public/docs/_examples/router/ts/src/app/heroes/hero-list.component.1.ts
new file mode 100644
index 0000000000..59552830a4
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/hero-list.component.1.ts
@@ -0,0 +1,52 @@
+// #docplaster
+// #docregion
+// TODO SOMEDAY: Feature Componetized like HeroCenter
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Hero, HeroService } from './hero.service';
+
+@Component({
+ // #docregion template
+ template: `
+ HEROES
+
+
+ {{ hero.id }} {{ hero.name }}
+
+
+
+ Go to sidekicks
+ `
+ // #enddocregion template
+})
+export class HeroListComponent implements OnInit {
+ heroes: Promise;
+
+ // #docregion ctor
+ constructor(
+ private router: Router,
+ private service: HeroService
+ ) {}
+ // #enddocregion ctor
+
+ ngOnInit() {
+ this.heroes = this.service.getHeroes();
+ }
+
+ // #docregion select
+ onSelect(hero: Hero) {
+ // #docregion nav-to-detail
+ this.router.navigate(['/hero', hero.id]);
+ // #enddocregion nav-to-detail
+ }
+ // #enddocregion select
+}
+// #enddocregion
+
+/* A link parameters array
+// #docregion link-parameters-array
+['/hero', hero.id] // { 15 }
+// #enddocregion link-parameters-array
+*/
diff --git a/public/docs/_examples/router/ts/src/app/heroes/hero-list.component.ts b/public/docs/_examples/router/ts/src/app/heroes/hero-list.component.ts
new file mode 100644
index 0000000000..c7dcc3877e
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/hero-list.component.ts
@@ -0,0 +1,63 @@
+// #docplaster
+// #docregion
+// TODO SOMEDAY: Feature Componetized like CrisisCenter
+// #docregion rxjs-imports
+import 'rxjs/add/operator/switchMap';
+import { Observable } from 'rxjs/Observable';
+// #enddocregion rxjs-imports
+import { Component, OnInit } from '@angular/core';
+// #docregion import-router
+import { Router, ActivatedRoute, Params } from '@angular/router';
+// #enddocregion import-router
+
+import { Hero, HeroService } from './hero.service';
+
+@Component({
+ // #docregion template
+ template: `
+ HEROES
+
+
+ {{ hero.id }} {{ hero.name }}
+
+
+
+ Go to sidekicks
+ `
+ // #enddocregion template
+})
+// #docregion ctor
+export class HeroListComponent implements OnInit {
+ heroes: Observable;
+
+ private selectedId: number;
+
+ constructor(
+ private service: HeroService,
+ private route: ActivatedRoute,
+ private router: Router
+ ) {}
+
+ ngOnInit() {
+ this.heroes = this.route.params
+ .switchMap((params: Params) => {
+ this.selectedId = +params['id'];
+ return this.service.getHeroes();
+ });
+ }
+ // #enddocregion ctor
+
+ // #docregion isSelected
+ isSelected(hero: Hero) { return hero.id === this.selectedId; }
+ // #enddocregion isSelected
+
+ // #docregion select
+ onSelect(hero: Hero) {
+ this.router.navigate(['/hero', hero.id]);
+ }
+ // #enddocregion select
+// #docregion ctor
+}
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/heroes/hero.service.ts b/public/docs/_examples/router/ts/src/app/heroes/hero.service.ts
new file mode 100644
index 0000000000..6e4e7bee60
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/hero.service.ts
@@ -0,0 +1,27 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+export class Hero {
+ constructor(public id: number, public name: string) { }
+}
+
+let HEROES = [
+ new Hero(11, 'Mr. Nice'),
+ new Hero(12, 'Narco'),
+ new Hero(13, 'Bombasto'),
+ new Hero(14, 'Celeritas'),
+ new Hero(15, 'Magneta'),
+ new Hero(16, 'RubberMan')
+];
+
+let heroesPromise = Promise.resolve(HEROES);
+
+@Injectable()
+export class HeroService {
+ getHeroes() { return heroesPromise; }
+
+ getHero(id: number | string) {
+ return heroesPromise
+ .then(heroes => heroes.find(hero => hero.id === +id));
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/heroes/heroes-routing.module.ts b/public/docs/_examples/router/ts/src/app/heroes/heroes-routing.module.ts
new file mode 100644
index 0000000000..dbee521793
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/heroes-routing.module.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { HeroListComponent } from './hero-list.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+const heroesRoutes: Routes = [
+ { path: 'heroes', component: HeroListComponent },
+// #docregion hero-detail-route
+ { path: 'hero/:id', component: HeroDetailComponent }
+// #enddocregion hero-detail-route
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(heroesRoutes)
+ ],
+ exports: [
+ RouterModule
+ ]
+})
+export class HeroRoutingModule { }
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/heroes/heroes.module.ts b/public/docs/_examples/router/ts/src/app/heroes/heroes.module.ts
new file mode 100644
index 0000000000..95ee64a182
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/heroes/heroes.module.ts
@@ -0,0 +1,33 @@
+// #docplaster
+// #docregion
+// #docregion v1
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { HeroListComponent } from './hero-list.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+import { HeroService } from './hero.service';
+
+// #enddocregion v1
+import { HeroRoutingModule } from './heroes-routing.module';
+
+// #docregion v1
+@NgModule({
+ imports: [
+ CommonModule,
+ FormsModule,
+// #enddocregion v1
+ HeroRoutingModule
+// #docregion v1
+ ],
+ declarations: [
+ HeroListComponent,
+ HeroDetailComponent
+ ],
+ providers: [ HeroService ]
+})
+export class HeroesModule {}
+// #enddocregion v1
+// #enddocregion
diff --git a/public/docs/_examples/router/ts/src/app/login-routing.module.ts b/public/docs/_examples/router/ts/src/app/login-routing.module.ts
new file mode 100644
index 0000000000..96d05e7972
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/login-routing.module.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { AuthGuard } from './auth-guard.service';
+import { AuthService } from './auth.service';
+import { LoginComponent } from './login.component';
+
+const loginRoutes: Routes = [
+ { path: 'login', component: LoginComponent }
+];
+
+@NgModule({
+ imports: [
+ RouterModule.forChild(loginRoutes)
+ ],
+ exports: [
+ RouterModule
+ ],
+ providers: [
+ AuthGuard,
+ AuthService
+ ]
+})
+export class LoginRoutingModule {}
diff --git a/public/docs/_examples/router/ts/src/app/login.component.1.ts b/public/docs/_examples/router/ts/src/app/login.component.1.ts
new file mode 100644
index 0000000000..ddee339011
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/login.component.1.ts
@@ -0,0 +1,46 @@
+// #docregion
+import { Component } from '@angular/core';
+import { Router } from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Component({
+ template: `
+ LOGIN
+ {{message}}
+
+ Login
+ Logout
+
`
+})
+export class LoginComponent {
+ message: string;
+
+ constructor(public authService: AuthService, public router: Router) {
+ this.setMessage();
+ }
+
+ setMessage() {
+ this.message = 'Logged ' + (this.authService.isLoggedIn ? 'in' : 'out');
+ }
+
+ login() {
+ this.message = 'Trying to log in ...';
+
+ this.authService.login().subscribe(() => {
+ this.setMessage();
+ if (this.authService.isLoggedIn) {
+ // Get the redirect URL from our auth service
+ // If no redirect has been set, use the default
+ let redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/crisis-center/admin';
+
+ // Redirect the user
+ this.router.navigate([redirect]);
+ }
+ });
+ }
+
+ logout() {
+ this.authService.logout();
+ this.setMessage();
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/login.component.ts b/public/docs/_examples/router/ts/src/app/login.component.ts
new file mode 100644
index 0000000000..41c88f4068
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/login.component.ts
@@ -0,0 +1,56 @@
+// #docregion
+import { Component } from '@angular/core';
+import { Router,
+ NavigationExtras } from '@angular/router';
+import { AuthService } from './auth.service';
+
+@Component({
+ template: `
+ LOGIN
+ {{message}}
+
+ Login
+ Logout
+
`
+})
+export class LoginComponent {
+ message: string;
+
+ constructor(public authService: AuthService, public router: Router) {
+ this.setMessage();
+ }
+
+ setMessage() {
+ this.message = 'Logged ' + (this.authService.isLoggedIn ? 'in' : 'out');
+ }
+
+ login() {
+ this.message = 'Trying to log in ...';
+
+ this.authService.login().subscribe(() => {
+ this.setMessage();
+ if (this.authService.isLoggedIn) {
+ // Get the redirect URL from our auth service
+ // If no redirect has been set, use the default
+ let redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/admin';
+
+ // #docregion preserve
+ // Set our navigation extras object
+ // that passes on our global query params and fragment
+ let navigationExtras: NavigationExtras = {
+ preserveQueryParams: true,
+ preserveFragment: true
+ };
+
+ // Redirect the user
+ this.router.navigate([redirect], navigationExtras);
+ // #enddocregion preserve
+ }
+ });
+ }
+
+ logout() {
+ this.authService.logout();
+ this.setMessage();
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/app/not-found.component.ts b/public/docs/_examples/router/ts/src/app/not-found.component.ts
new file mode 100644
index 0000000000..2e74544e17
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/not-found.component.ts
@@ -0,0 +1,7 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ template: 'Page not found '
+})
+export class PageNotFoundComponent {}
diff --git a/public/docs/_examples/router/ts/src/app/selective-preloading-strategy.ts b/public/docs/_examples/router/ts/src/app/selective-preloading-strategy.ts
new file mode 100644
index 0000000000..395f1056ef
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/app/selective-preloading-strategy.ts
@@ -0,0 +1,24 @@
+// #docregion
+import 'rxjs/add/observable/of';
+import { Injectable } from '@angular/core';
+import { PreloadingStrategy, Route } from '@angular/router';
+import { Observable } from 'rxjs/Observable';
+
+@Injectable()
+export class SelectivePreloadingStrategy implements PreloadingStrategy {
+ preloadedModules: string[] = [];
+
+ preload(route: Route, load: () => Observable): Observable {
+ if (route.data && route.data['preload']) {
+ // add the route path to the preloaded module array
+ this.preloadedModules.push(route.path);
+
+ // log the route path to the console
+ console.log('Preloaded: ' + route.path);
+
+ return load();
+ } else {
+ return Observable.of(null);
+ }
+ }
+}
diff --git a/public/docs/_examples/router/ts/src/index.html b/public/docs/_examples/router/ts/src/index.html
new file mode 100644
index 0000000000..0fa6453c36
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/index.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+ Angular Router
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loading...
+
+
+
+
diff --git a/public/docs/_examples/router/ts/src/main.ts b/public/docs/_examples/router/ts/src/main.ts
new file mode 100644
index 0000000000..f332d1d245
--- /dev/null
+++ b/public/docs/_examples/router/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/security/e2e-spec.ts b/public/docs/_examples/security/e2e-spec.ts
new file mode 100644
index 0000000000..23d11cd12b
--- /dev/null
+++ b/public/docs/_examples/security/e2e-spec.ts
@@ -0,0 +1,37 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, By } from 'protractor';
+
+describe('Security E2E Tests', () => {
+ beforeAll(() => browser.get(''));
+
+ it('sanitizes innerHTML', () => {
+ let interpolated = element(By.className('e2e-inner-html-interpolated'));
+ expect(interpolated.getText())
+ .toContain('Template Syntax ');
+ let bound = element(By.className('e2e-inner-html-bound'));
+ expect(bound.getText()).toContain('Template alert("0wned") Syntax');
+ let bold = element(By.css('.e2e-inner-html-bound b'));
+ expect(bold.getText()).toContain('Syntax');
+ });
+
+ it('escapes untrusted URLs', () => {
+ let untrustedUrl = element(By.className('e2e-dangerous-url'));
+ expect(untrustedUrl.getAttribute('href')).toMatch(/^unsafe:javascript/);
+ });
+
+ it('binds trusted URLs', () => {
+ let trustedUrl = element(By.className('e2e-trusted-url'));
+ expect(trustedUrl.getAttribute('href')).toMatch(/^javascript:alert/);
+ });
+
+ it('escapes untrusted resource URLs', () => {
+ let iframe = element(By.className('e2e-iframe-untrusted-src'));
+ expect(iframe.getAttribute('src')).toBe('');
+ });
+
+ it('binds trusted resource URLs', () => {
+ let iframe = element(By.className('e2e-iframe-trusted-src'));
+ expect(iframe.getAttribute('src')).toMatch(/^https:\/\/www.youtube.com\//);
+ });
+});
diff --git a/public/docs/_examples/security/ts/example-config.json b/public/docs/_examples/security/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/security/ts/plnkr.json b/public/docs/_examples/security/ts/plnkr.json
new file mode 100644
index 0000000000..4c9e85ce1e
--- /dev/null
+++ b/public/docs/_examples/security/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Content Security",
+ "basePath": "src/",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js"
+ ],
+ "tags": ["security"]
+}
diff --git a/public/docs/_examples/security/ts/src/app/app.component.ts b/public/docs/_examples/security/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..c30235e8e7
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/app/app.component.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+ Security
+
+
+ `
+})
+export class AppComponent {
+}
diff --git a/public/docs/_examples/security/ts/src/app/app.module.ts b/public/docs/_examples/security/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..21d880be3b
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/app/app.module.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+import { BypassSecurityComponent } from './bypass-security.component';
+import { InnerHtmlBindingComponent } from './inner-html-binding.component';
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [
+ AppComponent,
+ BypassSecurityComponent,
+ InnerHtmlBindingComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/security/ts/src/app/bypass-security.component.html b/public/docs/_examples/security/ts/src/app/bypass-security.component.html
new file mode 100644
index 0000000000..96adf058e2
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/app/bypass-security.component.html
@@ -0,0 +1,17 @@
+
+Bypass Security Component
+
+
+An untrusted URL:
+Click me
+A trusted URL:
+Click me
+
+
+
+Resource URL:
+Showing: {{dangerousVideoUrl}}
+Trusted:
+
+Untrusted:
+
diff --git a/public/docs/_examples/security/ts/src/app/bypass-security.component.ts b/public/docs/_examples/security/ts/src/app/bypass-security.component.ts
new file mode 100644
index 0000000000..ff41e287ba
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/app/bypass-security.component.ts
@@ -0,0 +1,38 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+import { DomSanitizer, SafeResourceUrl, SafeUrl } from '@angular/platform-browser';
+
+@Component({
+ selector: 'bypass-security',
+ templateUrl: './bypass-security.component.html',
+})
+export class BypassSecurityComponent {
+ dangerousUrl: string;
+ trustedUrl: SafeUrl;
+ dangerousVideoUrl: string;
+ videoUrl: SafeResourceUrl;
+
+ // #docregion trust-url
+ constructor(private sanitizer: DomSanitizer) {
+ // javascript: URLs are dangerous if attacker controlled.
+ // Angular sanitizes them in data binding, but you can
+ // explicitly tell Angular to trust this value:
+ this.dangerousUrl = 'javascript:alert("Hi there")';
+ this.trustedUrl = sanitizer.bypassSecurityTrustUrl(this.dangerousUrl);
+ // #enddocregion trust-url
+ this.updateVideoUrl('PUBnlbjZFAI');
+ }
+
+ // #docregion trust-video-url
+ updateVideoUrl(id: string) {
+ // Appending an ID to a YouTube URL is safe.
+ // Always make sure to construct SafeValue objects as
+ // close as possible to the input data so
+ // that it's easier to check if the value is safe.
+ this.dangerousVideoUrl = 'https://www.youtube.com/embed/' + id;
+ this.videoUrl =
+ this.sanitizer.bypassSecurityTrustResourceUrl(this.dangerousVideoUrl);
+ }
+ // #enddocregion trust-video-url
+}
diff --git a/public/docs/_examples/security/ts/src/app/inner-html-binding.component.html b/public/docs/_examples/security/ts/src/app/inner-html-binding.component.html
new file mode 100644
index 0000000000..fe540d25fe
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/app/inner-html-binding.component.html
@@ -0,0 +1,6 @@
+
+Binding innerHTML
+Bound value:
+{{htmlSnippet}}
+Result of binding to innerHTML:
+
diff --git a/public/docs/_examples/security/ts/src/app/inner-html-binding.component.ts b/public/docs/_examples/security/ts/src/app/inner-html-binding.component.ts
new file mode 100644
index 0000000000..8319503686
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/app/inner-html-binding.component.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'inner-html-binding',
+ templateUrl: './inner-html-binding.component.html',
+})
+// #docregion class
+export class InnerHtmlBindingComponent {
+ // For example, a user/attacker-controlled value from a URL.
+ htmlSnippet = 'Template Syntax ';
+}
diff --git a/public/docs/_examples/security/ts/src/index.html b/public/docs/_examples/security/ts/src/index.html
new file mode 100644
index 0000000000..d5dd11c038
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/index.html
@@ -0,0 +1,26 @@
+
+
+
+
+ Angular Content Security
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/security/ts/src/main.ts b/public/docs/_examples/security/ts/src/main.ts
new file mode 100644
index 0000000000..105b06712d
--- /dev/null
+++ b/public/docs/_examples/security/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+
diff --git a/public/docs/_examples/server-communication/e2e-spec.ts b/public/docs/_examples/server-communication/e2e-spec.ts
new file mode 100644
index 0000000000..a85c7489f8
--- /dev/null
+++ b/public/docs/_examples/server-communication/e2e-spec.ts
@@ -0,0 +1,138 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Server Communication', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ describe('Tour of Heroes (Observable)', function () {
+
+ let initialHeroCount = 4;
+ let newHeroName = 'Mr. IQ';
+ let heroCountAfterAdd = 5;
+
+ let heroListComp = element(by.tagName('hero-list'));
+ let addButton = heroListComp.element(by.tagName('button'));
+ let heroTags = heroListComp.all(by.tagName('li'));
+ let heroNameInput = heroListComp.element(by.tagName('input'));
+
+ it('should exist', function() {
+ expect(heroListComp).toBeDefined(' must exist');
+ });
+
+ it('should display ' + initialHeroCount + ' heroes after init', function () {
+ expect(heroTags.count()).toBe(initialHeroCount);
+ });
+
+ it('should not add hero with empty name', function () {
+ expect(addButton).toBeDefined('"Add Hero" button must be defined');
+ addButton.click().then(function() {
+ expect(heroTags.count()).toBe(initialHeroCount, 'No new hero should be added');
+ });
+ });
+
+ it('should add a new hero to the list', function () {
+ expect(heroNameInput).toBeDefined(' for hero name must exist');
+ expect(addButton).toBeDefined('"Add Hero" button must be defined');
+ heroNameInput.sendKeys(newHeroName);
+ addButton.click().then(function() {
+ expect(heroTags.count()).toBe(heroCountAfterAdd, 'A new hero should be added');
+ let newHeroInList = heroTags.get(heroCountAfterAdd - 1).getText();
+ expect(newHeroInList).toBe(newHeroName, 'The hero should be added to the end of the list');
+ });
+ });
+ });
+
+ describe('Wikipedia Demo', function () {
+
+ it('should initialize the demo with empty result list', function () {
+ let myWikiComp = element(by.tagName('my-wiki'));
+ expect(myWikiComp).toBeDefined(' must exist');
+ let resultList = myWikiComp.all(by.tagName('li'));
+ expect(resultList.count()).toBe(0, 'result list must be empty');
+ });
+
+ describe('Fetches after each keystroke', function () {
+ it('should fetch results after "B"', function(done: any) {
+ testForRefreshedResult('B', done);
+ });
+
+ it('should fetch results after "Ba"', function(done: any) {
+ testForRefreshedResult('a', done);
+ });
+
+ it('should fetch results after "Bas"', function(done: any) {
+ testForRefreshedResult('s', done);
+ });
+
+ it('should fetch results after "Basic"', function(done: any) {
+ testForRefreshedResult('ic', done);
+ });
+ });
+
+ function testForRefreshedResult(keyPressed: string, done: () => void) {
+ testForResult('my-wiki', keyPressed, false, done);
+ }
+ });
+
+ describe('Smarter Wikipedia Demo', function () {
+
+ it('should initialize the demo with empty result list', function () {
+ let myWikiSmartComp = element(by.tagName('my-wiki-smart'));
+ expect(myWikiSmartComp).toBeDefined(' must exist');
+ let resultList = myWikiSmartComp.all(by.tagName('li'));
+ expect(resultList.count()).toBe(0, 'result list must be empty');
+ });
+
+ it('should fetch results after "Java"', function(done: any) {
+ testForNewResult('Java', done);
+ });
+
+ it('should fetch results after "JavaS"', function(done: any) {
+ testForStaleResult('S', done);
+ });
+
+ it('should fetch results after "JavaSc"', function(done: any) {
+ testForStaleResult('c', done);
+ });
+
+ it('should fetch results after "JavaScript"', function(done: any) {
+ testForStaleResult('ript', done);
+ });
+
+
+ function testForNewResult(keyPressed: string, done: () => void) {
+ testForResult('my-wiki-smart', keyPressed, false, done);
+ }
+
+ function testForStaleResult(keyPressed: string, done: () => void) {
+ testForResult('my-wiki-smart', keyPressed, true, done);
+ }
+
+ });
+
+ function testForResult(componentTagName: string, keyPressed: string, hasListBeforeSearch: boolean, done: () => void) {
+ let searchWait = 1000; // Wait for wikipedia but not so long that tests timeout
+ let wikiComponent = element(by.tagName(componentTagName));
+ expect(wikiComponent).toBeDefined('<' + componentTagName + '> must exist');
+ let searchBox = wikiComponent.element(by.tagName('input'));
+ expect(searchBox).toBeDefined(' for search must exist');
+
+ searchBox.sendKeys(keyPressed).then(function () {
+ let resultList = wikiComponent.all(by.tagName('li'));
+
+ if (hasListBeforeSearch) {
+ expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty before search');
+ }
+
+ setTimeout(function() {
+ expect(resultList.count()).toBeGreaterThan(0, 'result list should not be empty after search');
+ done();
+ }, searchWait);
+ });
+ }
+
+});
diff --git a/public/docs/_examples/server-communication/ts/example-config.json b/public/docs/_examples/server-communication/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/server-communication/ts/plnkr.json b/public/docs/_examples/server-communication/ts/plnkr.json
new file mode 100644
index 0000000000..fe966be012
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Http",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ],
+ "tags": ["http", "jsonp"]
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/app.component.ts b/public/docs/_examples/server-communication/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..780d044cab
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/app.component.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `
+
+
+
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/server-communication/ts/src/app/app.module.1.ts b/public/docs/_examples/server-communication/ts/src/app/app.module.1.ts
new file mode 100644
index 0000000000..fb7012aa02
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/app.module.1.ts
@@ -0,0 +1,23 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { HttpModule, JsonpModule } from '@angular/http';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule,
+ JsonpModule
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {
+}
+
+
+
diff --git a/public/docs/_examples/server-communication/ts/src/app/app.module.ts b/public/docs/_examples/server-communication/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..fd0c720c3c
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/app.module.ts
@@ -0,0 +1,46 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { HttpModule, JsonpModule } from '@angular/http';
+
+
+import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
+import { HeroData } from './hero-data';
+import { requestOptionsProvider } from './default-request-options.service';
+
+import { AppComponent } from './app.component';
+
+import { HeroListComponent } from './toh/hero-list.component';
+import { HeroListPromiseComponent } from './toh/hero-list.component.promise';
+
+import { WikiComponent } from './wiki/wiki.component';
+import { WikiSmartComponent } from './wiki/wiki-smart.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ HttpModule,
+ JsonpModule,
+ // #docregion in-mem-web-api
+ InMemoryWebApiModule.forRoot(HeroData)
+ // #enddocregion in-mem-web-api
+ ],
+ declarations: [
+ AppComponent,
+ HeroListComponent,
+ HeroListPromiseComponent,
+ WikiComponent,
+ WikiSmartComponent
+ ],
+// #docregion provide-default-request-options
+ providers: [ requestOptionsProvider ],
+// #enddocregion provide-default-request-options
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {}
+
+
+
diff --git a/public/docs/_examples/server-communication/ts/src/app/default-request-options.service.ts b/public/docs/_examples/server-communication/ts/src/app/default-request-options.service.ts
new file mode 100644
index 0000000000..9ec52daa80
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/default-request-options.service.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { BaseRequestOptions, RequestOptions } from '@angular/http';
+
+@Injectable()
+export class DefaultRequestOptions extends BaseRequestOptions {
+
+ constructor() {
+ super();
+
+ // Set the default 'Content-Type' header
+ this.headers.set('Content-Type', 'application/json');
+ }
+}
+
+export const requestOptionsProvider = { provide: RequestOptions, useClass: DefaultRequestOptions };
diff --git a/public/docs/_examples/server-communication/ts/src/app/hero-data.ts b/public/docs/_examples/server-communication/ts/src/app/hero-data.ts
new file mode 100644
index 0000000000..4db6aca115
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/hero-data.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { InMemoryDbService } from 'angular-in-memory-web-api';
+export class HeroData implements InMemoryDbService {
+ createDb() {
+ let heroes = [
+ { id: 1, name: 'Windstorm' },
+ { id: 2, name: 'Bombasto' },
+ { id: 3, name: 'Magneta' },
+ { id: 4, name: 'Tornado' }
+ ];
+ return {heroes};
+ }
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/heroes.json b/public/docs/_examples/server-communication/ts/src/app/heroes.json
new file mode 100644
index 0000000000..dfb589066b
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/heroes.json
@@ -0,0 +1,8 @@
+{
+ "data": [
+ { "id": 1, "name": "Windstorm" },
+ { "id": 2, "name": "Bombasto" },
+ { "id": 3, "name": "Magneta" },
+ { "id": 4, "name": "Tornado" }
+ ]
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.html b/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.html
new file mode 100644
index 0000000000..65ca9cfbb7
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.html
@@ -0,0 +1,11 @@
+
+Tour of Heroes ({{mode}})
+Heroes:
+
+
+New hero name:
+Add Hero
+
+{{errorMessage}}
diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.promise.ts b/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.promise.ts
new file mode 100644
index 0000000000..4bbe7eade2
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.promise.ts
@@ -0,0 +1,40 @@
+// #docregion
+// Promise Version
+import { Component, OnInit } from '@angular/core';
+import { Hero } from './hero';
+import { HeroService } from './hero.service.promise';
+
+@Component({
+ selector: 'hero-list-promise',
+ templateUrl: './hero-list.component.html',
+ providers: [ HeroService ],
+ styles: ['.error {color:red;}']
+})
+// #docregion component
+export class HeroListPromiseComponent implements OnInit {
+ errorMessage: string;
+ heroes: Hero[];
+ mode = 'Promise';
+
+ constructor (private heroService: HeroService) {}
+
+ ngOnInit() { this.getHeroes(); }
+
+ // #docregion methods
+ getHeroes() {
+ this.heroService.getHeroes()
+ .then(
+ heroes => this.heroes = heroes,
+ error => this.errorMessage = error);
+ }
+
+ addHero (name: string) {
+ if (!name) { return; }
+ this.heroService.addHero(name)
+ .then(
+ hero => this.heroes.push(hero),
+ error => this.errorMessage = error);
+ }
+ // #enddocregion methods
+}
+// #enddocregion component
diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.ts b/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.ts
new file mode 100644
index 0000000000..8cca504762
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero-list.component.ts
@@ -0,0 +1,44 @@
+// #docregion
+// Observable Version
+import { Component, OnInit } from '@angular/core';
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+@Component({
+ selector: 'hero-list',
+ templateUrl: './hero-list.component.html',
+ providers: [ HeroService ],
+ styles: ['.error {color:red;}']
+})
+// #docregion component
+export class HeroListComponent implements OnInit {
+ errorMessage: string;
+ heroes: Hero[];
+ mode = 'Observable';
+
+ constructor (private heroService: HeroService) {}
+
+ ngOnInit() { this.getHeroes(); }
+
+ // #docregion methods
+ // #docregion getHeroes
+ getHeroes() {
+ this.heroService.getHeroes()
+ .subscribe(
+ heroes => this.heroes = heroes,
+ error => this.errorMessage = error);
+ }
+ // #enddocregion getHeroes
+
+ // #docregion addHero
+ addHero(name: string) {
+ if (!name) { return; }
+ this.heroService.create(name)
+ .subscribe(
+ hero => this.heroes.push(hero),
+ error => this.errorMessage = error);
+ }
+ // #enddocregion addHero
+ // #enddocregion methods
+}
+// #enddocregion component
diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.promise.ts b/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.promise.ts
new file mode 100644
index 0000000000..e38bd4bebf
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.promise.ts
@@ -0,0 +1,60 @@
+// #docplaster
+// #docregion
+// Promise Version
+import { Injectable } from '@angular/core';
+import { Http, Response } from '@angular/http';
+import { Headers, RequestOptions } from '@angular/http';
+
+// #docregion rxjs-imports
+import 'rxjs/add/operator/toPromise';
+// #enddocregion rxjs-imports
+
+import { Hero } from './hero';
+
+@Injectable()
+export class HeroService {
+ // URL to web api
+ private heroesUrl = 'app/heroes';
+
+ constructor (private http: Http) {}
+
+ // #docregion methods
+ getHeroes (): Promise {
+ return this.http.get(this.heroesUrl)
+ .toPromise()
+ .then(this.extractData)
+ .catch(this.handleError);
+ }
+
+ addHero (name: string): Promise {
+ let headers = new Headers({ 'Content-Type': 'application/json' });
+ let options = new RequestOptions({ headers: headers });
+
+ return this.http.post(this.heroesUrl, { name }, options)
+ .toPromise()
+ .then(this.extractData)
+ .catch(this.handleError);
+ }
+
+ private extractData(res: Response) {
+ let body = res.json();
+ return body.data || { };
+ }
+
+ private handleError (error: Response | any) {
+ // In a real world app, we might use a remote logging infrastructure
+ let errMsg: string;
+ if (error instanceof Response) {
+ const body = error.json() || '';
+ const err = body.error || JSON.stringify(body);
+ errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
+ } else {
+ errMsg = error.message ? error.message : error.toString();
+ }
+ console.error(errMsg);
+ return Promise.reject(errMsg);
+ }
+
+// #enddocregion methods
+}
+// #enddocregion
diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts b/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts
new file mode 100644
index 0000000000..4f0da49021
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero.service.ts
@@ -0,0 +1,80 @@
+// #docplaster
+// #docregion
+// Observable Version
+// #docregion v1
+import { Injectable } from '@angular/core';
+import { Http, Response } from '@angular/http';
+// #enddocregion v1
+// #docregion import-request-options
+import { Headers, RequestOptions } from '@angular/http';
+// #enddocregion import-request-options
+// #docregion v1
+
+// #docregion rxjs-imports
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+// #enddocregion rxjs-imports
+
+import { Hero } from './hero';
+
+@Injectable()
+export class HeroService {
+ // #docregion endpoint
+ private heroesUrl = 'api/heroes'; // URL to web API
+ // #enddocregion endpoint
+
+ // #docregion ctor
+ constructor (private http: Http) {}
+ // #enddocregion ctor
+
+ // #docregion methods, error-handling, http-get
+ getHeroes(): Observable {
+ return this.http.get(this.heroesUrl)
+ .map(this.extractData)
+ .catch(this.handleError);
+ }
+ // #enddocregion error-handling, http-get, v1
+
+ // #docregion create, create-sig
+ create(name: string): Observable {
+ // #enddocregion create-sig
+ let headers = new Headers({ 'Content-Type': 'application/json' });
+ let options = new RequestOptions({ headers: headers });
+
+ return this.http.post(this.heroesUrl, { name }, options)
+ .map(this.extractData)
+ .catch(this.handleError);
+ }
+ // #enddocregion create
+
+ // #docregion v1, extract-data
+ private extractData(res: Response) {
+ let body = res.json();
+ return body.data || { };
+ }
+ // #enddocregion extract-data
+ // #docregion error-handling
+
+ private handleError (error: Response | any) {
+ // In a real world app, you might use a remote logging infrastructure
+ let errMsg: string;
+ if (error instanceof Response) {
+ const body = error.json() || '';
+ const err = body.error || JSON.stringify(body);
+ errMsg = `${error.status} - ${error.statusText || ''} ${err}`;
+ } else {
+ errMsg = error.message ? error.message : error.toString();
+ }
+ console.error(errMsg);
+ return Observable.throw(errMsg);
+ }
+ // #enddocregion error-handling, methods
+}
+// #enddocregion
+
+/*
+ // #docregion endpoint-json
+ private heroesUrl = 'app/heroes.json'; // URL to JSON file
+ // #enddocregion endpoint-json
+*/
diff --git a/public/docs/_examples/server-communication/ts/src/app/toh/hero.ts b/public/docs/_examples/server-communication/ts/src/app/toh/hero.ts
new file mode 100644
index 0000000000..09b8d295ce
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/toh/hero.ts
@@ -0,0 +1,6 @@
+// #docregion
+export class Hero {
+ constructor(
+ public id: number,
+ public name: string) { }
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/wiki/wiki-smart.component.ts b/public/docs/_examples/server-communication/ts/src/app/wiki/wiki-smart.component.ts
new file mode 100644
index 0000000000..fc453a820e
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/wiki/wiki-smart.component.ts
@@ -0,0 +1,47 @@
+/* tslint:disable: member-ordering forin */
+// #docplaster
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+// #docregion rxjs-imports
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/debounceTime';
+import 'rxjs/add/operator/distinctUntilChanged';
+import 'rxjs/add/operator/switchMap';
+
+// #docregion import-subject
+import { Subject } from 'rxjs/Subject';
+// #enddocregion import-subject
+
+import { WikipediaService } from './wikipedia.service';
+
+@Component({
+ selector: 'my-wiki-smart',
+ template: `
+ Smarter Wikipedia Demo
+ Search when typing stops
+
+ `,
+ providers: [ WikipediaService ]
+})
+export class WikiSmartComponent implements OnInit {
+ items: Observable;
+
+ constructor (private wikipediaService: WikipediaService) {}
+
+ // #docregion subject
+ private searchTermStream = new Subject();
+ search(term: string) { this.searchTermStream.next(term); }
+ // #enddocregion subject
+
+ ngOnInit() {
+ // #docregion observable-operators
+ this.items = this.searchTermStream
+ .debounceTime(300)
+ .distinctUntilChanged()
+ .switchMap((term: string) => this.wikipediaService.search(term));
+ // #enddocregion observable-operators
+ }
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/wiki/wiki.component.ts b/public/docs/_examples/server-communication/ts/src/app/wiki/wiki.component.ts
new file mode 100644
index 0000000000..4230df12a1
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/wiki/wiki.component.ts
@@ -0,0 +1,26 @@
+// #docregion
+import { Component } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { WikipediaService } from './wikipedia.service';
+
+@Component({
+ selector: 'my-wiki',
+ template: `
+ Wikipedia Demo
+ Search after each keystroke
+
+ `,
+ providers: [ WikipediaService ]
+})
+export class WikiComponent {
+ items: Observable;
+
+ constructor (private wikipediaService: WikipediaService) { }
+
+ search (term: string) {
+ this.items = this.wikipediaService.search(term);
+ }
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/wiki/wikipedia.service.1.ts b/public/docs/_examples/server-communication/ts/src/app/wiki/wikipedia.service.1.ts
new file mode 100644
index 0000000000..5cbcb7d707
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/wiki/wikipedia.service.1.ts
@@ -0,0 +1,26 @@
+// Create the query string by hand
+// #docregion
+import { Injectable } from '@angular/core';
+import { Jsonp } from '@angular/http';
+
+import 'rxjs/add/operator/map';
+
+@Injectable()
+export class WikipediaService {
+ constructor(private jsonp: Jsonp) { }
+
+ // TODO: Add error handling
+ search(term: string) {
+
+ let wikiUrl = 'http://en.wikipedia.org/w/api.php';
+
+ // #docregion query-string
+ let queryString =
+ `?search=${term}&action=opensearch&format=json&callback=JSONP_CALLBACK`;
+
+ return this.jsonp
+ .get(wikiUrl + queryString)
+ .map(response => response.json()[1]);
+ // #enddocregion query-string
+ }
+}
diff --git a/public/docs/_examples/server-communication/ts/src/app/wiki/wikipedia.service.ts b/public/docs/_examples/server-communication/ts/src/app/wiki/wikipedia.service.ts
new file mode 100644
index 0000000000..a38167d1c6
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/app/wiki/wikipedia.service.ts
@@ -0,0 +1,30 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { Jsonp, URLSearchParams } from '@angular/http';
+
+import 'rxjs/add/operator/map';
+
+@Injectable()
+export class WikipediaService {
+ constructor(private jsonp: Jsonp) {}
+
+ search (term: string) {
+
+ let wikiUrl = 'http://en.wikipedia.org/w/api.php';
+
+ // #docregion search-parameters
+ let params = new URLSearchParams();
+ params.set('search', term); // the user's search value
+ params.set('action', 'opensearch');
+ params.set('format', 'json');
+ params.set('callback', 'JSONP_CALLBACK');
+ // #enddocregion search-parameters
+
+ // #docregion call-jsonp
+ // TODO: Add error handling
+ return this.jsonp
+ .get(wikiUrl, { search: params })
+ .map(response => response.json()[1]);
+ // #enddocregion call-jsonp
+ }
+}
diff --git a/public/docs/_examples/server-communication/ts/src/index.html b/public/docs/_examples/server-communication/ts/src/index.html
new file mode 100644
index 0000000000..5c51f21c91
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Angular Http Demo
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/server-communication/ts/src/main.ts b/public/docs/_examples/server-communication/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/server-communication/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/setup/e2e-spec.ts b/public/docs/_examples/setup/e2e-spec.ts
new file mode 100644
index 0000000000..73921707ee
--- /dev/null
+++ b/public/docs/_examples/setup/e2e-spec.ts
@@ -0,0 +1,17 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('QuickStart E2E Tests', function () {
+
+ let expectedMsg = 'Hello Angular';
+
+ beforeEach(function () {
+ browser.get('');
+ });
+
+ it(`should display: ${expectedMsg}`, function () {
+ expect(element(by.css('h1')).getText()).toEqual(expectedMsg);
+ });
+
+});
diff --git a/public/docs/_examples/setup/ts/example-config.json b/public/docs/_examples/setup/ts/example-config.json
new file mode 100644
index 0000000000..eb33c943c7
--- /dev/null
+++ b/public/docs/_examples/setup/ts/example-config.json
@@ -0,0 +1,3 @@
+{
+ "unittesting": true
+}
diff --git a/public/docs/_examples/setup/ts/non-essential-files.txt b/public/docs/_examples/setup/ts/non-essential-files.txt
new file mode 100644
index 0000000000..3c9cdde9dc
--- /dev/null
+++ b/public/docs/_examples/setup/ts/non-essential-files.txt
@@ -0,0 +1,13 @@
+.git
+.gitignore
+.travis.yml
+*.spec*.ts
+CHANGELOG.md
+e2e
+favicon.ico
+karma.conf.js
+karma-test-shim.js
+LICENSE
+non-essential-files.txt
+protractor.config.js
+README.md
diff --git a/public/docs/_examples/setup/ts/plnkr.json b/public/docs/_examples/setup/ts/plnkr.json
new file mode 100644
index 0000000000..22b528eec5
--- /dev/null
+++ b/public/docs/_examples/setup/ts/plnkr.json
@@ -0,0 +1,13 @@
+{
+ "description": "QuickStart Setup",
+ "basePath": "src/",
+ "files": [
+ "app/app.component.ts",
+ "app/app.module.ts",
+ "index.html",
+ "main.ts",
+ "styles.css"
+ ],
+ "open": "app/app.component.ts",
+ "tags": ["quickstart", "setup", "seed"]
+}
diff --git a/public/docs/_examples/setup/ts/quickstart-specs.plnkr.json b/public/docs/_examples/setup/ts/quickstart-specs.plnkr.json
new file mode 100644
index 0000000000..a7dfedb595
--- /dev/null
+++ b/public/docs/_examples/setup/ts/quickstart-specs.plnkr.json
@@ -0,0 +1,13 @@
+{
+ "description": "Quickstart AppComponent Testing",
+ "basePath": "src/",
+ "files":[
+ "browser-test-shim.js",
+ "app/app.component.ts",
+ "app/app.component.spec.ts",
+ "quickstart-specs.html"
+ ],
+ "main": "quickstart-specs.html",
+ "open": "app/app.component.spec.ts",
+ "tags": ["quickstart", "setup", "testing"]
+}
diff --git a/public/docs/_examples/setup/ts/src/app/app.component.spec.ts b/public/docs/_examples/setup/ts/src/app/app.component.spec.ts
new file mode 100644
index 0000000000..7769024464
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/app/app.component.spec.ts
@@ -0,0 +1,33 @@
+import { AppComponent } from './app.component';
+
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+describe('AppComponent', function () {
+ let de: DebugElement;
+ let comp: AppComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ AppComponent ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AppComponent);
+ comp = fixture.componentInstance;
+ de = fixture.debugElement.query(By.css('h1'));
+ });
+
+ it('should create component', () => expect(comp).toBeDefined() );
+
+ it('should have expected text', () => {
+ fixture.detectChanges();
+ const h1 = de.nativeElement;
+ expect(h1.innerText).toMatch(/angular/i,
+ ' should say something about "Angular"');
+ });
+});
diff --git a/public/docs/_examples/setup/ts/src/app/app.component.ts b/public/docs/_examples/setup/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..1ef28fc5c4
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/app/app.component.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ template: `Hello {{name}} `
+})
+export class AppComponent { name = 'Angular'; }
diff --git a/public/docs/_examples/setup/ts/src/app/app.module.ts b/public/docs/_examples/setup/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..50a0e6eb47
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/app/app.module.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/setup/ts/src/index.html b/public/docs/_examples/setup/ts/src/index.html
new file mode 100644
index 0000000000..58e5112308
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/index.html
@@ -0,0 +1,28 @@
+
+
+
+
+ Angular Quickstart Seed
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/setup/ts/src/main.ts b/public/docs/_examples/setup/ts/src/main.ts
new file mode 100644
index 0000000000..02d58dceac
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/setup/ts/src/quickstart-specs.html b/public/docs/_examples/setup/ts/src/quickstart-specs.html
new file mode 100644
index 0000000000..9bc81ccf2e
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/quickstart-specs.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ 1st Specs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/setup/ts/src/systemjs.config.extras.js b/public/docs/_examples/setup/ts/src/systemjs.config.extras.js
new file mode 100644
index 0000000000..027dfe58cf
--- /dev/null
+++ b/public/docs/_examples/setup/ts/src/systemjs.config.extras.js
@@ -0,0 +1,11 @@
+/**
+ * Add barrels and stuff
+ * Adjust as necessary for your application needs.
+ */
+// (function (global) {
+// System.config({
+// packages: {
+// // add packages here
+// }
+// });
+// })(this);
diff --git a/public/docs/_examples/structural-directives/e2e-spec.ts b/public/docs/_examples/structural-directives/e2e-spec.ts
new file mode 100644
index 0000000000..51f30bf0bb
--- /dev/null
+++ b/public/docs/_examples/structural-directives/e2e-spec.ts
@@ -0,0 +1,58 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Structural Directives', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('first div should show hero name with *ngIf', function () {
+ const allDivs = element.all(by.tagName('div'));
+ expect(allDivs.get(0).getText()).toEqual('Mr. Nice');
+ });
+
+ it('first li should show hero name with *ngFor', function () {
+ const allLis = element.all(by.tagName('li'));
+ expect(allLis.get(0).getText()).toEqual('Mr. Nice');
+ });
+
+ it('ngSwitch have three instances', function () {
+ const happyHeroEls = element.all(by.tagName('happy-hero'));
+ expect(happyHeroEls.count()).toEqual(3);
+ });
+
+ it('should toggle *ngIf="hero" with a button', function () {
+ const toggleHeroButton = element.all(by.cssContainingText('button', 'Toggle hero')).get(0);
+ const paragraph = element.all(by.cssContainingText('p', 'I turned the corner'));
+ expect(paragraph.get(0).getText()).toContain('I waved');
+ toggleHeroButton.click().then(() => {
+ expect(paragraph.get(0).getText()).not.toContain('I waved');
+ });
+ });
+
+ it('should have only one "Hip!" (the other is erased)', function () {
+ const paragraph = element.all(by.cssContainingText('p', 'Hip!'));
+ expect(paragraph.count()).toEqual(1);
+ });
+
+ it('myUnless should show 3 paragraph (A)s and (B)s at the start', function () {
+ const paragraph = element.all(by.css('p.unless'));
+ expect(paragraph.count()).toEqual(3);
+ for (let i = 0; i < 3; i++) {
+ expect(paragraph.get(i).getText()).toContain('(A)');
+ }
+ });
+
+ it('myUnless should show 1 paragraph (B) after toggling condition', function () {
+ const toggleConditionButton = element.all(by.cssContainingText('button', 'Toggle condition')).get(0);
+ const paragraph = element.all(by.css('p.unless'));
+
+ toggleConditionButton.click().then(() => {
+ expect(paragraph.count()).toEqual(1);
+ expect(paragraph.get(0).getText()).toContain('(B)');
+ });
+ });
+});
+
diff --git a/public/docs/_examples/structural-directives/ts/example-config.json b/public/docs/_examples/structural-directives/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/structural-directives/ts/plnkr.json b/public/docs/_examples/structural-directives/ts/plnkr.json
new file mode 100644
index 0000000000..58a26d7b4f
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/plnkr.json
@@ -0,0 +1,13 @@
+{
+ "description": "Structural directives",
+ "basePath": "src/",
+ "files": [
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!app/scrap.txt"
+ ],
+ "tags": [
+ "structural", "directives", "template", "ngIf",
+ "ngSwitch", "ngFor"
+ ]
+}
diff --git a/public/docs/_examples/structural-directives/ts/src/app/app.component.css b/public/docs/_examples/structural-directives/ts/src/app/app.component.css
new file mode 100644
index 0000000000..e28be894f8
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/app.component.css
@@ -0,0 +1,70 @@
+/* #docregion */
+button {
+ min-width: 100px;
+ font-size: 100%;
+}
+
+.box {
+ border: 1px solid gray;
+ max-width: 600px;
+ padding: 4px;
+}
+.choices {
+ font-style: italic;
+}
+
+code, .code {
+ background-color: #eee;
+ color: black;
+ font-family: Courier, sans-serif;
+ font-size: 85%;
+}
+
+div.code {
+ width: 400px;
+}
+
+.heroic {
+ font-size: 150%;
+ font-weight: bold;
+}
+
+hr {
+ margin: 40px 0
+}
+
+.odd {
+ background-color: palegoldenrod;
+}
+
+td, th {
+ text-align: left;
+ vertical-align: top;
+}
+
+/* #docregion p-span */
+p span { color: red; font-size: 70%; }
+/* #enddocregion p-span */
+
+.unless {
+ border: 2px solid;
+ padding: 6px;
+}
+
+p.unless {
+ width: 500px;
+}
+
+button.a, span.a, .unless.a {
+ color: red;
+ border-color: gold;
+ background-color: yellow;
+ font-size: 100%;
+}
+
+button.b, span.b, .unless.b {
+ color: black;
+ border-color: green;
+ background-color: lightgreen;
+ font-size: 100%;
+}
diff --git a/public/docs/_examples/structural-directives/ts/src/app/app.component.html b/public/docs/_examples/structural-directives/ts/src/app/app.component.html
new file mode 100644
index 0000000000..49c4928aad
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/app.component.html
@@ -0,0 +1,250 @@
+
+
+Structural Directives
+
+Conditional display of hero
+
+
+
+{{hero.name}}
+
+
+
+List of heroes
+
+
+
+
+
+
+
+
+NgIf
+
+
+
+ Expression is true and ngIf is true.
+ This paragraph is in the DOM.
+
+
+ Expression is false and ngIf is false.
+ This paragraph is not in the DOM.
+
+
+
+
+
+ Expression sets display to "block".
+ This paragraph is visible.
+
+
+ Expression sets display to "none".
+ This paragraph is hidden but still in the DOM.
+
+
+
+NgIf with template
+<ng-template> element
+
+
+ {{hero.name}}
+
+
+
+template attribute
+
+{{hero.name}}
+
+
+
+
+<ng-container>
+
+*ngIf with a <ng-container>
+
+Toggle hero
+
+
+
+ I turned the corner
+
+ and saw {{hero.name}}. I waved
+
+ and continued on my way.
+
+
+
+
+ I turned the corner
+
+ and saw {{hero.name}}. I waved
+
+ and continued on my way.
+
+
+
+<select> with <span>
+
+
+ Pick your favorite hero
+ ( show sad )
+
+
+
+
+ {{h.name}} ({{h.emotion}})
+
+
+
+
+
+<select> with <ng-container>
+
+
+ Pick your favorite hero
+ ( show sad )
+
+
+
+
+ {{h.name}} ({{h.emotion}})
+
+
+
+
+
+
+
+
+NgFor
+
+
+
+
<div *ngFor="let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
+
+
+ ({{i}}) {{hero.name}}
+
+
+
+
<div template="ngFor let hero of heroes; let i=index; let odd=odd; trackBy: trackById" [class.odd]="odd">
+
+
+ ({{i}}) {{hero.name}}
+
+
+
+
<template ngFor let-hero [ngForOf]="heroes" let-i="index" let-odd="odd" [ngForTrackBy]="trackById">
+
+
+ ({{i}}) {{hero.name}}
+
+
+
+
+
+
+NgSwitch
+
+Pick your favorite hero
+
+
+ {{h.name}}
+
+ None of the above
+
+
+NgSwitch
+
+
+
+
+
+
+
+
+
+
+NgSwitch with template attribute
+
+
+
+
+
+
+
+
+
+NgSwitch with <ng-template>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+<template>
+
+Hip!
+
+ Hip!
+
+Hooray!
+
+
+
+
+UnlessDirective
+
+ The condition is currently
+ {{condition}} .
+
+ Toggle condition to {{condition ? 'false' : 'true'}}
+
+
+
+
+ (A) This paragraph is displayed because the condition is false.
+
+
+
+ (B) Although the condition is true,
+ this paragraph is displayed because myUnless is set to false.
+
+
+
+
+UnlessDirective with template
+
+
+Show this sentence unless the condition is true.
+
+
+
+ (A) <p template="myUnless condition" class="code unless">
+
+
+
+
+ (A) <template [myUnless]="condition">
+
+
+
diff --git a/public/docs/_examples/structural-directives/ts/src/app/app.component.ts b/public/docs/_examples/structural-directives/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..ff6f0e65a1
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/app.component.ts
@@ -0,0 +1,23 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { Hero, heroes } from './hero';
+
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html',
+ styleUrls: [ './app.component.css' ]
+})
+export class AppComponent {
+ heroes = heroes;
+ hero = this.heroes[0];
+
+ condition = false;
+ logs: string[] = [];
+ showSad = true;
+ status = 'ready';
+
+ // #docregion trackByHero
+ trackById(index: number, hero: Hero): number { return hero.id; }
+ // #enddocregion trackByHero
+}
diff --git a/public/docs/_examples/structural-directives/ts/src/app/app.module.ts b/public/docs/_examples/structural-directives/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..b6ffb456c9
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/app.module.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+import { heroSwitchComponents } from './hero-switch.components';
+import { UnlessDirective } from './unless.directive';
+
+@NgModule({
+ imports: [ BrowserModule, FormsModule ],
+ declarations: [
+ AppComponent,
+ heroSwitchComponents,
+ UnlessDirective
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/structural-directives/ts/src/app/hero-switch.components.ts b/public/docs/_examples/structural-directives/ts/src/app/hero-switch.components.ts
new file mode 100644
index 0000000000..6608422d15
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/hero-switch.components.ts
@@ -0,0 +1,43 @@
+// #docregion
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+@Component({
+ selector: 'happy-hero',
+ template: `Wow. You like {{hero.name}}. What a happy hero ... just like you.`
+})
+export class HappyHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'sad-hero',
+ template: `You like {{hero.name}}? Such a sad hero. Are you sad too?`
+})
+export class SadHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'confused-hero',
+ template: `Are you as confused as {{hero.name}}?`
+})
+export class ConfusedHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'unknown-hero',
+ template: `{{message}}`
+})
+export class UnknownHeroComponent {
+ @Input() hero: Hero;
+ get message() {
+ return this.hero && this.hero.name ?
+ `${this.hero.name} is strange and mysterious.` :
+ 'Are you feeling indecisive?';
+ }
+}
+
+export const heroSwitchComponents =
+ [ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
diff --git a/public/docs/_examples/structural-directives/ts/src/app/hero.ts b/public/docs/_examples/structural-directives/ts/src/app/hero.ts
new file mode 100644
index 0000000000..a1de3b3b82
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/hero.ts
@@ -0,0 +1,13 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+ emotion?: string;
+}
+
+export const heroes: Hero[] = [
+ { id: 1, name: 'Mr. Nice', emotion: 'happy'},
+ { id: 2, name: 'Narco', emotion: 'sad' },
+ { id: 3, name: 'Windstorm', emotion: 'confused' },
+ { id: 4, name: 'Magneta'}
+];
diff --git a/public/docs/_examples/structural-directives/ts/src/app/scrap.txt b/public/docs/_examples/structural-directives/ts/src/app/scrap.txt
new file mode 100644
index 0000000000..96426d37f0
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/scrap.txt
@@ -0,0 +1,21 @@
+// interesting but unused code
+ heroChooser(picker: HTMLFieldSetElement) {
+ let choices = picker.children;
+ this.favoriteHero = undefined;
+ for (let i = 0; i < choices.length; i++) {
+ let choice = choices[i].children[0] as HTMLInputElement;
+ if (choice.checked) { this.favoriteHero = this.heroes[i]; }
+ }
+ }
+
+
+Switch with *ngFor repeated switchCases using <ng-container>
+
+
+ Your favorite hero is ...
+
+ {{hero.name}}
+
+ None of the above
+
+
diff --git a/public/docs/_examples/structural-directives/ts/src/app/unless.directive.ts b/public/docs/_examples/structural-directives/ts/src/app/unless.directive.ts
new file mode 100644
index 0000000000..470bbd932f
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/app/unless.directive.ts
@@ -0,0 +1,51 @@
+// #docplaster
+// #docregion
+// #docregion no-docs, skeleton
+import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
+
+// #enddocregion skeleton
+/**
+ * Add the template content to the DOM unless the condition is true.
+// #enddocregion no-docs
+ *
+ * If the expression assigned to `myUnless` evaluates to a truthy value
+ * then the templated elements are removed removed from the DOM,
+ * the templated elements are (re)inserted into the DOM.
+ *
+ *
+ * Congrats! Everything is great!
+ *
+ *
+ * ### Syntax
+ *
+ * - `...
`
+ * - `...
`
+ * - `...
`
+ *
+// #docregion no-docs
+ */
+// #docregion skeleton
+@Directive({ selector: '[myUnless]'})
+export class UnlessDirective {
+ // #enddocregion skeleton
+ private hasView = false;
+
+ // #docregion ctor
+ constructor(
+ private templateRef: TemplateRef,
+ private viewContainer: ViewContainerRef) { }
+ // #enddocregion ctor
+
+ // #docregion set
+ @Input() set myUnless(condition: boolean) {
+ if (!condition && !this.hasView) {
+ this.viewContainer.createEmbeddedView(this.templateRef);
+ this.hasView = true;
+ } else if (condition && this.hasView) {
+ this.viewContainer.clear();
+ this.hasView = false;
+ }
+ }
+ // #enddocregion set
+ // #docregion skeleton
+}
diff --git a/public/docs/_examples/structural-directives/ts/src/index.html b/public/docs/_examples/structural-directives/ts/src/index.html
new file mode 100644
index 0000000000..ce7a33266d
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+ Angular Structural Directives
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/structural-directives/ts/src/main.ts b/public/docs/_examples/structural-directives/ts/src/main.ts
new file mode 100644
index 0000000000..105b06712d
--- /dev/null
+++ b/public/docs/_examples/structural-directives/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+
diff --git a/public/docs/_examples/style-guide/e2e-spec.ts b/public/docs/_examples/style-guide/e2e-spec.ts
new file mode 100644
index 0000000000..d143ae5573
--- /dev/null
+++ b/public/docs/_examples/style-guide/e2e-spec.ts
@@ -0,0 +1,191 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Style Guide', function () {
+ it('01-01', function () {
+ browser.get('#/01-01');
+
+ let pre = element(by.tagName('toh-heroes > pre'));
+ expect(pre.getText()).toContain('Bombasto');
+ expect(pre.getText()).toContain('Tornado');
+ expect(pre.getText()).toContain('Magneta');
+ });
+
+ it('02-07', function () {
+ browser.get('#/02-07');
+
+ let hero = element(by.tagName('toh-hero > div'));
+ let users = element(by.tagName('admin-users > div'));
+
+ expect(hero.getText()).toBe('hero component');
+ expect(users.getText()).toBe('users component');
+ });
+
+ it('02-08', function () {
+ browser.get('#/02-08');
+
+ let input = element(by.tagName('input[tohvalidate]'));
+ expect(input.isPresent()).toBe(true);
+ });
+
+ it('03-01', function () {
+ browser.get('#/03-01');
+
+ let div = element(by.tagName('sg-app > div'));
+ expect(div.getText()).toBe('The expected error is 42');
+ });
+
+ it('03-02', function () {
+ browser.get('#/03-02');
+
+ let divs = element.all(by.tagName('sg-app > div'));
+ expect(divs.get(0).getText()).toBe('Heroes url: api/heroes');
+ expect(divs.get(1).getText()).toBe('Villains url: api/villains');
+ });
+
+ it('03-03', function () {
+ browser.get('#/03-03');
+
+ let div = element(by.tagName('sg-app > div'));
+ expect(div.getText()).toBe('Our hero is RubberMan and He is so elastic');
+ });
+
+ it('03-04', function () {
+ browser.get('#/03-04');
+
+ let buttons = element.all(by.tagName('sg-app > button'));
+ expect(buttons.get(0).getText()).toBe('Show toast');
+ expect(buttons.get(1).getText()).toBe('Hide toast');
+ });
+
+ it('03-06', function () {
+ browser.get('#/03-06');
+
+ let div = element(by.tagName('sg-app > div'));
+ expect(div.getText()).toBe('Actual favorite: Windstorm');
+
+ let lis = element.all(by.tagName('sg-app > ul > li'));
+ expect(lis.get(0).getText()).toBe('Windstorm');
+ expect(lis.get(1).getText()).toBe('Bombasto');
+ expect(lis.get(2).getText()).toBe('Magneta');
+ expect(lis.get(3).getText()).toBe('Tornado');
+ });
+
+ it('04-10', function () {
+ browser.get('#/04-10');
+
+ let div = element(by.tagName('sg-app > toh-heroes > div'));
+ expect(div.getText()).toBe('This is heroes component');
+ });
+
+ it('05-02', function () {
+ browser.get('#/05-02');
+
+ let button = element(by.tagName('sg-app > toh-hero-button > button'));
+ expect(button.getText()).toBe('Hero button');
+ });
+
+ it('05-03', function () {
+ browser.get('#/05-03');
+
+ let button = element(by.tagName('sg-app > toh-hero-button > button'));
+ expect(button.getText()).toBe('Hero button');
+ });
+
+ it('05-04', function () {
+ browser.get('#/05-04');
+
+ let h2 = element(by.tagName('sg-app > toh-heroes > div > h2'));
+ expect(h2.getText()).toBe('My Heroes');
+ });
+
+ it('05-12', function () {
+ browser.get('#/05-12');
+
+ let button = element(by.tagName('sg-app > toh-hero-button > button'));
+ expect(button.getText()).toBe('OK');
+ });
+
+ it('05-13', function () {
+ browser.get('#/05-13');
+
+ let button = element(by.tagName('sg-app > toh-hero-button > button'));
+ expect(button.getText()).toBe('OK');
+ });
+
+ it('05-14', function () {
+ browser.get('#/05-14');
+
+ let toast = element(by.tagName('sg-app > toh-toast'));
+ expect(toast.getText()).toBe('...');
+ });
+
+ it('05-15', function () {
+ browser.get('#/05-15');
+
+ let heroList = element(by.tagName('sg-app > toh-hero-list'));
+ expect(heroList.getText()).toBe('...');
+ });
+
+ it('05-16', function () {
+ browser.get('#/05-16');
+
+ let hero = element(by.tagName('sg-app > toh-hero'));
+ expect(hero.getText()).toBe('...');
+ });
+
+ it('05-17', function () {
+ browser.get('#/05-17');
+
+ let section = element(by.tagName('sg-app > toh-hero-list > section'));
+ expect(section.getText()).toContain('Our list of heroes');
+ expect(section.getText()).toContain('Total powers');
+ expect(section.getText()).toContain('Average power');
+ });
+
+ it('06-01', function () {
+ browser.get('#/06-01');
+
+ let div = element(by.tagName('sg-app > div[tohhighlight]'));
+ expect(div.getText()).toBe('Bombasta');
+ });
+
+ it('06-03', function () {
+ browser.get('#/06-03');
+
+ let input = element(by.tagName('input[tohvalidator]'));
+ expect(input.isPresent()).toBe(true);
+ });
+
+ it('07-01', function () {
+ browser.get('#/07-01');
+
+ let lis = element.all(by.tagName('sg-app > ul > li'));
+ expect(lis.get(0).getText()).toBe('Windstorm');
+ expect(lis.get(1).getText()).toBe('Bombasto');
+ expect(lis.get(2).getText()).toBe('Magneta');
+ expect(lis.get(3).getText()).toBe('Tornado');
+ });
+
+ it('07-03', function () {
+ browser.get('#/07-03');
+
+ let pre = element(by.tagName('toh-heroes > pre'));
+ expect(pre.getText()).toContain('[]');
+ });
+
+ it('07-04', function () {
+ browser.get('#/07-04');
+
+ let pre = element(by.tagName('toh-app > pre'));
+ expect(pre.getText()).toContain('[]');
+ });
+
+ it('09-01', function () {
+ browser.get('#/09-01');
+
+ let button = element(by.tagName('sg-app > toh-hero-button > button'));
+ expect(button.getText()).toBe('OK');
+ });
+});
diff --git a/public/docs/_examples/style-guide/ts/.gitignore b/public/docs/_examples/style-guide/ts/.gitignore
new file mode 100644
index 0000000000..bd6423cecb
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/.gitignore
@@ -0,0 +1,2 @@
+*.js
+!systemjs.custom.js
diff --git a/public/docs/_examples/style-guide/ts/example-config.json b/public/docs/_examples/style-guide/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/style-guide/ts/plnkr.json b/public/docs/_examples/style-guide/ts/plnkr.json
new file mode 100644
index 0000000000..bf9e9681c5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/plnkr.json
@@ -0,0 +1,9 @@
+{
+ "description": "Style Guide",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js"
+ ],
+ "tags": ["style guide, styleguide"]
+}
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/app.component.css b/public/docs/_examples/style-guide/ts/src/01-01/app/app.component.css
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/app.component.ts
new file mode 100644
index 0000000000..d5bb953540
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/app.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { HeroService } from './heroes';
+
+@Component({
+ selector: 'toh-app',
+ template: `
+
+ `,
+ styleUrls: ['./app.component.css'],
+ providers: [ HeroService ]
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/app.module.ts
new file mode 100644
index 0000000000..53f29ea8cb
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/app.module.ts
@@ -0,0 +1,27 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes/heroes.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ // #enddocregion
+ RouterModule.forChild([{ path: '01-01', component: AppComponent }])
+ // #docregion
+ ],
+ declarations: [
+ AppComponent,
+ HeroesComponent
+ ],
+ exports: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/hero.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/hero.component.avoid.ts
new file mode 100644
index 0000000000..853e6ab64e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/hero.component.avoid.ts
@@ -0,0 +1,49 @@
+// #docregion
+/* avoid */
+
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { BrowserModule } from '@angular/platform-browser';
+import { NgModule, Component, OnInit } from '@angular/core';
+
+class Hero {
+ id: number;
+ name: string;
+}
+
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+ {{heroes | json}}
+ `,
+ styleUrls: ['app/app.component.css']
+})
+class AppComponent implements OnInit {
+ title = 'Tour of Heroes';
+
+ heroes: Hero[] = [];
+
+ ngOnInit() {
+ getHeroes().then(heroes => this.heroes = heroes);
+ }
+}
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [ AppComponent ],
+ exports: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+
+const HEROES: Hero[] = [
+ {id: 1, name: 'Bombasto'},
+ {id: 2, name: 'Tornado'},
+ {id: 3, name: 'Magneta'},
+];
+
+function getHeroes(): Promise {
+ return Promise.resolve(HEROES); // TODO: get hero data from the server;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..9e3546db90
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/heroes.component.ts
@@ -0,0 +1,21 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { Hero, HeroService } from './shared';
+
+@Component({
+ selector: 'toh-heroes',
+ template: `
+ {{heroes | json}}
+ `
+})
+export class HeroesComponent implements OnInit {
+ heroes: Hero[] = [];
+
+ constructor(private heroService: HeroService) {}
+
+ ngOnInit() {
+ this.heroService.getHeroes()
+ .then(heroes => this.heroes = heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/index.ts
new file mode 100644
index 0000000000..a8d7f1d422
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/index.ts
@@ -0,0 +1,2 @@
+export * from './shared';
+export * from './heroes.component';
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..d94e5bacac
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/hero.service.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { HEROES } from './mock-heroes';
+
+@Injectable()
+export class HeroService {
+ getHeroes() {
+ return Promise.resolve(HEROES);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..c0c1a87eb2
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/index.ts
@@ -0,0 +1,3 @@
+export * from './hero.model';
+export * from './hero.service';
+export * from './mock-heroes';
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/mock-heroes.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/mock-heroes.ts
new file mode 100644
index 0000000000..2e9a69f59d
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/heroes/shared/mock-heroes.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Hero } from './hero.model';
+
+export const HEROES: Hero[] = [
+ {id: 1, name: 'Bombasto'},
+ {id: 2, name: 'Tornado'},
+ {id: 3, name: 'Magneta'},
+];
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/app/index.ts b/public/docs/_examples/style-guide/ts/src/01-01/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/01-01/main.ts b/public/docs/_examples/style-guide/ts/src/01-01/main.ts
new file mode 100644
index 0000000000..7e8269bd65
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/01-01/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/style-guide/ts/src/02-05/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/02-05/app/app.component.ts
new file mode 100644
index 0000000000..66ebfa954e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-05/app/app.component.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'toh-app',
+ template: `
+ Tour of Heroes
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/02-05/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/02-05/app/app.module.ts
new file mode 100644
index 0000000000..306ec125e0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-05/app/app.module.ts
@@ -0,0 +1,23 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ // #enddocregion
+ RouterModule.forChild([{ path: '02-05', component: AppComponent }])
+ // #docregion
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
+// #enddocregion
diff --git a/public/docs/_examples/style-guide/ts/src/02-05/main.ts b/public/docs/_examples/style-guide/ts/src/02-05/main.ts
new file mode 100644
index 0000000000..6c32161f84
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-05/main.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+ .then(success => console.log(`Bootstrap success`))
+ .catch(err => console.error(err));
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/app.component.ts
new file mode 100644
index 0000000000..c82e12624d
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/app.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: `
+
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/app.module.ts
new file mode 100644
index 0000000000..4320fe67d5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/app.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroComponent } from './heroes';
+import { UsersComponent } from './users';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '02-07', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroComponent,
+ UsersComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/hero.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/hero.component.avoid.ts
new file mode 100644
index 0000000000..976fcb6cdf
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/hero.component.avoid.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Component } from '@angular/core';
+// #docregion example
+/* avoid */
+
+// HeroComponent is in the Tour of Heroes feature
+@Component({
+ selector: 'hero'
+})
+export class HeroComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/hero.component.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/hero.component.ts
new file mode 100644
index 0000000000..44c04dd855
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/hero.component.ts
@@ -0,0 +1,13 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+// #docregion example
+@Component({
+ // #enddocregion example
+ template: 'hero component
',
+ // #docregion example
+ selector: 'toh-hero'
+})
+export class HeroComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/index.ts
new file mode 100644
index 0000000000..084f36d703
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './hero.component';
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/index.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/index.ts
new file mode 100644
index 0000000000..fc87f976b3
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/index.ts
@@ -0,0 +1,3 @@
+export * from './heroes';
+export * from './users';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/users/index.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/users/index.ts
new file mode 100644
index 0000000000..475ba6d2a7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/users/index.ts
@@ -0,0 +1 @@
+export * from './users.component';
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/users/users.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/users/users.component.avoid.ts
new file mode 100644
index 0000000000..9a5bd936e9
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/users/users.component.avoid.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Component } from '@angular/core';
+// #docregion example
+/* avoid */
+
+// UsersComponent is in an Admin feature
+@Component({
+ selector: 'users'
+})
+export class UsersComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/02-07/app/users/users.component.ts b/public/docs/_examples/style-guide/ts/src/02-07/app/users/users.component.ts
new file mode 100644
index 0000000000..2fb6d54ebe
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-07/app/users/users.component.ts
@@ -0,0 +1,13 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+// #docregion example
+@Component({
+ // #enddocregion example
+ template: 'users component
',
+ // #docregion example
+ selector: 'admin-users'
+})
+export class UsersComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/app.component.ts
new file mode 100644
index 0000000000..bf27aeaf8a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/app.module.ts
new file mode 100644
index 0000000000..e840cc50a5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/app.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { InputHighlightDirective,
+ ValidateDirective } from './shared';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '02-08', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ InputHighlightDirective,
+ ValidateDirective
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/index.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/index.ts
new file mode 100644
index 0000000000..ebe5c92f03
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/index.ts
@@ -0,0 +1,2 @@
+export * from './shared';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/shared/index.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/index.ts
new file mode 100644
index 0000000000..b844b75492
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/index.ts
@@ -0,0 +1,2 @@
+export * from './input-highlight.directive';
+export * from './validate.directive';
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/shared/input-highlight.directive.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/input-highlight.directive.ts
new file mode 100644
index 0000000000..fd74771981
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/input-highlight.directive.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Directive, ElementRef } from '@angular/core';
+
+@Directive({ selector: 'input'})
+/** Highlight the attached input text element in blue */
+export class InputHighlightDirective {
+ constructor(el: ElementRef) {
+ el.nativeElement.style.backgroundColor = 'powderblue';
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/shared/validate.directive.avoid.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/validate.directive.avoid.ts
new file mode 100644
index 0000000000..b038f37075
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/validate.directive.avoid.ts
@@ -0,0 +1,10 @@
+// #docregion
+import { Directive } from '@angular/core';
+// #docregion example
+/* avoid */
+
+@Directive({
+ selector: '[validate]'
+})
+export class ValidateDirective {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/02-08/app/shared/validate.directive.ts b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/validate.directive.ts
new file mode 100644
index 0000000000..039afb846f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/02-08/app/shared/validate.directive.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Directive } from '@angular/core';
+
+// #docregion example
+@Directive({
+ selector: '[tohValidate]'
+})
+export class ValidateDirective {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/03-01/app/app.component.ts
new file mode 100644
index 0000000000..cb9479d2d9
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-01/app/app.component.ts
@@ -0,0 +1,18 @@
+import { Component, OnInit } from '@angular/core';
+
+import { ExceptionService } from './core';
+
+@Component({
+ selector: 'sg-app',
+ template: 'The expected error is {{errorCode}}
',
+ providers: [ExceptionService]
+})
+export class AppComponent implements OnInit {
+ errorCode: number;
+
+ constructor(private exceptionService: ExceptionService) { }
+
+ ngOnInit() {
+ this.errorCode = this.exceptionService.getException();
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/03-01/app/app.module.ts
new file mode 100644
index 0000000000..48079f21c7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-01/app/app.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '03-01', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/03-01/app/core/exception.service.avoid.ts b/public/docs/_examples/style-guide/ts/src/03-01/app/core/exception.service.avoid.ts
new file mode 100644
index 0000000000..0a22811fe3
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-01/app/core/exception.service.avoid.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+// #docregion example
+/* avoid */
+
+export class exceptionService {
+ constructor() { }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-01/app/core/exception.service.ts b/public/docs/_examples/style-guide/ts/src/03-01/app/core/exception.service.ts
new file mode 100644
index 0000000000..dd77b4f7dc
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-01/app/core/exception.service.ts
@@ -0,0 +1,14 @@
+// #docplaster
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+// #docregion example
+export class ExceptionService {
+ constructor() { }
+ // #enddocregion example
+ // testing harness
+ getException() { return 42; }
+ // #docregion example
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-01/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/03-01/app/core/index.ts
new file mode 100644
index 0000000000..8acaa4bcf9
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-01/app/core/index.ts
@@ -0,0 +1 @@
+export * from './exception.service';
diff --git a/public/docs/_examples/style-guide/ts/src/03-01/app/index.ts b/public/docs/_examples/style-guide/ts/src/03-01/app/index.ts
new file mode 100644
index 0000000000..e120e2dbfd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-01/app/index.ts
@@ -0,0 +1,2 @@
+export * from './core';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/03-02/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/03-02/app/app.component.ts
new file mode 100644
index 0000000000..132ea54c85
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-02/app/app.component.ts
@@ -0,0 +1,19 @@
+import { Component } from '@angular/core';
+
+import { heroesUrl, mockHeroes, VILLAINS_URL } from './core';
+
+@Component({
+ selector: 'sg-app',
+ template: `
+ Heroes url: {{heroesUrl}}
+ Villains url: {{villainsUrl}}
+
+ Mock Heroes
+ {{hero}}
+ `
+})
+export class AppComponent {
+ heroes = mockHeroes; // prefer
+ heroesUrl = heroesUrl; // prefer
+ villainsUrl = VILLAINS_URL; // tolerate
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-02/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/03-02/app/app.module.ts
new file mode 100644
index 0000000000..2db4012ebf
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-02/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '03-02', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/03-02/app/core/data.service.ts b/public/docs/_examples/style-guide/ts/src/03-02/app/core/data.service.ts
new file mode 100644
index 0000000000..5c26478c7b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-02/app/core/data.service.ts
@@ -0,0 +1,4 @@
+// #docregion
+export const mockHeroes = ['Sam', 'Jill']; // prefer
+export const heroesUrl = 'api/heroes'; // prefer
+export const VILLAINS_URL = 'api/villains'; // tolerate
diff --git a/public/docs/_examples/style-guide/ts/src/03-02/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/03-02/app/core/index.ts
new file mode 100644
index 0000000000..2ba773ede8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-02/app/core/index.ts
@@ -0,0 +1 @@
+export * from './data.service';
diff --git a/public/docs/_examples/style-guide/ts/src/03-02/app/index.ts b/public/docs/_examples/style-guide/ts/src/03-02/app/index.ts
new file mode 100644
index 0000000000..e120e2dbfd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-02/app/index.ts
@@ -0,0 +1,2 @@
+export * from './core';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/app.component.ts
new file mode 100644
index 0000000000..3ca522bc45
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/app.component.ts
@@ -0,0 +1,18 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Hero, HeroCollectorService } from './core';
+
+@Component({
+ selector: 'sg-app',
+ template: 'Our hero is {{hero.name}} and {{hero.power}}
',
+ providers: [HeroCollectorService]
+})
+export class AppComponent implements OnInit {
+ hero: Hero;
+
+ constructor(private heroCollectorService: HeroCollectorService) { }
+
+ ngOnInit() {
+ this.hero = this.heroCollectorService.getHero();
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/app.module.ts
new file mode 100644
index 0000000000..29b3d2e765
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/app.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '03-03', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero-collector.service.avoid.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero-collector.service.avoid.ts
new file mode 100644
index 0000000000..f481af18b6
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero-collector.service.avoid.ts
@@ -0,0 +1,15 @@
+// #docregion
+// #docregion example
+/* avoid */
+
+import { Injectable } from '@angular/core';
+
+import { IHero } from './hero.model.avoid';
+
+@Injectable()
+export class HeroCollectorService {
+ hero: IHero;
+
+ constructor() { }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero-collector.service.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero-collector.service.ts
new file mode 100644
index 0000000000..1df5c0deb0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero-collector.service.ts
@@ -0,0 +1,25 @@
+// #docplaster
+// #docregion
+// #docregion example
+import { Injectable } from '@angular/core';
+
+import { Hero } from './hero.model';
+
+@Injectable()
+export class HeroCollectorService {
+ hero: Hero;
+
+ constructor() { }
+ // #enddocregion example
+ // testing harness
+ getHero() {
+ this.hero = {
+ name: 'RubberMan',
+ power: 'He is so elastic'
+ };
+
+ return this.hero;
+ }
+ // #docregion example
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero.model.avoid.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero.model.avoid.ts
new file mode 100644
index 0000000000..ce93b2c59a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero.model.avoid.ts
@@ -0,0 +1,14 @@
+// #docregion
+// #docregion example
+/* avoid */
+
+export interface IHero {
+ name: string;
+ power: string;
+}
+
+export class Hero implements IHero {
+ name: string;
+ power: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero.model.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero.model.ts
new file mode 100644
index 0000000000..c3277621cb
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/core/hero.model.ts
@@ -0,0 +1,7 @@
+// #docregion
+// #docregion example
+export class Hero {
+ name: string;
+ power: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/core/index.ts
new file mode 100644
index 0000000000..17ad67b0b1
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/core/index.ts
@@ -0,0 +1,2 @@
+export * from './hero-collector.service';
+export * from './hero.model';
diff --git a/public/docs/_examples/style-guide/ts/src/03-03/app/index.ts b/public/docs/_examples/style-guide/ts/src/03-03/app/index.ts
new file mode 100644
index 0000000000..e120e2dbfd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-03/app/index.ts
@@ -0,0 +1,2 @@
+export * from './core';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/03-04/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/03-04/app/app.component.ts
new file mode 100644
index 0000000000..555c9e9441
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-04/app/app.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnInit } from '@angular/core';
+
+import { ToastService } from './core';
+
+@Component({
+ selector: 'sg-app',
+ template: `
+ Show toast
+ Hide toast
+ `,
+ providers: [ToastService]
+})
+export class AppComponent implements OnInit {
+ constructor(private toastService: ToastService) { }
+
+ hide() {
+ this.toastService.hide();
+ }
+
+ show() {
+ this.toastService.show();
+ }
+
+ ngOnInit() {
+ this.toastService.activate('Hello style-guide!');
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-04/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/03-04/app/app.module.ts
new file mode 100644
index 0000000000..a5a8d5bb4e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-04/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '03-04', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/03-04/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/03-04/app/core/index.ts
new file mode 100644
index 0000000000..e78b628f9c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-04/app/core/index.ts
@@ -0,0 +1 @@
+export * from './toast.service';
diff --git a/public/docs/_examples/style-guide/ts/src/03-04/app/core/toast.service.avoid.ts b/public/docs/_examples/style-guide/ts/src/03-04/app/core/toast.service.avoid.ts
new file mode 100644
index 0000000000..0f3a7c25ea
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-04/app/core/toast.service.avoid.ts
@@ -0,0 +1,27 @@
+// #docregion
+// #docregion example
+/* avoid */
+
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ToastService {
+ message: string;
+
+ private _toastCount: number;
+
+ hide() {
+ this._toastCount--;
+ this._log();
+ }
+
+ show() {
+ this._toastCount++;
+ this._log();
+ }
+
+ private _log() {
+ console.log(this.message);
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-04/app/core/toast.service.ts b/public/docs/_examples/style-guide/ts/src/03-04/app/core/toast.service.ts
new file mode 100644
index 0000000000..ab148a1732
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-04/app/core/toast.service.ts
@@ -0,0 +1,32 @@
+// #docplaster
+// #docregion
+// #docregion example
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ToastService {
+ message: string;
+
+ private toastCount: number;
+
+ hide() {
+ this.toastCount--;
+ this.log();
+ }
+
+ show() {
+ this.toastCount++;
+ this.log();
+ }
+
+ private log() {
+ console.log(this.message);
+ }
+ // #enddocregion example
+ // testing harness
+ activate(message: string) {
+ this.message = message;
+ }
+ // #docregion example
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-04/app/index.ts b/public/docs/_examples/style-guide/ts/src/03-04/app/index.ts
new file mode 100644
index 0000000000..e120e2dbfd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-04/app/index.ts
@@ -0,0 +1,2 @@
+export * from './core';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/app.component.html b/public/docs/_examples/style-guide/ts/src/03-06/app/app.component.html
new file mode 100644
index 0000000000..67fb0d5964
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/app.component.html
@@ -0,0 +1,6 @@
+Actual favorite: {{favorite?.name}}
+
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/app.component.ts
new file mode 100644
index 0000000000..8ec308bc6a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/app.component.ts
@@ -0,0 +1,21 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Hero, HeroService } from './heroes';
+import { ExceptionService, SpinnerService, ToastService } from './core';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html',
+ providers: [HeroService, ExceptionService, SpinnerService, ToastService]
+})
+export class AppComponent implements OnInit {
+ favorite: Hero;
+ heroes: Hero[];
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroService.getHero(1).subscribe(hero => this.favorite = hero);
+ this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/app.module.ts
new file mode 100644
index 0000000000..f259ce23a2
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '03-06', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/exception.service.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/exception.service.ts
new file mode 100644
index 0000000000..7180c88e6b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/exception.service.ts
@@ -0,0 +1,4 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ExceptionService { }
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/index.ts
new file mode 100644
index 0000000000..e4e6723f91
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/index.ts
@@ -0,0 +1,6 @@
+// #docregion
+// #docregion example
+export * from './exception.service';
+export * from './spinner';
+export * from './toast';
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/index.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/index.ts
new file mode 100644
index 0000000000..1d619300c0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/index.ts
@@ -0,0 +1,3 @@
+// #docregion
+export * from './spinner.component';
+export * from './spinner.service';
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/spinner.component.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/spinner.component.ts
new file mode 100644
index 0000000000..1fd2a01500
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/spinner.component.ts
@@ -0,0 +1,16 @@
+import { Component, OnDestroy, OnInit } from '@angular/core';
+
+import { SpinnerService } from './spinner.service';
+
+@Component({
+ selector: 'toh-spinner',
+ template: 'spinner
'
+})
+
+export class SpinnerComponent implements OnDestroy, OnInit {
+ constructor(private spinnerService: SpinnerService) { }
+
+ ngOnInit() { }
+
+ ngOnDestroy() { }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/spinner.service.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/spinner.service.ts
new file mode 100644
index 0000000000..ad5d2ed6e0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/spinner/spinner.service.ts
@@ -0,0 +1,12 @@
+import { Injectable } from '@angular/core';
+
+export interface ISpinnerState { }
+
+@Injectable()
+export class SpinnerService {
+ spinnerState: any;
+
+ show() { }
+
+ hide() { }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/index.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/index.ts
new file mode 100644
index 0000000000..01b41aff98
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/index.ts
@@ -0,0 +1,3 @@
+// #docregion
+export * from './toast.component';
+export * from './toast.service';
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/toast.component.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/toast.component.ts
new file mode 100644
index 0000000000..dd0bba5eba
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/toast.component.ts
@@ -0,0 +1,13 @@
+import { Component, OnInit } from '@angular/core';
+
+import { ToastService } from './toast.service';
+
+@Component({
+ selector: 'toh-toast',
+ template: 'toast
'
+})
+export class ToastComponent implements OnInit {
+ constructor(toastService: ToastService) { }
+
+ ngOnInit() { }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/toast.service.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/toast.service.ts
new file mode 100644
index 0000000000..e92e75ee45
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/core/toast/toast.service.ts
@@ -0,0 +1,6 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class ToastService {
+ activate: (message?: string, title?: string) => void;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..c3277621cb
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.model.ts
@@ -0,0 +1,7 @@
+// #docregion
+// #docregion example
+export class Hero {
+ name: string;
+ power: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.service.avoid.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.service.avoid.ts
new file mode 100644
index 0000000000..8287c567a7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.service.avoid.ts
@@ -0,0 +1,32 @@
+// #docregion
+// #docregion example
+/* avoid */
+
+import { ExceptionService, SpinnerService, ToastService } from '../../core';
+import { Http } from '@angular/http';
+import { Injectable } from '@angular/core';
+import { Hero } from './hero.model';
+// #enddocregion example
+
+@Injectable()
+export class HeroService {
+
+ constructor(
+ private exceptionService: ExceptionService,
+ private spinnerService: SpinnerService,
+ private toastService: ToastService,
+ private http: Http
+ ) { }
+
+ getHero(id: number) {
+ return this.http.get(`api/heroes/${id}`)
+ .map(response => response.json().data as Hero);
+ }
+
+ getHeroes() {
+ return this.http.get(`api/heroes`)
+ .map(response => response.json().data as Hero[]);
+ }
+
+}
+
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..5792cd1ab2
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/hero.service.ts
@@ -0,0 +1,32 @@
+// #docregion
+// #docregion example
+import { Injectable } from '@angular/core';
+import { Http } from '@angular/http';
+
+import { Hero } from './hero.model';
+import { ExceptionService, SpinnerService, ToastService } from '../../core';
+// #enddocregion example
+
+@Injectable()
+export class HeroService {
+ cachedHeroes: Hero[];
+
+ constructor(
+ private exceptionService: ExceptionService,
+ private spinnerService: SpinnerService,
+ private toastService: ToastService,
+ private http: Http
+ ) { }
+
+ getHero(id: number) {
+ return this.http.get(`api/heroes/${id}`)
+ .map(response => response.json().data as Hero);
+ }
+
+ getHeroes() {
+ return this.http.get(`api/heroes`)
+ .map(response => response.json().data as Hero[]);
+ }
+
+}
+
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..dbb150d3f8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/heroes/shared/index.ts
@@ -0,0 +1,2 @@
+export * from './hero.model';
+export * from './hero.service';
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/index.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/index.ts
new file mode 100644
index 0000000000..cf861e261a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/index.ts
@@ -0,0 +1,3 @@
+export * from './heroes';
+export * from './core';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/03-06/app/shared/toast/toast.component.ts b/public/docs/_examples/style-guide/ts/src/03-06/app/shared/toast/toast.component.ts
new file mode 100644
index 0000000000..e1c1ae6665
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/03-06/app/shared/toast/toast.component.ts
@@ -0,0 +1,13 @@
+import { Component, OnInit } from '@angular/core';
+
+import { ToastService } from '../../core';
+
+@Component({
+ selector: 'toh-toast',
+ template: 'toast
'
+})
+export class ToastComponent implements OnInit {
+ constructor(toastService: ToastService) { }
+
+ ngOnInit() { }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-08/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/04-08/app/app.component.ts
new file mode 100644
index 0000000000..fdd8e6ef56
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-08/app/app.component.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/04-08/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/04-08/app/app.module.ts
new file mode 100644
index 0000000000..25568b7fb4
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-08/app/app.module.ts
@@ -0,0 +1,28 @@
+// #docplaster
+// #docregion
+// #docregion example
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+// #enddocregion example
+import { RouterModule } from '@angular/router';
+// #docregion example
+
+import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes/heroes.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+// #enddocregion example
+ RouterModule.forChild([{ path: '04-08', component: AppComponent }])
+// #docregion example
+ ],
+ declarations: [
+ AppComponent,
+ HeroesComponent
+ ],
+ exports: [ AppComponent ],
+ entryComponents: [ AppComponent ]
+})
+export class AppModule {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/04-08/app/heroes/heroes.component.html b/public/docs/_examples/style-guide/ts/src/04-08/app/heroes/heroes.component.html
new file mode 100644
index 0000000000..1244e68a4a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-08/app/heroes/heroes.component.html
@@ -0,0 +1 @@
+This is heroes component
diff --git a/public/docs/_examples/style-guide/ts/src/04-08/app/heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/src/04-08/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..e2df4c91fd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-08/app/heroes/heroes.component.ts
@@ -0,0 +1,11 @@
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'toh-heroes',
+ templateUrl: './heroes.component.html'
+})
+export class HeroesComponent implements OnInit {
+ constructor() { /* ... */ }
+
+ ngOnInit() { /* ... */ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/app.component.ts
new file mode 100644
index 0000000000..fdd8e6ef56
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/app.component.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/app.module.ts
new file mode 100644
index 0000000000..78106657ad
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/app.module.ts
@@ -0,0 +1,30 @@
+// #docplaster
+// #docregion
+// #docregion example
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+// #enddocregion example
+import { RouterModule } from '@angular/router';
+// #docregion example
+
+import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes/heroes.component';
+import { SharedModule } from './shared/shared.module';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ SharedModule,
+// #enddocregion example
+ RouterModule.forChild([{ path: '04-10', component: AppComponent }])
+// #docregion example
+ ],
+ declarations: [
+ AppComponent,
+ HeroesComponent
+ ],
+ exports: [ AppComponent ],
+ entryComponents: [ AppComponent ]
+})
+export class AppModule {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/heroes/heroes.component.html b/public/docs/_examples/style-guide/ts/src/04-10/app/heroes/heroes.component.html
new file mode 100644
index 0000000000..170f76aee1
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/heroes/heroes.component.html
@@ -0,0 +1,8 @@
+
+This is heroes component
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..376567650e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/heroes/heroes.component.ts
@@ -0,0 +1,27 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { FilterTextService } from '../shared/filter-text/filter-text.service';
+
+@Component({
+ selector: 'toh-heroes',
+ templateUrl: './heroes.component.html'
+})
+export class HeroesComponent {
+
+ heroes = [
+ { id: 1, name: 'Windstorm' },
+ { id: 2, name: 'Bombasto' },
+ { id: 3, name: 'Magneta' },
+ { id: 4, name: 'Tornado' }
+ ];
+
+ filteredHeroes = this.heroes;
+
+ constructor(private filterService: FilterTextService) { }
+
+ filterChanged(searchText: string) {
+ this.filteredHeroes = this.filterService.filter(searchText, ['id', 'name'], this.heroes);
+ }
+}
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/shared/filter-text/filter-text.component.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/filter-text/filter-text.component.ts
new file mode 100644
index 0000000000..fffe4bf0b7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/filter-text/filter-text.component.ts
@@ -0,0 +1,26 @@
+// #docregion
+import { Component, EventEmitter, Output } from '@angular/core';
+
+@Component({
+ selector: 'toh-filter-text',
+ template: ' '
+})
+export class FilterTextComponent {
+ @Output() changed: EventEmitter;
+
+ filter: string;
+
+ constructor() {
+ this.changed = new EventEmitter();
+ }
+
+ clear() {
+ this.filter = '';
+ }
+
+ filterChanged(event: any) {
+ event.preventDefault();
+ console.log(`Filter Changed: ${this.filter}`);
+ this.changed.emit(this.filter);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/shared/filter-text/filter-text.service.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/filter-text/filter-text.service.ts
new file mode 100644
index 0000000000..87978e10e5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/filter-text/filter-text.service.ts
@@ -0,0 +1,30 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class FilterTextService {
+ constructor() {
+ console.log('Created an instance of FilterTextService');
+ }
+
+ filter(data: string, props: Array, originalList: Array) {
+ let filteredList: any[];
+ if (data && props && originalList) {
+ data = data.toLowerCase();
+ let filtered = originalList.filter(item => {
+ let match = false;
+ for (let prop of props) {
+ if (item[prop].toString().toLowerCase().indexOf(data) > -1) {
+ match = true;
+ break;
+ }
+ };
+ return match;
+ });
+ filteredList = filtered;
+ } else {
+ filteredList = originalList;
+ }
+ return filteredList;
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/shared/init-caps.pipe.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/init-caps.pipe.ts
new file mode 100644
index 0000000000..5019bcb234
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/init-caps.pipe.ts
@@ -0,0 +1,7 @@
+// #docregion
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({ name: 'initCaps' })
+export class InitCapsPipe implements PipeTransform {
+ transform = (value: string) => value;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-10/app/shared/shared.module.ts b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/shared.module.ts
new file mode 100644
index 0000000000..6160abcc84
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-10/app/shared/shared.module.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { FilterTextComponent } from './filter-text/filter-text.component';
+import { FilterTextService } from './filter-text/filter-text.service';
+import { InitCapsPipe } from './init-caps.pipe';
+
+@NgModule({
+ imports: [CommonModule, FormsModule],
+ declarations: [
+ FilterTextComponent,
+ InitCapsPipe
+ ],
+ providers: [FilterTextService],
+ exports: [
+ CommonModule,
+ FormsModule,
+ FilterTextComponent,
+ InitCapsPipe
+ ]
+})
+export class SharedModule { }
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/app.component.ts
new file mode 100644
index 0000000000..693619a982
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/app.component.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'toh-app',
+ template: `
+
+
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/app.module.ts
new file mode 100644
index 0000000000..ef3c156bcf
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/app.module.ts
@@ -0,0 +1,30 @@
+// #docplaster
+// #docregion
+// #docregion example
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+// #enddocregion example
+import { RouterModule } from '@angular/router';
+// #docregion example
+
+import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes/heroes.component';
+import { CoreModule } from './core/core.module';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ CoreModule,
+// #enddocregion example
+ RouterModule.forChild([{ path: '04-11', component: AppComponent }])
+// #docregion example
+ ],
+ declarations: [
+ AppComponent,
+ HeroesComponent
+ ],
+ exports: [ AppComponent ],
+ entryComponents: [ AppComponent ]
+})
+export class AppModule {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/core.module.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/core/core.module.ts
new file mode 100644
index 0000000000..4ba5c79a14
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/core.module.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { LoggerService } from './logger.service';
+import { NavComponent } from './nav/nav.component';
+import { SpinnerComponent } from './spinner/spinner.component';
+import { SpinnerService } from './spinner/spinner.service';
+
+@NgModule({
+ imports: [
+ CommonModule // we use ngFor
+ ],
+ exports: [NavComponent, SpinnerComponent],
+ declarations: [NavComponent, SpinnerComponent],
+ providers: [LoggerService, SpinnerService]
+})
+export class CoreModule { }
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/core/index.ts
new file mode 100644
index 0000000000..098f40c7d5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/index.ts
@@ -0,0 +1,4 @@
+// #docregion
+export * from './logger.service';
+export * from './spinner/spinner.service';
+export * from './nav/nav.component';
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/logger.service.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/core/logger.service.ts
new file mode 100644
index 0000000000..9c7080f07a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/logger.service.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class LoggerService {
+ log(msg: string) {
+ console.log(msg);
+ }
+
+ error(msg: string) {
+ console.error(msg);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.css b/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.css
new file mode 100644
index 0000000000..c7903fd25c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.css
@@ -0,0 +1,63 @@
+/*#docregion*/
+.mdl-layout__header {
+ display: flex;
+ position: fixed;
+ background-color: #222;
+}
+
+.nav-link {
+ padding: 0 1em;
+ width: 100px;
+ color: rgba(255,255,255,.6);
+ text-align: center;
+ text-decoration: none;
+}
+
+.nav-link.router-link-active {
+ color: rgba(255,255,255, 1);
+}
+
+.nav-link.router-link-active::after {
+ height: 3px;
+ width: 100%;
+ display: block;
+ content: " ";
+ bottom: 0;
+ left: 0;
+ position: inherit;
+ background: rgb(83,109,254);
+}
+
+.md-title-icon > i {
+ background-image: url("assets/ng.png");
+ background-repeat: no-repeat;
+ background-position: center center;
+ padding: 1em 2em;
+}
+
+.mdl-layout__header-row {
+ height: 56px;
+ padding: 0 16px 0 72px;
+ padding-left: 8px;
+ background-color: #673AB7;
+ background: #0033FF;
+ background-color: #222;
+}
+
+#reset-button {
+ position: fixed;
+ right: 2em;
+ top: 1em;
+}
+
+@media (max-width: 480px) {
+ #reset-button {
+ display: none
+ }
+}
+
+@media (max-width: 320px) {
+ a.nav-link {
+ font-size: 12px;
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.html b/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.html
new file mode 100644
index 0000000000..b3c7e441e0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.html
@@ -0,0 +1,14 @@
+
+
+
+
Tour of Heroes
+
+
+
+
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.ts
new file mode 100644
index 0000000000..7e21016e2b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/nav/nav.component.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'toh-nav',
+ templateUrl: './nav.component.html',
+ styleUrls: ['./nav.component.css'],
+})
+export class NavComponent implements OnInit {
+ menuItems = [
+ 'Heroes',
+ 'Villains',
+ 'Other'
+ ];
+
+ ngOnInit() { }
+
+ constructor() { }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.css b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.css
new file mode 100644
index 0000000000..afad0fe8e3
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.css
@@ -0,0 +1,21 @@
+/*#docregion*/
+.spinner {
+ position: absolute;
+ left: 7em;
+ top: 20em;
+ position: absolute;
+ background-color: blue;
+ height: .3em;
+ width: 6em;
+ margin:-60px 0 0 -60px;
+ -webkit-animation:spin 4s linear infinite;
+ -moz-animation:spin 4s linear infinite;
+ animation:spin 4s linear infinite;
+}
+@-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } }
+@-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } }
+@keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } }
+
+.spinner-hidden {
+ display:none;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.html b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.html
new file mode 100644
index 0000000000..a07e3378c0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.ts
new file mode 100644
index 0000000000..de24167a2b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.component.ts
@@ -0,0 +1,35 @@
+// #docregion
+import { Component, OnDestroy, OnInit } from '@angular/core';
+import { Subscription } from 'rxjs/Subscription';
+
+import { LoggerService } from '../logger.service';
+import { SpinnerState, SpinnerService } from './spinner.service';
+
+@Component({
+ selector: 'toh-spinner',
+ templateUrl: './spinner.component.html',
+ styleUrls: ['./spinner.component.css']
+})
+export class SpinnerComponent implements OnDestroy, OnInit {
+ visible = false;
+
+ private spinnerStateChanged: Subscription;
+
+ constructor(
+ private loggerService: LoggerService,
+ private spinnerService: SpinnerService
+ ) { }
+
+ ngOnInit() {
+ console.log(this.visible);
+ this.spinnerStateChanged = this.spinnerService.spinnerState
+ .subscribe((state: SpinnerState) => {
+ this.visible = state.show;
+ this.loggerService.log(`visible=${this.visible}`);
+ });
+ }
+
+ ngOnDestroy() {
+ this.spinnerStateChanged.unsubscribe();
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.service.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.service.ts
new file mode 100644
index 0000000000..85e366c43e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/core/spinner/spinner.service.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { Subject } from 'rxjs/Subject';
+
+export interface SpinnerState {
+ show: boolean;
+}
+
+@Injectable()
+export class SpinnerService {
+ private spinnerSubject = new Subject();
+
+ spinnerState = this.spinnerSubject.asObservable();
+
+ constructor() { }
+
+ show() {
+ this.spinnerSubject.next({ show: true });
+ }
+
+ hide() {
+ this.spinnerSubject.next({ show: false });
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/heroes/heroes.component.html b/public/docs/_examples/style-guide/ts/src/04-11/app/heroes/heroes.component.html
new file mode 100644
index 0000000000..9035a4b8ff
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/heroes/heroes.component.html
@@ -0,0 +1,12 @@
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-11/app/heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/src/04-11/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..856baadd74
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-11/app/heroes/heroes.component.ts
@@ -0,0 +1,32 @@
+import { Component } from '@angular/core';
+
+import { LoggerService } from '../core/logger.service';
+import { SpinnerService } from '../core/spinner/spinner.service';
+
+@Component({
+ selector: 'toh-heroes',
+ templateUrl: './heroes.component.html'
+})
+export class HeroesComponent {
+ heroes: any[];
+
+ constructor(
+ private loggerService: LoggerService,
+ private spinnerService: SpinnerService
+ ) { }
+
+ getHeroes() {
+ this.loggerService.log(`Getting heroes`);
+ this.spinnerService.show();
+ setTimeout(() => {
+ this.heroes = [
+ { id: 1, name: 'Windstorm' },
+ { id: 2, name: 'Bombasto' },
+ { id: 3, name: 'Magneta' },
+ { id: 4, name: 'Tornado' }
+ ];
+ this.loggerService.log(`We have ${HeroesComponent.length} heroes`);
+ this.spinnerService.hide();
+ }, 2000);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/app.component.ts
new file mode 100644
index 0000000000..dc85b06e9f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/app.component.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'toh-app',
+ template: `
+
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/app.module.ts
new file mode 100644
index 0000000000..7c9fb44182
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/app.module.ts
@@ -0,0 +1,30 @@
+// #docplaster
+// #docregion
+// #docregion example
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+// #enddocregion example
+import { RouterModule } from '@angular/router';
+// #docregion example
+
+import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes/heroes.component';
+import { CoreModule } from './core/core.module';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ CoreModule,
+// #enddocregion example
+ RouterModule.forChild([{ path: '04-12', component: AppComponent }])
+// #docregion example
+ ],
+ declarations: [
+ AppComponent,
+ HeroesComponent
+ ],
+ exports: [ AppComponent ],
+ entryComponents: [ AppComponent ]
+})
+export class AppModule {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/core.module.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/core/core.module.ts
new file mode 100644
index 0000000000..069141bf2a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/core.module.ts
@@ -0,0 +1,21 @@
+// #docregion
+import { NgModule, Optional, SkipSelf } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { LoggerService } from './logger.service';
+import { NavComponent } from './nav/nav.component';
+import { throwIfAlreadyLoaded } from './module-import-guard';
+
+@NgModule({
+ imports: [
+ CommonModule // we use ngFor
+ ],
+ exports: [NavComponent],
+ declarations: [NavComponent],
+ providers: [LoggerService]
+})
+export class CoreModule {
+ constructor( @Optional() @SkipSelf() parentModule: CoreModule) {
+ throwIfAlreadyLoaded(parentModule, 'CoreModule');
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/index.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/core/index.ts
new file mode 100644
index 0000000000..8768b77f41
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/index.ts
@@ -0,0 +1,3 @@
+// #docregion
+export * from './logger.service';
+export * from './nav/nav.component';
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/logger.service.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/core/logger.service.ts
new file mode 100644
index 0000000000..9c7080f07a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/logger.service.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class LoggerService {
+ log(msg: string) {
+ console.log(msg);
+ }
+
+ error(msg: string) {
+ console.error(msg);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/module-import-guard.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/core/module-import-guard.ts
new file mode 100644
index 0000000000..5248b15b2e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/module-import-guard.ts
@@ -0,0 +1,6 @@
+// #docregion
+export function throwIfAlreadyLoaded(parentModule: any, moduleName: string) {
+ if (parentModule) {
+ throw new Error(`${moduleName} has already been loaded. Import Core modules in the AppModule only.`);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.css b/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.css
new file mode 100644
index 0000000000..c7903fd25c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.css
@@ -0,0 +1,63 @@
+/*#docregion*/
+.mdl-layout__header {
+ display: flex;
+ position: fixed;
+ background-color: #222;
+}
+
+.nav-link {
+ padding: 0 1em;
+ width: 100px;
+ color: rgba(255,255,255,.6);
+ text-align: center;
+ text-decoration: none;
+}
+
+.nav-link.router-link-active {
+ color: rgba(255,255,255, 1);
+}
+
+.nav-link.router-link-active::after {
+ height: 3px;
+ width: 100%;
+ display: block;
+ content: " ";
+ bottom: 0;
+ left: 0;
+ position: inherit;
+ background: rgb(83,109,254);
+}
+
+.md-title-icon > i {
+ background-image: url("assets/ng.png");
+ background-repeat: no-repeat;
+ background-position: center center;
+ padding: 1em 2em;
+}
+
+.mdl-layout__header-row {
+ height: 56px;
+ padding: 0 16px 0 72px;
+ padding-left: 8px;
+ background-color: #673AB7;
+ background: #0033FF;
+ background-color: #222;
+}
+
+#reset-button {
+ position: fixed;
+ right: 2em;
+ top: 1em;
+}
+
+@media (max-width: 480px) {
+ #reset-button {
+ display: none
+ }
+}
+
+@media (max-width: 320px) {
+ a.nav-link {
+ font-size: 12px;
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.html b/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.html
new file mode 100644
index 0000000000..b3c7e441e0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.html
@@ -0,0 +1,14 @@
+
+
+
+
Tour of Heroes
+
+
+
+
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.ts
new file mode 100644
index 0000000000..7e21016e2b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/core/nav/nav.component.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'toh-nav',
+ templateUrl: './nav.component.html',
+ styleUrls: ['./nav.component.css'],
+})
+export class NavComponent implements OnInit {
+ menuItems = [
+ 'Heroes',
+ 'Villains',
+ 'Other'
+ ];
+
+ ngOnInit() { }
+
+ constructor() { }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/heroes/heroes.component.html b/public/docs/_examples/style-guide/ts/src/04-12/app/heroes/heroes.component.html
new file mode 100644
index 0000000000..9035a4b8ff
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/heroes/heroes.component.html
@@ -0,0 +1,12 @@
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/04-12/app/heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/src/04-12/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..f84b16c140
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/04-12/app/heroes/heroes.component.ts
@@ -0,0 +1,24 @@
+import { Component } from '@angular/core';
+
+import { LoggerService } from '../core/logger.service';
+
+@Component({
+ selector: 'toh-heroes',
+ templateUrl: './heroes.component.html'
+})
+export class HeroesComponent {
+ heroes: any[];
+
+ constructor(private loggerService: LoggerService) { }
+
+ getHeroes() {
+ this.loggerService.log(`Getting heroes`);
+ this.heroes = [
+ { id: 1, name: 'Windstorm' },
+ { id: 2, name: 'Bombasto' },
+ { id: 3, name: 'Magneta' },
+ { id: 4, name: 'Tornado' }
+ ];
+ this.loggerService.log(`We have ${HeroesComponent.length} heroes`);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/app.component.html b/public/docs/_examples/style-guide/ts/src/05-02/app/app.component.html
new file mode 100644
index 0000000000..607d068557
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/app.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/app.component.ts
new file mode 100644
index 0000000000..7c9f37919f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/app.module.ts
new file mode 100644
index 0000000000..1c458e2ca1
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroButtonComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '05-02', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroButtonComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.avoid.ts
new file mode 100644
index 0000000000..e48d41f721
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.avoid.ts
@@ -0,0 +1,11 @@
+// #docplaster
+import { Component } from '@angular/core';
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: 'tohHeroButton',
+ templateUrl: './hero-button.component.html'
+})
+export class HeroButtonComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.html b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.html
new file mode 100644
index 0000000000..9ad67e50ac
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.html
@@ -0,0 +1 @@
+Hero button
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.ts
new file mode 100644
index 0000000000..c90c966f1a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/hero-button.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+// #docregion example
+@Component({
+ selector: 'toh-hero-button',
+ templateUrl: './hero-button.component.html'
+})
+export class HeroButtonComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/index.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/index.ts
new file mode 100644
index 0000000000..6bb67c5670
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/hero-button/index.ts
@@ -0,0 +1 @@
+export * from './hero-button.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..2334d49c9a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/heroes/shared/index.ts
@@ -0,0 +1 @@
+export * from './hero-button';
diff --git a/public/docs/_examples/style-guide/ts/src/05-02/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-02/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-02/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.avoid.html b/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.avoid.html
new file mode 100644
index 0000000000..91bdbe403c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.avoid.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.html b/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.html
new file mode 100644
index 0000000000..607d068557
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.ts
new file mode 100644
index 0000000000..7c9f37919f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/app.module.ts
new file mode 100644
index 0000000000..1b754e3ee5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroButtonComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '05-03', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroButtonComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.avoid.ts
new file mode 100644
index 0000000000..74f5c36339
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.avoid.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: '[tohHeroButton]',
+ templateUrl: './hero-button.component.html'
+})
+export class HeroButtonComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.html b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.html
new file mode 100644
index 0000000000..9ad67e50ac
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.html
@@ -0,0 +1 @@
+Hero button
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.ts
new file mode 100644
index 0000000000..c90c966f1a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/hero-button.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+// #docregion example
+@Component({
+ selector: 'toh-hero-button',
+ templateUrl: './hero-button.component.html'
+})
+export class HeroButtonComponent {}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/index.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/index.ts
new file mode 100644
index 0000000000..6bb67c5670
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/hero-button/index.ts
@@ -0,0 +1 @@
+export * from './hero-button.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..2334d49c9a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/heroes/shared/index.ts
@@ -0,0 +1 @@
+export * from './hero-button';
diff --git a/public/docs/_examples/style-guide/ts/src/05-03/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-03/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-03/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/app.component.ts
new file mode 100644
index 0000000000..0e43893f7f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/app.module.ts
new file mode 100644
index 0000000000..07f97cc6e4
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/app.module.ts
@@ -0,0 +1,21 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroesComponent } from './heroes';
+import { HeroService } from './heroes/shared';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '05-04', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroesComponent
+ ],
+ exports: [ AppComponent ],
+ providers: [ HeroService ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.avoid.ts
new file mode 100644
index 0000000000..0ceb37032d
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.avoid.ts
@@ -0,0 +1,64 @@
+import { Component, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { Hero, HeroService } from './shared';
+
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: 'toh-heroes',
+ template: `
+
+
My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
{{selectedHero.name | uppercase}} is my hero
+
+
+ `,
+ styles: [`
+ .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 .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;
+ }
+ `]
+})
+export class HeroesComponent implements OnInit {
+ heroes: Observable;
+ selectedHero: Hero;
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroes = this.heroService.getHeroes();
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.css b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.css
new file mode 100644
index 0000000000..82f0c1d0ab
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.css
@@ -0,0 +1,28 @@
+/* #docregion */
+.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 .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;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.html b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.html
new file mode 100644
index 0000000000..bab05ceb2b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.html
@@ -0,0 +1,12 @@
+
+
+
My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
{{selectedHero.name | uppercase}} is my hero
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.ts
new file mode 100644
index 0000000000..c19bc997ae
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/heroes.component.ts
@@ -0,0 +1,22 @@
+import { Component, OnInit } from '@angular/core';
+import { Observable } from 'rxjs/Observable';
+
+import { Hero, HeroService } from './shared';
+
+// #docregion example
+@Component({
+ selector: 'toh-heroes',
+ templateUrl: './heroes.component.html',
+ styleUrls: ['./heroes.component.css']
+})
+export class HeroesComponent implements OnInit {
+ heroes: Observable;
+ selectedHero: Hero;
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroes = this.heroService.getHeroes();
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/index.ts
new file mode 100644
index 0000000000..a8d7f1d422
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/index.ts
@@ -0,0 +1,2 @@
+export * from './shared';
+export * from './heroes.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..9d388780a6
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/hero.service.ts
@@ -0,0 +1,18 @@
+import { Injectable } from '@angular/core';
+import { Http } from '@angular/http';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/map';
+
+import { Hero } from './hero.model';
+
+@Injectable()
+export class HeroService {
+
+ constructor(private http: Http) {}
+
+ getHeroes(): Observable {
+ return this.http.get('api/heroes')
+ .map(resp => resp.json().data as Hero[]);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..dbb150d3f8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/heroes/shared/index.ts
@@ -0,0 +1,2 @@
+export * from './hero.model';
+export * from './hero.service';
diff --git a/public/docs/_examples/style-guide/ts/src/05-04/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-04/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-04/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/app.component.ts
new file mode 100644
index 0000000000..dac40205c9
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/app.module.ts
new file mode 100644
index 0000000000..5177b2cc64
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroButtonComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '05-12', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroButtonComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/hero-button.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/hero-button.component.avoid.ts
new file mode 100644
index 0000000000..8f393ddd32
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/hero-button.component.avoid.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component, EventEmitter } from '@angular/core';
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: 'toh-hero-button',
+ template: ` `,
+ inputs: [
+ 'label'
+ ],
+ outputs: [
+ 'change'
+ ]
+})
+export class HeroButtonComponent {
+ change = new EventEmitter();
+ label: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/hero-button.component.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/hero-button.component.ts
new file mode 100644
index 0000000000..b299740765
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/hero-button.component.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+// #docregion example
+@Component({
+ selector: 'toh-hero-button',
+ template: `{{label}} `
+})
+export class HeroButtonComponent {
+ @Output() change = new EventEmitter();
+ @Input() label: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/index.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/index.ts
new file mode 100644
index 0000000000..6bb67c5670
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/hero-button/index.ts
@@ -0,0 +1 @@
+export * from './hero-button.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..2334d49c9a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/heroes/shared/index.ts
@@ -0,0 +1 @@
+export * from './hero-button';
diff --git a/public/docs/_examples/style-guide/ts/src/05-12/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-12/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-12/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.avoid.html b/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.avoid.html
new file mode 100644
index 0000000000..0a263a6a95
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.avoid.html
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.html b/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.html
new file mode 100644
index 0000000000..3cd94ca772
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+The Great Bombasto
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.ts
new file mode 100644
index 0000000000..7c9f37919f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/app.module.ts
new file mode 100644
index 0000000000..7ebe91dbbc
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroButtonComponent, HeroHighlightDirective } from './heroes';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '05-13', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroButtonComponent, HeroHighlightDirective
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/hero-button.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/hero-button.component.avoid.ts
new file mode 100644
index 0000000000..4e67a14113
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/hero-button.component.avoid.ts
@@ -0,0 +1,14 @@
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+// #docregion example
+/* avoid pointless aliasing */
+
+@Component({
+ selector: 'toh-hero-button',
+ template: `{{label}} `
+})
+export class HeroButtonComponent {
+ // Pointless aliases
+ @Output('changeEvent') change = new EventEmitter();
+ @Input('labelAttribute') label: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/hero-button.component.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/hero-button.component.ts
new file mode 100644
index 0000000000..af6e7d46b7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/hero-button.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+// #docregion example
+@Component({
+ selector: 'toh-hero-button',
+ template: `{{label}} `
+})
+export class HeroButtonComponent {
+ // No aliases
+ @Output() change = new EventEmitter();
+ @Input() label: string;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/index.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/index.ts
new file mode 100644
index 0000000000..6bb67c5670
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-button/index.ts
@@ -0,0 +1 @@
+export * from './hero-button.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-highlight.directive.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-highlight.directive.ts
new file mode 100644
index 0000000000..737af31f4f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/hero-highlight.directive.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
+
+@Directive({ selector: '[heroHighlight]' })
+export class HeroHighlightDirective implements OnChanges {
+
+ // Aliased because `color` is a better property name than `heroHighlight`
+ @Input('heroHighlight') color: string;
+
+ constructor(private el: ElementRef) {}
+
+ ngOnChanges() {
+ this.el.nativeElement.style.backgroundColor = this.color || 'yellow';
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..565f46cf4f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/heroes/shared/index.ts
@@ -0,0 +1,2 @@
+export * from './hero-button';
+export * from './hero-highlight.directive';
diff --git a/public/docs/_examples/style-guide/ts/src/05-13/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-13/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-13/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/app.component.ts
new file mode 100644
index 0000000000..8ed6da4c82
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ` `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/app.module.ts
new file mode 100644
index 0000000000..0b294573d2
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { ToastComponent } from './shared';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '05-14', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ ToastComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/index.ts
new file mode 100644
index 0000000000..ebe5c92f03
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/index.ts
@@ -0,0 +1,2 @@
+export * from './shared';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/index.ts
new file mode 100644
index 0000000000..7ff6d415e7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/index.ts
@@ -0,0 +1 @@
+export * from './toast';
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/index.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/index.ts
new file mode 100644
index 0000000000..6502de796e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/index.ts
@@ -0,0 +1 @@
+export * from './toast.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/toast.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/toast.component.avoid.ts
new file mode 100644
index 0000000000..037ff2c8b5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/toast.component.avoid.ts
@@ -0,0 +1,40 @@
+// #docregion
+import { OnInit } from '@angular/core';
+// #docregion example
+/* avoid */
+
+export class ToastComponent implements OnInit {
+
+ private defaults = {
+ title: '',
+ message: 'May the Force be with you'
+ };
+ message: string;
+ title: string;
+ private toastElement: any;
+
+ ngOnInit() {
+ this.toastElement = document.getElementById('toh-toast');
+ }
+
+ // private methods
+ private hide() {
+ this.toastElement.style.opacity = 0;
+ window.setTimeout(() => this.toastElement.style.zIndex = 0, 400);
+ }
+
+ activate(message = this.defaults.message, title = this.defaults.title) {
+ this.title = title;
+ this.message = message;
+ this.show();
+ }
+
+ private show() {
+ console.log(this.message);
+ this.toastElement.style.opacity = 1;
+ this.toastElement.style.zIndex = 9999;
+
+ window.setTimeout(() => this.hide(), 2500);
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/toast.component.ts b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/toast.component.ts
new file mode 100644
index 0000000000..d7b8ea4af0
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-14/app/shared/toast/toast.component.ts
@@ -0,0 +1,45 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+@Component({
+ selector: 'toh-toast',
+ template: `...`
+})
+// #docregion example
+export class ToastComponent implements OnInit {
+ // public properties
+ message: string;
+ title: string;
+
+ // private fields
+ private defaults = {
+ title: '',
+ message: 'May the Force be with you'
+ };
+ private toastElement: any;
+
+ // public methods
+ activate(message = this.defaults.message, title = this.defaults.title) {
+ this.title = title;
+ this.message = message;
+ this.show();
+ }
+
+ ngOnInit() {
+ this.toastElement = document.getElementById('toh-toast');
+ }
+
+ // private methods
+ private hide() {
+ this.toastElement.style.opacity = 0;
+ window.setTimeout(() => this.toastElement.style.zIndex = 0, 400);
+ }
+
+ private show() {
+ console.log(this.message);
+ this.toastElement.style.opacity = 1;
+ this.toastElement.style.zIndex = 9999;
+ window.setTimeout(() => this.hide(), 2500);
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/app.component.ts
new file mode 100644
index 0000000000..91b569b1e7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/app.component.ts
@@ -0,0 +1,10 @@
+import { Component } from '@angular/core';
+
+import { HeroService } from './heroes';
+
+@Component({
+ selector: 'sg-app',
+ template: ' ',
+ providers: [HeroService]
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/app.module.ts
new file mode 100644
index 0000000000..9bd4b8c9a2
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/app.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroListComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '05-15', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroListComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/hero-list.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/hero-list.component.avoid.ts
new file mode 100644
index 0000000000..c323ba2b1c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/hero-list.component.avoid.ts
@@ -0,0 +1,39 @@
+// #docregion
+/* avoid */
+
+import { OnInit } from '@angular/core';
+import { Http, Response } from '@angular/http';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/finally';
+import 'rxjs/add/operator/map';
+
+import { Hero } from '../shared/hero.model';
+
+const heroesUrl = 'http://angular.io';
+
+export class HeroListComponent implements OnInit {
+ heroes: Hero[];
+ constructor(private http: Http) {}
+ getHeroes() {
+ this.heroes = [];
+ this.http.get(heroesUrl)
+ .map((response: Response) => response.json().data)
+ .catch(this.catchBadResponse)
+ .finally(() => this.hideSpinner())
+ .subscribe((heroes: Hero[]) => this.heroes = heroes);
+ }
+ ngOnInit() {
+ this.getHeroes();
+ }
+
+ private catchBadResponse(err: any, source: Observable) {
+ // log and handle the exception
+ return new Observable();
+ }
+
+ private hideSpinner() {
+ // hide the spinner
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/hero-list.component.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/hero-list.component.ts
new file mode 100644
index 0000000000..1fdb893c13
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/hero-list.component.ts
@@ -0,0 +1,23 @@
+// #docregion example
+import { Component, OnInit } from '@angular/core';
+
+import { Hero, HeroService } from '../shared';
+
+@Component({
+ selector: 'toh-hero-list',
+ template: `...`
+})
+export class HeroListComponent implements OnInit {
+ heroes: Hero[];
+ constructor(private heroService: HeroService) {}
+ getHeroes() {
+ this.heroes = [];
+ this.heroService.getHeroes()
+ .subscribe(heroes => this.heroes = heroes);
+ }
+ ngOnInit() {
+ this.getHeroes();
+ }
+}
+// #enddocregion example
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/index.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/index.ts
new file mode 100644
index 0000000000..c4bcb3278e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/hero-list/index.ts
@@ -0,0 +1 @@
+export * from './hero-list.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/index.ts
new file mode 100644
index 0000000000..f1112f1c7c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/index.ts
@@ -0,0 +1,2 @@
+export * from './hero-list';
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..72d07bbed4
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/hero.service.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+
+import { Hero } from './hero.model';
+
+@Injectable()
+export class HeroService {
+ getHeroes() {
+ let heroes: Hero[] = [];
+ return Observable.of(heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..27516fdedd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/heroes/shared/index.ts
@@ -0,0 +1,3 @@
+// #docregion
+export * from './hero.model';
+export * from './hero.service';
diff --git a/public/docs/_examples/style-guide/ts/src/05-15/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-15/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-15/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.avoid.html b/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.avoid.html
new file mode 100644
index 0000000000..2c0cea58e2
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.avoid.html
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.html b/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.html
new file mode 100644
index 0000000000..4883a6940a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.html
@@ -0,0 +1,2 @@
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.ts
new file mode 100644
index 0000000000..7c9f37919f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-16/app/app.module.ts
new file mode 100644
index 0000000000..c3fb36f8ac
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/app.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '05-16', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/hero.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/hero.component.avoid.ts
new file mode 100644
index 0000000000..823aa2e1c7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/hero.component.avoid.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Component, EventEmitter, Output } from '@angular/core';
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: 'toh-hero',
+ template: `...`
+})
+export class HeroComponent {
+ @Output() onSavedTheDay = new EventEmitter();
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/hero.component.ts b/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/hero.component.ts
new file mode 100644
index 0000000000..bbd4a4b5f3
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/hero.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Component, EventEmitter, Output } from '@angular/core';
+
+@Component({
+ selector: 'toh-hero',
+ template: `...`
+})
+// #docregion example
+export class HeroComponent {
+ @Output() savedTheDay = new EventEmitter();
+}
+// #enddocregion example
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/index.ts
new file mode 100644
index 0000000000..084f36d703
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './hero.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-16/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-16/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-16/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/app.component.ts
new file mode 100644
index 0000000000..86728b8b80
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/app.module.ts
new file mode 100644
index 0000000000..e850d80ae3
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/app.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroComponent, HeroListComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '05-17', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroComponent,
+ HeroListComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts
new file mode 100644
index 0000000000..f007512949
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/hero-list.component.avoid.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { Hero } from '../shared/hero.model';
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: 'toh-hero-list',
+ template: `
+
+ Our list of heroes:
+
+
+ Total powers: {{totalPowers}}
+ Average power: {{totalPowers / heroes.length}}
+
+ `
+})
+export class HeroListComponent {
+ heroes: Hero[];
+ totalPowers: number;
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/hero-list.component.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/hero-list.component.ts
new file mode 100644
index 0000000000..5f18cc5b0c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/hero-list.component.ts
@@ -0,0 +1,35 @@
+// #docplaster
+// #docregion
+import { Component } from '@angular/core';
+
+import { Hero } from '../shared/hero.model';
+
+// #docregion example
+@Component({
+ selector: 'toh-hero-list',
+ template: `
+
+ Our list of heroes:
+
+
+ Total powers: {{totalPowers}}
+ Average power: {{avgPower}}
+
+ `
+})
+export class HeroListComponent {
+ heroes: Hero[];
+ totalPowers: number;
+
+ // #enddocregion example
+ // testing harness
+ constructor() {
+ this.heroes = [];
+ }
+
+ // #docregion example
+ get avgPower() {
+ return this.totalPowers / this.heroes.length;
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/index.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/index.ts
new file mode 100644
index 0000000000..c4bcb3278e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero-list/index.ts
@@ -0,0 +1 @@
+export * from './hero-list.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero/hero.component.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero/hero.component.ts
new file mode 100644
index 0000000000..334f836a7d
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero/hero.component.ts
@@ -0,0 +1,13 @@
+import { Component, Input } from '@angular/core';
+
+import { Hero } from '../shared/hero.model';
+
+@Component({
+ selector: 'toh-hero',
+ template: `...`
+})
+export class HeroComponent {
+ @Input() hero: Hero;
+}
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero/index.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero/index.ts
new file mode 100644
index 0000000000..084f36d703
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/hero/index.ts
@@ -0,0 +1 @@
+export * from './hero.component';
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/index.ts
new file mode 100644
index 0000000000..dcf3e79bd3
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/index.ts
@@ -0,0 +1,3 @@
+export * from './hero';
+export * from './hero-list';
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..0dceb684c4
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/heroes/shared/index.ts
@@ -0,0 +1 @@
+export * from './hero.model';
diff --git a/public/docs/_examples/style-guide/ts/src/05-17/app/index.ts b/public/docs/_examples/style-guide/ts/src/05-17/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/05-17/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/06-01/app/app.component.html b/public/docs/_examples/style-guide/ts/src/06-01/app/app.component.html
new file mode 100644
index 0000000000..2ccf87d0f5
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-01/app/app.component.html
@@ -0,0 +1,2 @@
+
+Bombasta
diff --git a/public/docs/_examples/style-guide/ts/src/06-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/06-01/app/app.component.ts
new file mode 100644
index 0000000000..7c9f37919f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-01/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/06-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/06-01/app/app.module.ts
new file mode 100644
index 0000000000..318cd306d7
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-01/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HighlightDirective } from './shared';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '06-01', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HighlightDirective
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/06-01/app/index.ts b/public/docs/_examples/style-guide/ts/src/06-01/app/index.ts
new file mode 100644
index 0000000000..ebe5c92f03
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-01/app/index.ts
@@ -0,0 +1,2 @@
+export * from './shared';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/06-01/app/shared/highlight.directive.ts b/public/docs/_examples/style-guide/ts/src/06-01/app/shared/highlight.directive.ts
new file mode 100644
index 0000000000..991a6c5d25
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-01/app/shared/highlight.directive.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Directive, HostListener } from '@angular/core';
+
+// #docregion example
+@Directive({
+ selector: '[tohHighlight]'
+})
+export class HighlightDirective {
+ @HostListener('mouseover') onMouseEnter() {
+ // do highlight work
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/06-01/app/shared/index.ts b/public/docs/_examples/style-guide/ts/src/06-01/app/shared/index.ts
new file mode 100644
index 0000000000..105a035680
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-01/app/shared/index.ts
@@ -0,0 +1 @@
+export * from './highlight.directive';
diff --git a/public/docs/_examples/style-guide/ts/src/06-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/06-03/app/app.component.ts
new file mode 100644
index 0000000000..0d0a7d107b
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-03/app/app.component.ts
@@ -0,0 +1,9 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: `
+
+ `
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/06-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/06-03/app/app.module.ts
new file mode 100644
index 0000000000..b19f3fdc00
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-03/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { ValidatorDirective, Validator2Directive } from './shared';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '06-03', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ ValidatorDirective, Validator2Directive
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/06-03/app/index.ts b/public/docs/_examples/style-guide/ts/src/06-03/app/index.ts
new file mode 100644
index 0000000000..ebe5c92f03
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-03/app/index.ts
@@ -0,0 +1,2 @@
+export * from './shared';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/06-03/app/shared/index.ts b/public/docs/_examples/style-guide/ts/src/06-03/app/shared/index.ts
new file mode 100644
index 0000000000..ba25e4c458
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-03/app/shared/index.ts
@@ -0,0 +1,2 @@
+export * from './validator.directive';
+export * from './validator2.directive';
diff --git a/public/docs/_examples/style-guide/ts/src/06-03/app/shared/validator.directive.ts b/public/docs/_examples/style-guide/ts/src/06-03/app/shared/validator.directive.ts
new file mode 100644
index 0000000000..d9e32c017f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-03/app/shared/validator.directive.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Directive, HostBinding, HostListener } from '@angular/core';
+
+@Directive({
+ selector: '[tohValidator]'
+})
+export class ValidatorDirective {
+ @HostBinding('attr.role') role = 'button';
+ @HostListener('mouseenter') onMouseEnter() {
+ // do work
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/06-03/app/shared/validator2.directive.ts b/public/docs/_examples/style-guide/ts/src/06-03/app/shared/validator2.directive.ts
new file mode 100644
index 0000000000..7936a83cb1
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/06-03/app/shared/validator2.directive.ts
@@ -0,0 +1,16 @@
+// #docregion
+import { Directive } from '@angular/core';
+
+@Directive({
+ selector: '[tohValidator2]',
+ host: {
+ 'attr.role': 'button',
+ '(mouseenter)': 'onMouseEnter()'
+ }
+})
+export class Validator2Directive {
+ role = 'button';
+ onMouseEnter() {
+ // do work
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/app.component.html b/public/docs/_examples/style-guide/ts/src/07-01/app/app.component.html
new file mode 100644
index 0000000000..3c05329f3f
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/app.component.html
@@ -0,0 +1,5 @@
+
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/app.component.ts
new file mode 100644
index 0000000000..354b0ec303
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/app.component.ts
@@ -0,0 +1,18 @@
+import { Component, OnInit } from '@angular/core';
+
+import { Hero, HeroService } from './heroes';
+
+@Component({
+ selector: 'sg-app',
+ templateUrl: './app.component.html',
+ providers: [HeroService]
+})
+export class AppComponent implements OnInit {
+ heroes: Hero[];
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/app.module.ts
new file mode 100644
index 0000000000..0077500dea
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '07-01', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..b5aba5d00c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/hero.service.ts
@@ -0,0 +1,17 @@
+// #docregion
+import { Injectable } from '@angular/core';
+import { Http, Response } from '@angular/http';
+
+import { Hero } from './hero.model';
+
+@Injectable()
+// #docregion example
+export class HeroService {
+ constructor(private http: Http) { }
+
+ getHeroes() {
+ return this.http.get('api/heroes')
+ .map((response: Response) => response.json().data);
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..dbb150d3f8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/heroes/shared/index.ts
@@ -0,0 +1,2 @@
+export * from './hero.model';
+export * from './hero.service';
diff --git a/public/docs/_examples/style-guide/ts/src/07-01/app/index.ts b/public/docs/_examples/style-guide/ts/src/07-01/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-01/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/app.component.ts
new file mode 100644
index 0000000000..f4d25e1ab6
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/app.component.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Component } from '@angular/core';
+
+import { HeroService } from './heroes';
+
+@Component({
+ selector: 'toh-app',
+ template: `
+
+ `,
+ providers: [HeroService]
+})
+export class AppComponent {}
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/app.module.ts
new file mode 100644
index 0000000000..8ba06d22be
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/app.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroListComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '07-03', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroListComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/hero-list/hero-list.component.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/hero-list/hero-list.component.ts
new file mode 100644
index 0000000000..cf9bb19243
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/hero-list/hero-list.component.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { Hero, HeroService } from '../shared';
+
+@Component({
+ selector: 'toh-heroes',
+ template: `
+ {{heroes | json}}
+ `
+})
+export class HeroListComponent implements OnInit {
+ heroes: Hero[] = [];
+
+ constructor(private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroService.getHeroes().subscribe(heroes => this.heroes = heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/hero-list/index.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/hero-list/index.ts
new file mode 100644
index 0000000000..c4bcb3278e
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/hero-list/index.ts
@@ -0,0 +1 @@
+export * from './hero-list.component';
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/index.ts
new file mode 100644
index 0000000000..f1112f1c7c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/index.ts
@@ -0,0 +1,2 @@
+export * from './hero-list';
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..72d07bbed4
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/hero.service.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+
+import { Hero } from './hero.model';
+
+@Injectable()
+export class HeroService {
+ getHeroes() {
+ let heroes: Hero[] = [];
+ return Observable.of(heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..27516fdedd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/heroes/shared/index.ts
@@ -0,0 +1,3 @@
+// #docregion
+export * from './hero.model';
+export * from './hero.service';
diff --git a/public/docs/_examples/style-guide/ts/src/07-03/app/index.ts b/public/docs/_examples/style-guide/ts/src/07-03/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-03/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/app.component.ts
new file mode 100644
index 0000000000..b0bc9677fe
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/app.component.ts
@@ -0,0 +1,19 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { HeroArena, HeroService, Hero } from './heroes';
+
+@Component({
+ selector: 'toh-app',
+ template: '{{heroes | json}} ',
+ providers: [HeroArena, HeroService]
+})
+export class AppComponent implements OnInit {
+ heroes: Hero[] = [];
+
+ constructor(private heroArena: HeroArena) { }
+
+ ngOnInit() {
+ this.heroArena.getParticipants().subscribe(heroes => this.heroes = heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/app.module.ts
new file mode 100644
index 0000000000..71c515c9c9
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ RouterModule.forChild([{ path: '07-04', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero-arena.service.avoid.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero-arena.service.avoid.ts
new file mode 100644
index 0000000000..698fa65239
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero-arena.service.avoid.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Inject } from '@angular/core';
+import { Http } from '@angular/http';
+
+import { HeroService } from './hero.service';
+// #docregion example
+/* avoid */
+
+export class HeroArena {
+ constructor(
+ @Inject(HeroService) private heroService: HeroService,
+ @Inject(Http) private http: Http) {}
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero-arena.service.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero-arena.service.ts
new file mode 100644
index 0000000000..42bc51f9e9
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero-arena.service.ts
@@ -0,0 +1,21 @@
+// #docplaster
+// #docregion
+import { Injectable } from '@angular/core';
+import { Http } from '@angular/http';
+
+import { HeroService } from './index';
+
+// #docregion example
+@Injectable()
+export class HeroArena {
+ constructor(
+ private heroService: HeroService,
+ private http: Http) {}
+ // #enddocregion example
+ // test harness
+ getParticipants() {
+ return this.heroService.getHeroes();
+ }
+ // #docregion example
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero.model.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero.model.ts
new file mode 100644
index 0000000000..8f7cc205c8
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero.model.ts
@@ -0,0 +1,5 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero.service.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero.service.ts
new file mode 100644
index 0000000000..72d07bbed4
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/hero.service.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+
+import { Hero } from './hero.model';
+
+@Injectable()
+export class HeroService {
+ getHeroes() {
+ let heroes: Hero[] = [];
+ return Observable.of(heroes);
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..e8ba54b540
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/heroes/shared/index.ts
@@ -0,0 +1,4 @@
+// #docregion
+export * from './hero.model';
+export * from './hero.service';
+export * from './hero-arena.service';
diff --git a/public/docs/_examples/style-guide/ts/src/07-04/app/index.ts b/public/docs/_examples/style-guide/ts/src/07-04/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/07-04/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/app.component.ts
new file mode 100644
index 0000000000..ebc904f722
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'sg-app',
+ template: ' '
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/app.module.ts
new file mode 100644
index 0000000000..5872e801d6
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/app.module.ts
@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroButtonComponent } from './heroes';
+
+@NgModule({
+ imports: [
+ RouterModule.forChild([{ path: '09-01', component: AppComponent }])
+ ],
+ declarations: [
+ AppComponent,
+ HeroButtonComponent
+ ],
+ exports: [ AppComponent ]
+})
+export class AppModule {}
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/index.ts
new file mode 100644
index 0000000000..c3da79f741
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/index.ts
@@ -0,0 +1 @@
+export * from './shared';
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/hero-button.component.avoid.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/hero-button.component.avoid.ts
new file mode 100644
index 0000000000..7dc42c2f40
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/hero-button.component.avoid.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Component } from '@angular/core';
+// #docregion example
+/* avoid */
+
+@Component({
+ selector: 'toh-hero-button',
+ template: `OK`
+})
+export class HeroButtonComponent {
+ onInit() { // misspelled
+ console.log('The component is initialized');
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/hero-button.component.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/hero-button.component.ts
new file mode 100644
index 0000000000..cc69810dee
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/hero-button.component.ts
@@ -0,0 +1,14 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+// #docregion example
+@Component({
+ selector: 'toh-hero-button',
+ template: `OK `
+})
+export class HeroButtonComponent implements OnInit {
+ ngOnInit() {
+ console.log('The component is initialized');
+ }
+}
+// #enddocregion example
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/index.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/index.ts
new file mode 100644
index 0000000000..6bb67c5670
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/hero-button/index.ts
@@ -0,0 +1 @@
+export * from './hero-button.component';
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/index.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/index.ts
new file mode 100644
index 0000000000..2334d49c9a
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/heroes/shared/index.ts
@@ -0,0 +1 @@
+export * from './hero-button';
diff --git a/public/docs/_examples/style-guide/ts/src/09-01/app/index.ts b/public/docs/_examples/style-guide/ts/src/09-01/app/index.ts
new file mode 100644
index 0000000000..fe8300f1dd
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/09-01/app/index.ts
@@ -0,0 +1,2 @@
+export * from './heroes';
+export * from './app.component';
diff --git a/public/docs/_examples/style-guide/ts/src/app/app.component.html b/public/docs/_examples/style-guide/ts/src/app/app.component.html
new file mode 100644
index 0000000000..0680b43f9c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/app/app.component.html
@@ -0,0 +1 @@
+
diff --git a/public/docs/_examples/style-guide/ts/src/app/app.component.ts b/public/docs/_examples/style-guide/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..7556beb1ff
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/style-guide/ts/src/app/app.routes.ts b/public/docs/_examples/style-guide/ts/src/app/app.routes.ts
new file mode 100644
index 0000000000..bce6b4df06
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/app/app.routes.ts
@@ -0,0 +1,8 @@
+import { Routes } from '@angular/router';
+
+import { AppComponent as S0101 } from '../01-01/app';
+
+export const routes: Routes = [
+ { path: '', redirectTo: '/01-01', pathMatch: 'full' },
+ { path: '01-01', component: S0101 },
+];
diff --git a/public/docs/_examples/style-guide/ts/src/app/hero-data.ts b/public/docs/_examples/style-guide/ts/src/app/hero-data.ts
new file mode 100644
index 0000000000..f3e6feb91c
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/app/hero-data.ts
@@ -0,0 +1,11 @@
+export class HeroData {
+ createDb() {
+ let heroes = [
+ { id: 1, name: 'Windstorm' },
+ { id: 2, name: 'Bombasto' },
+ { id: 3, name: 'Magneta' },
+ { id: 4, name: 'Tornado' }
+ ];
+ return {heroes};
+ }
+}
diff --git a/public/docs/_examples/style-guide/ts/src/index.html b/public/docs/_examples/style-guide/ts/src/index.html
new file mode 100644
index 0000000000..188c2c26a1
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+ Style Guide Sample
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ loading...
+
+
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/main.ts b/public/docs/_examples/style-guide/ts/src/main.ts
new file mode 100644
index 0000000000..5b4c98ba69
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/main.ts
@@ -0,0 +1,99 @@
+import { NgModule } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { HttpModule } from '@angular/http';
+import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
+
+import { RouterModule } from '@angular/router';
+
+import { HashLocationStrategy,
+ LocationStrategy } from '@angular/common';
+
+import 'rxjs/add/operator/map';
+
+import { HeroData } from './app/hero-data';
+import { AppComponent } from './app/app.component';
+
+import * as s0101 from './01-01/app/app.module';
+import * as s0205 from './02-05/app/app.module';
+import * as s0207 from './02-07/app/app.module';
+import * as s0208 from './02-08/app/app.module';
+import * as s0301 from './03-01/app/app.module';
+import * as s0302 from './03-02/app/app.module';
+import * as s0303 from './03-03/app/app.module';
+import * as s0304 from './03-04/app/app.module';
+import * as s0306 from './03-06/app/app.module';
+import * as s0408 from './04-08/app/app.module';
+import * as s0410 from './04-10/app/app.module';
+import * as s0411 from './04-11/app/app.module';
+import * as s0412 from './04-12/app/app.module';
+import * as s0502 from './05-02/app/app.module';
+import * as s0503 from './05-03/app/app.module';
+import * as s0504 from './05-04/app/app.module';
+import * as s0512 from './05-12/app/app.module';
+import * as s0513 from './05-13/app/app.module';
+import * as s0514 from './05-14/app/app.module';
+import * as s0515 from './05-15/app/app.module';
+import * as s0516 from './05-16/app/app.module';
+import * as s0517 from './05-17/app/app.module';
+import * as s0601 from './06-01/app/app.module';
+import * as s0603 from './06-03/app/app.module';
+import * as s0701 from './07-01/app/app.module';
+import * as s0703 from './07-03/app/app.module';
+import * as s0704 from './07-04/app/app.module';
+import * as s0901 from './09-01/app/app.module';
+
+///////////////////
+const moduleMetadata = {
+ imports: [
+ BrowserModule,
+ HttpModule,
+ InMemoryWebApiModule.forRoot(HeroData),
+
+ s0101.AppModule,
+ s0205.AppModule,
+ s0207.AppModule,
+ s0208.AppModule,
+ s0301.AppModule,
+ s0302.AppModule,
+ s0303.AppModule,
+ s0304.AppModule,
+ s0306.AppModule,
+ s0408.AppModule,
+ s0410.AppModule,
+ s0411.AppModule,
+ s0412.AppModule,
+ s0502.AppModule,
+ s0503.AppModule,
+ s0504.AppModule,
+ s0512.AppModule,
+ s0513.AppModule,
+ s0514.AppModule,
+ s0515.AppModule,
+ s0516.AppModule,
+ s0517.AppModule,
+ s0601.AppModule,
+ s0603.AppModule,
+ s0701.AppModule,
+ s0703.AppModule,
+ s0704.AppModule,
+ s0901.AppModule,
+
+ RouterModule.forRoot([
+ { path: '', redirectTo: '/01-01', pathMatch: 'full' }
+ ], {/* enableTracing: true */}),
+ ],
+ providers: [
+ { provide: LocationStrategy, useClass: HashLocationStrategy }
+ ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+};
+
+@NgModule(moduleMetadata)
+class MainModule { }
+
+platformBrowserDynamic().bootstrapModule(MainModule);
+
+
diff --git a/public/docs/_examples/style-guide/ts/src/systemjs.custom.js b/public/docs/_examples/style-guide/ts/src/systemjs.custom.js
new file mode 100644
index 0000000000..3424b2b62d
--- /dev/null
+++ b/public/docs/_examples/style-guide/ts/src/systemjs.custom.js
@@ -0,0 +1,51 @@
+(function(global) {
+ // extra local packages
+ var packageNames = [
+ '01-01', '01-01/app', '01-01/app/heroes', '01-01/app/heroes/shared',
+ '02-05', '02-05/app',
+ '02-07', '02-07/app', '02-07/app/heroes', '02-07/app/users',
+ '02-08', '02-08/app', '02-08/app/shared',
+ '03-01', '03-01/app', '03-01/app/core',
+ '03-02', '03-02/app', '03-02/app/core',
+ '03-03', '03-03/app', '03-03/app/core',
+ '03-04', '03-04/app', '03-04/app/core',
+ '03-05', '03-05/app', '03-05/app/core', '03-05/app/core/spinner', '03-05/app/core/toast',
+ '03-05/app/heroes', '03-05/app/heroes/shared',
+ '03-06', '03-06/app', '03-06/app/core', '03-06/app/core/spinner', '03-06/app/core/toast',
+ '03-06/app/heroes', '03-06/app/heroes/shared',
+ '04-08', '04-08/app', '04-08/app/heroes',
+ '04-10', '04-10/app', '04-10/app/shared', '04-10/app/heroes', '04-10/app/shared/spinner', '04-10/app/shared/toast',
+ '04-10/app/shared/filter-text',
+ '04-11', '04-11/app', '04-11/app/core', '04-11/app/heroes', '04-11/app/core/spinner',
+ '04-11/app/core/nav',
+ '04-12', '04-12/app', '04-12/app/core', '04-12/app/heroes', '04-12/app/core/nav',
+ '05-02', '05-02/app', '05-02/app/heroes', '05-02/app/heroes/shared', '05-02/app/heroes/shared/hero-button',
+ '05-03', '05-03/app', '05-03/app/heroes', '05-03/app/heroes/shared', '05-03/app/heroes/shared/hero-button',
+ '05-04', '05-04/app', '05-04/app/heroes', '05-04/app/heroes/shared',
+ '05-12', '05-12/app', '05-12/app/heroes', '05-12/app/heroes/shared', '05-12/app/heroes/shared/hero-button',
+ '05-13', '05-13/app', '05-13/app/heroes', '05-13/app/heroes/shared', '05-13/app/heroes/shared/hero-button',
+ '05-14', '05-14/app', '05-14/app/shared', '05-14/app/shared/toast',
+ '05-15', '05-15/app', '05-15/app/heroes', '05-15/app/heroes/hero-list', '05-15/app/heroes/shared',
+ '05-16', '05-16/app', '05-16/app/heroes',
+ '05-17', '05-17/app', '05-17/app/heroes', '05-17/app/heroes/hero', '05-17/app/heroes/hero-list',
+ '05-17/app/heroes/shared',
+ '06-01', '06-01/app', '06-01/app/shared',
+ '06-03', '06-03/app', '06-03/app/shared',
+ '07-01', '07-01/app', '07-01/app/heroes', '07-01/app/heroes/shared',
+ '07-03', '07-03/app', '07-03/app/heroes', '07-03/app/heroes/hero-list', '07-03/app/heroes/shared',
+ '07-04', '07-04/app', '07-04/app/heroes', '07-04/app/heroes/shared',
+ '09-01', '09-01/app', '09-01/app/heroes', '09-01/app/heroes/shared', '09-01/app/heroes/shared/hero-button'
+ ];
+
+ var packages = {};
+ packageNames.forEach(function(pkgName) {
+ packages[pkgName] = { main: 'index.js', defaultExtension: 'js', meta: { './*.js': { loader: 'systemjs-angular-loader.js' }} };
+ });
+
+ var config = {
+ packages: packages
+ }
+
+ System.config(config);
+
+})(this);
diff --git a/public/docs/_examples/styleguide/e2e-spec.ts b/public/docs/_examples/styleguide/e2e-spec.ts
new file mode 100644
index 0000000000..af10d2b71d
--- /dev/null
+++ b/public/docs/_examples/styleguide/e2e-spec.ts
@@ -0,0 +1,16 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+describe('Documentation StyleGuide E2E Tests', function() {
+
+ let expectedMsg = 'My First Angular App';
+
+ beforeEach(function () {
+ browser.get('');
+ });
+
+ it('should display: ' + expectedMsg, function() {
+ expect(element(by.id('output')).getText()).toEqual(expectedMsg);
+ });
+});
diff --git a/public/docs/_examples/styleguide/foo.dart b/public/docs/_examples/styleguide/foo.dart
new file mode 100644
index 0000000000..aeea7eb657
--- /dev/null
+++ b/public/docs/_examples/styleguide/foo.dart
@@ -0,0 +1,17 @@
+library angular2.src.core.application_static;
+
+// #docregion import
+import 'dart:async';
+import 'application_common.dart';
+import 'package:angular2/src/core/linker/dynamic_component_loader.dart' show ComponentRef;
+// #enddocregion import
+/// Starts an application from a root component.
+///
+/// See [commonBootstrap] for detailed documentation.
+Future bootstrapStatic(Type appComponentType,
+ [List componentInjectableBindings, void initReflector()]) {
+ if (initReflector != null) {
+ initReflector();
+ }
+ return commonBootstrap(appComponentType, componentInjectableBindings);
+}
diff --git a/public/docs/_examples/styleguide/foo.yaml b/public/docs/_examples/styleguide/foo.yaml
new file mode 100644
index 0000000000..37f8b7ab22
--- /dev/null
+++ b/public/docs/_examples/styleguide/foo.yaml
@@ -0,0 +1,21 @@
+name: benchpress
+version: <%= packageJson.version %>
+authors:
+<%= Object.keys(packageJson.contributors).map(function(name) {
+ return '- '+name+' <'+packageJson.contributors[name]+'>';
+}).join('\n') %>
+description: Benchpress - a framework for e2e performance tests
+homepage: <%= packageJson.homepage %>
+environment:
+ sdk: '>=1.10.0 <2.0.0'
+# #docregion dependencies
+dependencies:
+ angular2: '^<%= packageJson.version %>'
+ stack_trace: '^1.1.1'
+ webdriver: '^0.9.0'
+dev_dependencies:
+ guinness: '^0.1.17'
+dependency_overrides:
+ angular2:
+ path: ../angular2
+# #enddocregion
diff --git a/public/docs/_examples/styleguide/js/example-config.json b/public/docs/_examples/styleguide/js/example-config.json
new file mode 100644
index 0000000000..81f31aaf0d
--- /dev/null
+++ b/public/docs/_examples/styleguide/js/example-config.json
@@ -0,0 +1,3 @@
+{
+ "build": "build:babel"
+}
diff --git a/public/docs/_examples/styleguide/js/spec.js b/public/docs/_examples/styleguide/js/spec.js
new file mode 100644
index 0000000000..590ccc5dc4
--- /dev/null
+++ b/public/docs/_examples/styleguide/js/spec.js
@@ -0,0 +1,8 @@
+// #docregion
+describe("Jasmine sample test", function() {
+
+ it("1+1 should be 2", function() {
+ var result = 1 + 1;
+ expect(result).toBe(2);
+ });
+});
\ No newline at end of file
diff --git a/public/docs/_examples/styleguide/js/src/app.js b/public/docs/_examples/styleguide/js/src/app.js
new file mode 100644
index 0000000000..d38669cd78
--- /dev/null
+++ b/public/docs/_examples/styleguide/js/src/app.js
@@ -0,0 +1,55 @@
+(function(app) {
+
+// #docregion
+// #docregion class-w-annotations
+app.AppComponent =
+ // #docregion component
+ ng.core.Component({
+ selector: 'my-app',
+ // #enddocregion
+ // #docregion view
+ template: 'My First Angular App '
+ })
+ // #enddocregion
+ // #docregion class
+ .Class({
+ constructor: function () { }
+ });
+ // #enddocregion
+// #enddocregion
+
+// #docregion bootstrap
+app.AppModule =
+ ng.core.NgModule({
+ imports: [ ng.platformBrowser.BrowserModule ],
+ declarations: [ app.AppComponent ],
+ bootstrap: [ app.AppComponent ]
+ })
+ .Class({
+ constructor: function() {}
+ });
+
+document.addEventListener('DOMContentLoaded', function() {
+ ng.platformBrowserDynamic
+ .platformBrowserDynamic()
+ .bootstrapModule(app.AppModule);
+});
+// #enddocregion
+// #enddocregion
+
+})(window.app || (window.app = {}));
+
+/* Non DSL Approach */
+(function(app) {
+
+// #docregion no-dsl
+app.AppComponent = function AppComponent () {}
+
+app.AppComponent.annotations = [
+ new ng.core.Component({
+ selector: 'my-app',
+ template: 'My First Angular App '
+ })
+];
+// #enddocregion
+})(window.app || (window.app = {}));
diff --git a/public/docs/_examples/styleguide/js/src/index.html b/public/docs/_examples/styleguide/js/src/index.html
new file mode 100644
index 0000000000..ac2d34ba5d
--- /dev/null
+++ b/public/docs/_examples/styleguide/js/src/index.html
@@ -0,0 +1,27 @@
+
+
+
+ Documentation Style
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ foo2
+
+
+
diff --git a/public/docs/_examples/styleguide/jsonly.zipconfig.json b/public/docs/_examples/styleguide/jsonly.zipconfig.json
new file mode 100644
index 0000000000..118c776bdc
--- /dev/null
+++ b/public/docs/_examples/styleguide/jsonly.zipconfig.json
@@ -0,0 +1,3 @@
+{
+ "files": ["**/*.js"]
+}
\ No newline at end of file
diff --git a/public/docs/_examples/styleguide/package.1.json b/public/docs/_examples/styleguide/package.1.json
new file mode 100644
index 0000000000..481f99fb12
--- /dev/null
+++ b/public/docs/_examples/styleguide/package.1.json
@@ -0,0 +1,23 @@
+{
+ "name": "angular2-quickstart",
+ "version": "1.0.0",
+ "scripts": {
+ "tsc": "tsc",
+ "tsc:w": "tsc -w",
+ "lite": "lite-server",
+ "start": "concurrently \"npm run tsc:w\" \"npm run lite\" "
+ },
+ "license": "MIT",
+ "dependencies": {
+ "angular2": "2.0.0-beta.0",
+ "systemjs": "0.19.6",
+ "core-js": "^2.4.0",
+ "rxjs": "5.0.0-beta.0",
+ "zone.js": "0.5.10"
+ },
+ "devDependencies": {
+ "concurrently": "^1.0.0",
+ "lite-server": "^1.3.1",
+ "typescript": "^1.7.3"
+ }
+}
diff --git a/public/docs/_examples/styleguide/ts/example-config.json b/public/docs/_examples/styleguide/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/styleguide/ts/src/app/app.component.ts b/public/docs/_examples/styleguide/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..8b71f6ddc4
--- /dev/null
+++ b/public/docs/_examples/styleguide/ts/src/app/app.component.ts
@@ -0,0 +1,7 @@
+import { Component } from '@angular/core';
+@Component({
+ selector: 'my-app',
+ template: 'My First Angular App '
+})
+export class AppComponent { }
+
diff --git a/public/docs/_examples/styleguide/ts/src/app/app.module.ts b/public/docs/_examples/styleguide/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..0a9ee6adf7
--- /dev/null
+++ b/public/docs/_examples/styleguide/ts/src/app/app.module.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [ BrowserModule ],
+ declarations: [ AppComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/styleguide/ts/src/index.html b/public/docs/_examples/styleguide/ts/src/index.html
new file mode 100644
index 0000000000..5cb8919509
--- /dev/null
+++ b/public/docs/_examples/styleguide/ts/src/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+ Documentation Style
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/styleguide/ts/src/main.ts b/public/docs/_examples/styleguide/ts/src/main.ts
new file mode 100644
index 0000000000..6b6532d428
--- /dev/null
+++ b/public/docs/_examples/styleguide/ts/src/main.ts
@@ -0,0 +1,5 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/styleguide/zipconfig.json b/public/docs/_examples/styleguide/zipconfig.json
new file mode 100644
index 0000000000..74ca947f2b
--- /dev/null
+++ b/public/docs/_examples/styleguide/zipconfig.json
@@ -0,0 +1,4 @@
+{
+ "zipRegion": "class",
+ "files": ["**/*.*", "!**/*zipconfig.json"]
+}
\ No newline at end of file
diff --git a/public/docs/_examples/template-syntax/e2e-spec.ts b/public/docs/_examples/template-syntax/e2e-spec.ts
new file mode 100644
index 0000000000..71f1c58165
--- /dev/null
+++ b/public/docs/_examples/template-syntax/e2e-spec.ts
@@ -0,0 +1,43 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by } from 'protractor';
+
+// Not yet complete
+describe('Template Syntax', function () {
+
+ beforeAll(function () {
+ browser.get('');
+ });
+
+ it('should be able to use interpolation with a hero', function () {
+ let heroInterEle = element.all(by.css('h2+p')).get(0);
+ expect(heroInterEle.getText()).toEqual('My current hero is Hercules');
+ });
+
+ it('should be able to use interpolation with a calculation', function () {
+ let theSumEles = element.all(by.cssContainingText('h3~p', 'The sum of'));
+ expect(theSumEles.count()).toBe(2);
+ expect(theSumEles.get(0).getText()).toEqual('The sum of 1 + 1 is 2');
+ expect(theSumEles.get(1).getText()).toEqual('The sum of 1 + 1 is not 4');
+ });
+
+ it('should be able to use class binding syntax', function () {
+ let specialEle = element(by.cssContainingText('div', 'Special'));
+ expect(specialEle.getAttribute('class')).toMatch('special');
+ });
+
+ it('should be able to use style binding syntax', function () {
+ let specialButtonEle = element(by.cssContainingText('div.special~button', 'button'));
+ expect(specialButtonEle.getAttribute('style')).toMatch('color: red');
+ });
+
+ it('should two-way bind to sizer', async () => {
+ let div = element(by.css('div#two-way-1'));
+ let incButton = div.element(by.buttonText('+'));
+ let input = div.element(by.css('input'));
+ let initSize = await input.getAttribute('value');
+ incButton.click();
+ expect(input.getAttribute('value')).toEqual((+initSize + 1).toString());
+ });
+});
+
diff --git a/public/docs/_examples/template-syntax/ts/example-config.json b/public/docs/_examples/template-syntax/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/template-syntax/ts/plnkr.json b/public/docs/_examples/template-syntax/ts/plnkr.json
new file mode 100644
index 0000000000..099616ad7c
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/plnkr.json
@@ -0,0 +1,6 @@
+{
+ "description": "Template Syntax Collection",
+ "basePath": "src/",
+ "files":["!**/*.d.ts", "!**/*.js"],
+ "tags": ["template"]
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/app.component.css b/public/docs/_examples/template-syntax/ts/src/app/app.component.css
new file mode 100644
index 0000000000..23f9667623
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/app.component.css
@@ -0,0 +1,17 @@
+a.to-toc { margin: 30px 0; }
+button { font-size: 100%; margin: 0 2px; }
+div[clickable] {cursor: pointer; max-width: 200px; margin: 16px 0}
+#noTrackByCnt, #withTrackByCnt {color: darkred; max-width: 450px; margin: 4px;}
+img {height: 100px;}
+.box {border: 1px solid black; padding: 6px; max-width: 450px;}
+.child-div {margin-left: 1em; font-weight: normal}
+.context {margin-left: 1em;}
+.hidden {display: none}
+.parent-div {margin-top: 1em; font-weight: bold}
+.special {font-weight:bold; font-size: x-large}
+.bad {color: red;}
+.saveable {color: limegreen;}
+.curly, .modified {font-family: "Brush Script MT"}
+.toe {margin-left: 1em; font-style: italic;}
+little-hero {color:blue; font-size: smaller; background-color: Turquoise }
+.to-toc {margin-top: 10px; display: block}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/app.component.html b/public/docs/_examples/template-syntax/ts/src/app/app.component.html
new file mode 100644
index 0000000000..760fa3a367
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/app.component.html
@@ -0,0 +1,820 @@
+
+
+Template Syntax
+Interpolation
+Expression context
+Statement context
+Mental Model
+Buttons
+Properties vs. Attributes
+
+Property Binding
+
+
+Event Binding
+Two-way Binding
+
+Directives
+
+
+Template reference variables
+Inputs and outputs
+Pipes
+Safe navigation operator ?.
+Enums
+
+
+Interpolation
+
+
+My current hero is {{currentHero.name}}
+
+
+
+
+ {{title}}
+
+
+
+
+
+
+The sum of 1 + 1 is {{1 + 1}}
+
+
+
+
+The sum of 1 + 1 is not {{1 + 1 + getVal()}}
+
+
+top
+
+Expression context
+
+Component expression context ({{title}}, [hidden]="isUnchanged")
+
+
+ {{title}}
+ changed
+
+
+
+
+Template input variable expression context (let hero)
+
+
+
+ {{hero.name}}
+
+
+
+Template reference variable expression context (#heroInput)
+
+ Type something:
+
+ {{heroInput.value}}
+
+
+
+top
+
+Statement context
+
+Component statement context ( (click)="onSave() )
+
+
+ Delete hero
+
+
+
+Template $event statement context
+
+
+ Save
+
+
+
+Template input variable statement context (let hero)
+
+
+
+ {{hero.name}}
+
+
+
+Template reference variable statement context (#heroForm)
+
+
+
+
+
+
+top
+
+
+New Mental Model
+
+
+
+
+Mental Model
+
+Save
+
+
+
+
+
+
+
Mental Model
+
+
+
+
+
+
+
+
+
+ Save
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Save
+
+click me
+
+{{clicked}}
+
+
+
+ Hero Name:
+
+
+
+ {{name}}
+
+
+
+
+help
+
+
+
+
+Special
+
+
+
+
+
+
+button
+
+top
+
+
+Property vs. Attribute (img examples)
+
+
+
+
+
+
+
+
+
+top
+
+
+
+
+Enabled (but does nothing)
+Disabled
+Still disabled
+
+disabled by attribute
+disabled by property binding
+
+Disabled Cancel
+Enabled Save
+
+top
+
+
+Property Binding
+
+
+
+
+
+Cancel is disabled
+
+
+[ngClass] binding to the classes property
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ is the interpolated image.
+ is the property bound image.
+
+"{{title}}" is the interpolated title.
+" " is the property bound title.
+
+
+
+
+"{{evilTitle}}" is the interpolated evil title.
+" " is the property bound evil title.
+
+
+top
+
+
+Attribute Binding
+
+
+
+
+
+ One-Two
+
+
+
+ Five Six
+
+
+
+
+
+
+{{actionName}} with Aria
+
+
+
+
+
+
+ Disabled
+
+ Disabled as well
+
+
+ Enabled (but inert)
+
+
+top
+
+
+Class Binding
+
+
+
+Bad curly special
+
+
+
+
+Bad curly
+
+
+
+
+
+The class binding is special
+
+
+
+This one is not so special
+
+
+This class binding is special too
+
+top
+
+
+Style Binding
+
+
+Red
+Save
+
+
+
+Big
+Small
+
+
+top
+
+
+Event Binding
+
+
+Save
+
+
+
+On Save
+
+
+
+
+
+
+
click with myClick
+
+
+{{clickMessage}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+Click me
+
Click me too!
+
+
+
+
+
+
+ Save, no propagation
+
+
+
+
+
+
+ Save w/ propagation
+
+
+
+top
+
+Two-way Binding
+
+
+
+
Resizable Text
+
+
FontSize (px):
+
+
+
+
De-sugared two-way binding
+
+
+
+
+
+top
+
+
+NgModel (two-way) Binding
+
+Result: {{currentHero.name}}
+
+
+
+
+without NgModel
+
+
+
+
+[(ngModel)]
+
+
+
+
+bindon-ngModel
+
+
+
+
+(ngModelChange)="...name=$event"
+
+
+
+
+(ngModelChange)="setUppercaseName($event)"
+
+top
+
+
+NgClass Binding
+
+currentClasses is {{currentClasses | json}}
+
+This div is initially saveable, unchanged, and special
+
+
+
+
+saveable |
+modified: |
+special:
+Refresh currentClasses
+
+
+ This div should be {{ canSave ? "": "not"}} saveable,
+ {{ isUnchanged ? "unchanged" : "modified" }} and,
+ {{ isSpecial ? "": "not"}} special after clicking "Refresh".
+
+
+This div is special
+
+Bad curly special
+Curly special
+
+top
+
+
+NgStyle Binding
+
+
+
+ This div is x-large or smaller.
+
+
+
+[ngStyle] binding to currentStyles - CSS property names
+currentStyles is {{currentStyles | json}}
+
+
+ This div is initially italic, normal weight, and extra large (24px).
+
+
+
+
+
+italic: |
+normal: |
+xlarge:
+Refresh currentStyles
+
+
+ This div should be {{ canSave ? "italic": "plain"}},
+ {{ isUnchanged ? "normal weight" : "bold" }} and,
+ {{ isSpecial ? "extra large": "normal size"}} after clicking "Refresh".
+
+top
+
+
+NgIf Binding
+
+
+
+
+
+
+Hello, {{currentHero.name}}
+Hello, {{nullHero.name}}
+
+
+
+
+Add {{currentHero.name}} with template
+
+
+Hero Detail removed from DOM (via template) because isActive is false
+
+
+
+
+
+
+Show with class
+Hide with class
+
+
+
+
+Show with style
+Hide with style
+
+
+top
+
+
+NgFor Binding
+
+
+
+
+
+
+
+
+
+
+
+top
+
+*ngFor with index
+with semi-colon separator
+
+
+
{{i + 1}} - {{hero.name}}
+
+
+
+with comma separator
+
+
+
{{i + 1}} - {{hero.name}}
+
+
+top
+
+*ngFor trackBy
+Reset heroes
+Change ids
+Clear counts
+
+without trackBy
+
+
({{hero.id}}) {{hero.name}}
+
+
+ Hero DOM elements change #{{heroesNoTrackByCount}} without trackBy
+
+
+
+with trackBy
+
+
({{hero.id}}) {{hero.name}}
+
+
+ Hero DOM elements change #{{heroesWithTrackByCount}} with trackBy
+
+
+
+
+
+with trackBy and semi-colon separator
+
+
+
+ ({{hero.id}}) {{hero.name}}
+
+
+
+
+with trackBy and comma separator
+
+
({{hero.id}}) {{hero.name}}
+
+
+with trackBy and space separator
+
+
({{hero.id}}) {{hero.name}}
+
+
+with generic trackById function
+
+
({{hero.id}}) {{hero.name}}
+
+
+top
+
+
+NgSwitch Binding
+
+Pick your favorite hero
+
+
+ {{h.name}}
+
+
+
+
+
+
+
+
+
+
+
Are you as confused as {{currentHero.name}}?
+
+
+
+
+
+
+top
+
+
+Template reference variables
+
+
+
+
+
+
+
+
+
+Call
+
+
+
+
+Fax
+
+
+
+
+
+Example Form
+
+
+top
+
+
+
+
+
+
+Save
+
+
+
+
+
+
+
+myClick2
+{{clickMessage2}}
+
+top
+
+
+Pipes
+
+
+Title through uppercase pipe: {{title | uppercase}}
+
+
+
+
+
+ Title through a pipe chain:
+ {{title | uppercase | lowercase}}
+
+
+
+
+
+Birthdate: {{currentHero?.birthdate | date:'longDate'}}
+
+
+
+{{currentHero | json}}
+
+
+Birthdate: {{(currentHero?.birthdate | date:'longDate') | uppercase}}
+
+
+
+ Price: {{product.price | currency:'USD':true}}
+
+
+top
+
+
+Safe navigation operator ?.
+
+
+
+ The title is {{title}}
+
+
+
+
+
+ The current hero's name is {{currentHero?.name}}
+
+
+
+
+
+ The current hero's name is {{currentHero.name}}
+
+
+
+
+
+
+
+
+The null hero's name is {{nullHero.name}}
+
+
+
+
+The null hero's name is {{nullHero && nullHero.name}}
+
+
+
+
+
+
+ The null hero's name is {{nullHero?.name}}
+
+
+
+top
+
+
+
+Enums in binding
+
+
+
+ The name of the Color.Red enum is {{Color[Color.Red]}}.
+ The current color is {{Color[color]}} and its number is {{color}}.
+ Enum Toggle
+
+
+
+top
diff --git a/public/docs/_examples/template-syntax/ts/src/app/app.component.ts b/public/docs/_examples/template-syntax/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..f0fa2a2d23
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/app.component.ts
@@ -0,0 +1,183 @@
+/* tslint:disable:forin member-ordering */
+// #docplaster
+
+import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core';
+
+import { Hero } from './hero';
+
+export enum Color {Red, Green, Blue};
+
+/**
+ * Giant grab bag of stuff to drive the chapter
+ */
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html',
+ styleUrls: [ './app.component.css' ]
+})
+export class AppComponent implements AfterViewInit, OnInit {
+
+ ngOnInit() {
+ this.resetHeroes();
+ this.setCurrentClasses();
+ this.setCurrentStyles();
+ }
+
+ ngAfterViewInit() {
+ // Detect effects of NgForTrackBy
+ trackChanges(this.heroesNoTrackBy, () => this.heroesNoTrackByCount++);
+ trackChanges(this.heroesWithTrackBy, () => this.heroesWithTrackByCount++);
+ }
+
+ @ViewChildren('noTrackBy') heroesNoTrackBy: QueryList;
+ @ViewChildren('withTrackBy') heroesWithTrackBy: QueryList;
+
+ actionName = 'Go for it';
+ badCurly = 'bad curly';
+ classes = 'special';
+ help = '';
+
+ alert(msg?: string) { window.alert(msg); }
+ callFax(value: string) { this.alert(`Faxing ${value} ...`); }
+ callPhone(value: string) { this.alert(`Calling ${value} ...`); }
+ canSave = true;
+
+ changeIds() {
+ this.resetHeroes();
+ this.heroes.forEach(h => h.id += 10 * this.heroIdIncrement++);
+ this.heroesWithTrackByCountReset = -1;
+ }
+
+ clearTrackByCounts() {
+ const trackByCountReset = this.heroesWithTrackByCountReset;
+ this.resetHeroes();
+ this.heroesNoTrackByCount = -1;
+ this.heroesWithTrackByCount = trackByCountReset;
+ this.heroIdIncrement = 1;
+ }
+
+ clicked = '';
+ clickMessage = '';
+ clickMessage2 = '';
+
+ Color = Color;
+ color = Color.Red;
+ colorToggle() {this.color = (this.color === Color.Red) ? Color.Blue : Color.Red; }
+
+ currentHero: Hero;
+
+ deleteHero(hero: Hero) {
+ this.alert(`Delete ${hero ? hero.name : 'the hero'}.`);
+ }
+
+ // #docregion evil-title
+ evilTitle = 'Template Syntax';
+ // #enddocregion evil-title
+
+ fontSizePx = 16;
+
+ title = 'Template Syntax';
+
+ getVal(): number { return 2; }
+
+ name: string = Hero.heroes[0].name;
+ hero: Hero; // defined to demonstrate template context precedence
+ heroes: Hero[];
+
+ // trackBy change counting
+ heroesNoTrackByCount = 0;
+ heroesWithTrackByCount = 0;
+ heroesWithTrackByCountReset = 0;
+
+ heroIdIncrement = 1;
+
+ // heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
+ // Public Domain terms of use: http://www.wpclipart.com/terms.html
+ heroImageUrl = 'images/hero.png';
+ // villainImageUrl = 'http://www.clker.com/cliparts/u/s/y/L/x/9/villain-man-hi.png'
+ // Public Domain terms of use http://www.clker.com/disclaimer.html
+ villainImageUrl = 'images/villain.png';
+
+ iconUrl = 'images/ng-logo.png';
+ isActive = false;
+ isSpecial = true;
+ isUnchanged = true;
+
+ get nullHero(): Hero { return null; }
+
+ onClickMe(event: KeyboardEvent) {
+ let evtMsg = event ? ' Event target class is ' + (event.target).className : '';
+ this.alert('Click me.' + evtMsg);
+ }
+
+ onSave(event: KeyboardEvent) {
+ let evtMsg = event ? ' Event target is ' + (event.target).innerText : '';
+ this.alert('Saved.' + evtMsg);
+ if (event) { event.stopPropagation(); }
+ }
+
+ onSubmit() {/* referenced but not used */}
+
+ product = {
+ name: 'frimfram',
+ price: 42
+ };
+
+ // updates with fresh set of cloned heroes
+ resetHeroes() {
+ this.heroes = Hero.heroes.map(hero => hero.clone());
+ this.currentHero = this.heroes[0];
+ this.heroesWithTrackByCountReset = 0;
+ }
+
+ setUppercaseName(name: string) {
+ this.currentHero.name = name.toUpperCase();
+ }
+
+ // #docregion setClasses
+ currentClasses: {};
+ setCurrentClasses() {
+ // CSS classes: added/removed per current state of component properties
+ this.currentClasses = {
+ saveable: this.canSave,
+ modified: !this.isUnchanged,
+ special: this.isSpecial
+ };
+ }
+ // #enddocregion setClasses
+
+ // #docregion setStyles
+ currentStyles: {};
+ setCurrentStyles() {
+ // CSS styles: set per current state of component properties
+ this.currentStyles = {
+ 'font-style': this.canSave ? 'italic' : 'normal',
+ 'font-weight': !this.isUnchanged ? 'bold' : 'normal',
+ 'font-size': this.isSpecial ? '24px' : '12px'
+ };
+ }
+ // #enddocregion setStyles
+
+ // #docregion trackByHeroes
+ trackByHeroes(index: number, hero: Hero): number { return hero.id; }
+ // #enddocregion trackByHeroes
+
+ // #docregion trackById
+ trackById(index: number, item: any): number { return item['id']; }
+ // #enddocregion trackById
+}
+
+// helper to track changes to viewChildren
+function trackChanges(views: QueryList, changed: () => void) {
+ let oldRefs = views.toArray();
+ views.changes.subscribe((changes: QueryList) => {
+ const changedRefs = changes.toArray();
+ // Is every changed ElemRef the same as old and in the same position
+ const isSame = oldRefs.every((v, i) => v === changedRefs[i]);
+ if (!isSame) {
+ oldRefs = changedRefs;
+ // wait a tick because called after views are constructed
+ setTimeout(changed, 0);
+ }
+ });
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/app.module.1.ts b/public/docs/_examples/template-syntax/ts/src/app/app.module.1.ts
new file mode 100644
index 0000000000..8ea0d3d207
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/app.module.1.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular
+
+/* Other imports */
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule // <--- import into the NgModule
+ ],
+ /* Other module metadata */
+})
+export class AppModule { }
diff --git a/public/docs/_examples/template-syntax/ts/src/app/app.module.ts b/public/docs/_examples/template-syntax/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..5c2fbed6f1
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/app.module.ts
@@ -0,0 +1,29 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { BigHeroDetailComponent, HeroDetailComponent } from './hero-detail.component';
+import { ClickDirective, ClickDirective2 } from './click.directive';
+import { HeroFormComponent } from './hero-form.component';
+import { heroSwitchComponents } from './hero-switch.components';
+import { SizerComponent } from './sizer.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ declarations: [
+ AppComponent,
+ BigHeroDetailComponent,
+ HeroDetailComponent,
+ HeroFormComponent,
+ heroSwitchComponents,
+ ClickDirective,
+ ClickDirective2,
+ SizerComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/template-syntax/ts/src/app/click.directive.ts b/public/docs/_examples/template-syntax/ts/src/app/click.directive.ts
new file mode 100644
index 0000000000..09757bfeaf
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/click.directive.ts
@@ -0,0 +1,41 @@
+/* tslint:disable use-output-property-decorator */
+// #docplaster
+import { Directive, ElementRef, EventEmitter, Output } from '@angular/core';
+
+@Directive({selector: '[myClick]'})
+export class ClickDirective {
+ // #docregion output-myClick
+ @Output('myClick') clicks = new EventEmitter(); // @Output(alias) propertyName = ...
+ // #enddocregion output-myClick
+
+ toggle = false;
+
+ constructor(el: ElementRef) {
+ el.nativeElement
+ .addEventListener('click', (event: Event) => {
+ this.toggle = !this.toggle;
+ this.clicks.emit(this.toggle ? 'Click!' : '');
+ });
+ }
+}
+
+// #docregion output-myClick2
+@Directive({
+ // #enddocregion output-myClick2
+ selector: '[myClick2]',
+ // #docregion output-myClick2
+ outputs: ['clicks:myClick'] // propertyName:alias
+})
+// #enddocregion output-myClick2
+export class ClickDirective2 {
+ clicks = new EventEmitter();
+ toggle = false;
+
+ constructor(el: ElementRef) {
+ el.nativeElement
+ .addEventListener('click', (event: Event) => {
+ this.toggle = !this.toggle;
+ this.clicks.emit(this.toggle ? 'Click2!' : '');
+ });
+ }
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/hero-detail.component.ts b/public/docs/_examples/template-syntax/ts/src/app/hero-detail.component.ts
new file mode 100644
index 0000000000..725849d692
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/hero-detail.component.ts
@@ -0,0 +1,80 @@
+/* tslint:disable use-input-property-decorator use-output-property-decorator */
+// #docplaster
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+import { Hero } from './hero';
+
+// #docregion input-output-2
+@Component({
+// #enddocregion input-output-2
+ selector: 'hero-detail',
+ // #docregion input-output-2
+ inputs: ['hero'],
+ outputs: ['deleteRequest'],
+ // #enddocregion input-output-2
+ styles: ['button {margin-left: 8px} div {margin: 8px 0} img {height:24px}'],
+ // #docregion template-1
+ template: `
+
+
+
+ {{prefix}} {{hero?.name}}
+
+
Delete
+
`
+ // #enddocregion template-1
+// #docregion input-output-2
+})
+// #enddocregion input-output-2
+export class HeroDetailComponent {
+ hero: Hero = new Hero(-1, '', 'Zzzzzzzz'); // default sleeping hero
+ // heroImageUrl = 'http://www.wpclipart.com/cartoon/people/hero/hero_silhoutte_T.png';
+ // Public Domain terms of use: http://www.wpclipart.com/terms.html
+ heroImageUrl = 'images/hero.png';
+ lineThrough = '';
+ @Input() prefix = '';
+
+ // #docregion deleteRequest
+ // This component make a request but it can't actually delete a hero.
+ deleteRequest = new EventEmitter();
+
+ delete() {
+ this.deleteRequest.emit(this.hero);
+ // #enddocregion deleteRequest
+ this.lineThrough = this.lineThrough ? '' : 'line-through';
+ // #docregion deleteRequest
+ }
+ // #enddocregion deleteRequest
+}
+
+@Component({
+ selector: 'big-hero-detail',
+ template: `
+
+
+
{{hero?.name}}
+
Name: {{hero?.name}}
+
Emotion: {{hero?.emotion}}
+
Birthdate: {{hero?.birthdate | date:'longDate'}}
+
+
Rate/hr: {{hero?.rate | currency:'EUR'}}
+
+
Delete
+
+ `,
+ styles: [`
+ .detail { border: 1px solid black; padding: 4px; max-width: 450px; }
+ img { float: left; margin-right: 8px; height: 100px; }
+ `]
+})
+export class BigHeroDetailComponent extends HeroDetailComponent {
+
+ // #docregion input-output-1
+ @Input() hero: Hero;
+ @Output() deleteRequest = new EventEmitter();
+ // #enddocregion input-output-1
+
+ delete() {
+ this.deleteRequest.emit(this.hero);
+ }
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/hero-form.component.html b/public/docs/_examples/template-syntax/ts/src/app/hero-form.component.html
new file mode 100644
index 0000000000..2182060439
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/hero-form.component.html
@@ -0,0 +1,15 @@
+
diff --git a/public/docs/_examples/template-syntax/ts/src/app/hero-form.component.ts b/public/docs/_examples/template-syntax/ts/src/app/hero-form.component.ts
new file mode 100644
index 0000000000..7bdc4a0bba
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/hero-form.component.ts
@@ -0,0 +1,30 @@
+import { Component, Input, ViewChild } from '@angular/core';
+import { NgForm } from '@angular/forms';
+
+import { Hero } from './hero';
+
+@Component({
+ selector: 'hero-form',
+ templateUrl: './hero-form.component.html',
+ styles: [`
+ button { margin: 6px 0; }
+ #heroForm { border: 1px solid black; margin: 20px 0; padding: 8px; max-width: 350px; }
+ `]
+})
+export class HeroFormComponent {
+ @Input() hero: Hero;
+ @ViewChild('heroForm') form: NgForm;
+
+ private _submitMessage = '';
+
+ get submitMessage() {
+ if (!this.form.valid) {
+ this._submitMessage = '';
+ }
+ return this._submitMessage;
+ }
+
+ onSubmit(form: NgForm) {
+ this._submitMessage = 'Submitted. form value is ' + JSON.stringify(form.value);
+ }
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/hero-switch.components.ts b/public/docs/_examples/template-syntax/ts/src/app/hero-switch.components.ts
new file mode 100644
index 0000000000..a21892e248
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/hero-switch.components.ts
@@ -0,0 +1,42 @@
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+@Component({
+ selector: 'happy-hero',
+ template: `Wow. You like {{hero.name}}. What a happy hero ... just like you.`
+})
+export class HappyHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'sad-hero',
+ template: `You like {{hero.name}}? Such a sad hero. Are you sad too?`
+})
+export class SadHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'confused-hero',
+ template: `Are you as confused as {{hero.name}}?`
+})
+export class ConfusedHeroComponent {
+ @Input() hero: Hero;
+}
+
+@Component({
+ selector: 'unknown-hero',
+ template: `{{message}}`
+})
+export class UnknownHeroComponent {
+ @Input() hero: Hero;
+ get message() {
+ return this.hero && this.hero.name ?
+ `${this.hero.name} is strange and mysterious.` :
+ 'Are you feeling indecisive?';
+ }
+}
+
+export const heroSwitchComponents =
+ [ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
diff --git a/public/docs/_examples/template-syntax/ts/src/app/hero.ts b/public/docs/_examples/template-syntax/ts/src/app/hero.ts
new file mode 100644
index 0000000000..f8cc3b16a6
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/hero.ts
@@ -0,0 +1,34 @@
+export class Hero {
+ static nextId = 0;
+
+ static heroes: Hero[] = [
+ new Hero(
+ null,
+ 'Hercules',
+ 'happy',
+ new Date(1970, 1, 25),
+ 'http://www.imdb.com/title/tt0065832/',
+ 325
+ ),
+ new Hero(1, 'Mr. Nice', 'happy'),
+ new Hero(2, 'Narco', 'sad' ),
+ new Hero(3, 'Windstorm', 'confused' ),
+ new Hero(4, 'Magneta')
+ ];
+
+
+ constructor(
+ public id?: number,
+ public name?: string,
+ public emotion?: string,
+ public birthdate?: Date,
+ public url?: string,
+ public rate = 100,
+ ) {
+ this.id = id ? id : Hero.nextId++;
+ }
+
+ clone(): Hero {
+ return Object.assign(new Hero(), this);
+ }
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/app/sizer.component.ts b/public/docs/_examples/template-syntax/ts/src/app/sizer.component.ts
new file mode 100644
index 0000000000..b6065c8cd1
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/app/sizer.component.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+@Component({
+ selector: 'my-sizer',
+ template: `
+
+ -
+ +
+ FontSize: {{size}}px
+
`
+})
+export class SizerComponent {
+ @Input() size: number | string;
+ @Output() sizeChange = new EventEmitter();
+
+ dec() { this.resize(-1); }
+ inc() { this.resize(+1); }
+
+ resize(delta: number) {
+ this.size = Math.min(40, Math.max(8, +this.size + delta));
+ this.sizeChange.emit(this.size);
+ }
+}
diff --git a/public/docs/_examples/template-syntax/ts/src/images/hero.png b/public/docs/_examples/template-syntax/ts/src/images/hero.png
new file mode 100644
index 0000000000..2a128ac367
Binary files /dev/null and b/public/docs/_examples/template-syntax/ts/src/images/hero.png differ
diff --git a/public/docs/_examples/template-syntax/ts/src/images/ng-logo.png b/public/docs/_examples/template-syntax/ts/src/images/ng-logo.png
new file mode 100644
index 0000000000..1e488b1a49
Binary files /dev/null and b/public/docs/_examples/template-syntax/ts/src/images/ng-logo.png differ
diff --git a/public/docs/_examples/template-syntax/ts/src/images/villain.png b/public/docs/_examples/template-syntax/ts/src/images/villain.png
new file mode 100644
index 0000000000..26697d1a42
Binary files /dev/null and b/public/docs/_examples/template-syntax/ts/src/images/villain.png differ
diff --git a/public/docs/_examples/template-syntax/ts/src/index.html b/public/docs/_examples/template-syntax/ts/src/index.html
new file mode 100644
index 0000000000..3e711397cb
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/index.html
@@ -0,0 +1,26 @@
+
+
+
+ Template Syntax
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
+
diff --git a/public/docs/_examples/template-syntax/ts/src/main.ts b/public/docs/_examples/template-syntax/ts/src/main.ts
new file mode 100644
index 0000000000..311c44b76d
--- /dev/null
+++ b/public/docs/_examples/template-syntax/ts/src/main.ts
@@ -0,0 +1,5 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/testing/ts/.gitignore b/public/docs/_examples/testing/ts/.gitignore
new file mode 100644
index 0000000000..5374943421
--- /dev/null
+++ b/public/docs/_examples/testing/ts/.gitignore
@@ -0,0 +1 @@
+!src/browser-test-shim.js
diff --git a/public/docs/_examples/testing/ts/1st-specs.plnkr.json b/public/docs/_examples/testing/ts/1st-specs.plnkr.json
new file mode 100644
index 0000000000..9fe15ddef1
--- /dev/null
+++ b/public/docs/_examples/testing/ts/1st-specs.plnkr.json
@@ -0,0 +1,14 @@
+{
+ "description": "Testing - 1st.specs",
+ "basePath": "src/",
+ "files":[
+ "browser-test-shim.js",
+ "styles.css",
+
+ "app/1st.spec.ts",
+ "1st-specs.html"
+ ],
+ "main": "1st-specs.html",
+ "open": "app/1st.spec.ts",
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/app-specs.plnkr.json b/public/docs/_examples/testing/ts/app-specs.plnkr.json
new file mode 100644
index 0000000000..d97bf82c86
--- /dev/null
+++ b/public/docs/_examples/testing/ts/app-specs.plnkr.json
@@ -0,0 +1,24 @@
+{
+ "description": "Testing - app.specs",
+ "basePath": "src/",
+ "files":[
+ "browser-test-shim.js",
+ "systemjs.config.extras.js",
+ "styles.css",
+
+ "app/**/*.css",
+ "app/**/*.html",
+ "app/**/*.ts",
+ "app/**/*.spec.ts",
+
+ "testing/*.ts",
+
+ "!main.ts",
+ "!app/bag/*.*",
+ "!app/1st.spec.ts",
+
+ "app-specs.html"
+ ],
+ "main": "app-specs.html",
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/bag-specs.plnkr.json b/public/docs/_examples/testing/ts/bag-specs.plnkr.json
new file mode 100644
index 0000000000..cd22e10c28
--- /dev/null
+++ b/public/docs/_examples/testing/ts/bag-specs.plnkr.json
@@ -0,0 +1,21 @@
+{
+ "description": "Testing - bag.specs",
+ "basePath": "src/",
+ "files":[
+ "browser-test-shim.js",
+ "systemjs.config.extras.js",
+ "styles.css",
+
+ "app/bag/**/*.html",
+ "app/bag/**/*.ts",
+ "app/bag/**/*.spec.ts",
+
+ "!app/bag/bag-main.ts",
+
+ "testing/*.ts",
+
+ "bag-specs.html"
+ ],
+ "main": "bag-specs.html",
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/bag.plnkr.json b/public/docs/_examples/testing/ts/bag.plnkr.json
new file mode 100644
index 0000000000..4bb0ac9c5b
--- /dev/null
+++ b/public/docs/_examples/testing/ts/bag.plnkr.json
@@ -0,0 +1,14 @@
+{
+ "description": "Running the bag",
+ "basePath": "src/",
+ "files":[
+ "styles.css",
+
+ "app/bag/bag.ts",
+ "app/bag/bag-external-template.html",
+ "app/bag/bag-main.ts",
+ "bag.html"
+ ],
+ "main": "bag.html",
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/banner-inline-specs.plnkr.json b/public/docs/_examples/testing/ts/banner-inline-specs.plnkr.json
new file mode 100644
index 0000000000..77b8c212cf
--- /dev/null
+++ b/public/docs/_examples/testing/ts/banner-inline-specs.plnkr.json
@@ -0,0 +1,15 @@
+{
+ "description": "Testing - banner-inline.component.specs",
+ "basePath": "src/",
+ "files":[
+ "browser-test-shim.js",
+ "systemjs.config.extras.js",
+
+ "app/banner-inline.component.ts",
+ "app/banner-inline.component.spec.ts",
+ "banner-inline-specs.html"
+ ],
+ "main": "banner-inline-specs.html",
+ "open": "app/banner-inline.component.spec.ts",
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/banner-specs.plnkr.json b/public/docs/_examples/testing/ts/banner-specs.plnkr.json
new file mode 100644
index 0000000000..6e5f20bccb
--- /dev/null
+++ b/public/docs/_examples/testing/ts/banner-specs.plnkr.json
@@ -0,0 +1,17 @@
+{
+ "description": "Testing - banner.component.specs",
+ "basePath": "src/",
+ "files":[
+ "browser-test-shim.js",
+ "systemjs.config.extras.js",
+
+ "app/banner.component.css",
+ "app/banner.component.html",
+ "app/banner.component.ts",
+ "app/banner.component.spec.ts",
+ "banner-specs.html"
+ ],
+ "main": "banner-specs.html",
+ "open": "app/banner.component.spec.ts",
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/example-config.json b/public/docs/_examples/testing/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/testing/ts/karma-test-shim.js b/public/docs/_examples/testing/ts/karma-test-shim.js
new file mode 100644
index 0000000000..1b8d6acdd4
--- /dev/null
+++ b/public/docs/_examples/testing/ts/karma-test-shim.js
@@ -0,0 +1,96 @@
+// #docregion
+// /*global jasmine, __karma__, window*/
+Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing.
+
+// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
+// Error.stackTraceLimit = Infinity; //
+
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
+
+// builtPaths: root paths for output ("built") files
+// get from karma.config.js, then prefix with '/src/' (default is 'app/')
+var builtPaths = (__karma__.config.builtPaths || ['src/'])
+ .map(function(p) { return '/base/'+p;});
+
+__karma__.loaded = function () { };
+
+function isJsFile(path) {
+ return path.slice(-3) == '.js';
+}
+
+function isSpecFile(path) {
+ return /\.spec\.(.*\.)?js$/.test(path);
+}
+
+// Is a "built" file if is JavaScript file in one of the "built" folders
+function isBuiltFile(path) {
+ return isJsFile(path) &&
+ builtPaths.reduce(function(keep, bp) {
+ return keep || (path.substr(0, bp.length) === bp);
+ }, false);
+}
+
+var allSpecFiles = Object.keys(window.__karma__.files)
+ .filter(isSpecFile)
+ .filter(isBuiltFile);
+
+System.config({
+ baseURL: 'base/src',
+ // Extend usual application package list with testing folder
+ packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } },
+
+ // Assume npm: is set in `paths` in systemjs.config
+ // Map the angular testing umd bundles
+ map: {
+ '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
+ '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
+ '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
+ '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
+ '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
+ '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
+ '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
+ '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',
+ },
+});
+
+System.import('systemjs.config.js')
+ .then(importSystemJsExtras)
+ .then(initTestBed)
+ .then(initTesting);
+
+/** Optional SystemJS configuration extras. Keep going w/o it */
+function importSystemJsExtras(){
+ return System.import('systemjs.config.extras.js')
+ .catch(function(reason) {
+ console.log(
+ 'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.'
+ );
+ console.log(reason);
+ });
+}
+
+function initTestBed(){
+ return Promise.all([
+ System.import('@angular/core/testing'),
+ System.import('@angular/platform-browser-dynamic/testing')
+ ])
+
+ .then(function (providers) {
+ var coreTesting = providers[0];
+ var browserTesting = providers[1];
+
+ coreTesting.TestBed.initTestEnvironment(
+ browserTesting.BrowserDynamicTestingModule,
+ browserTesting.platformBrowserDynamicTesting());
+ })
+}
+
+// Import all spec files and start karma
+function initTesting () {
+ return Promise.all(
+ allSpecFiles.map(function (moduleName) {
+ return System.import(moduleName);
+ })
+ )
+ .then(__karma__.start, __karma__.error);
+}
diff --git a/public/docs/_examples/testing/ts/karma.conf.js b/public/docs/_examples/testing/ts/karma.conf.js
new file mode 100644
index 0000000000..a00b8add54
--- /dev/null
+++ b/public/docs/_examples/testing/ts/karma.conf.js
@@ -0,0 +1,98 @@
+// #docregion
+module.exports = function(config) {
+
+ var appBase = 'src/'; // transpiled app JS and map files
+ var appAssets = '/base/app/'; // component assets fetched by Angular's compiler
+
+ // Testing helpers (optional) are conventionally in a folder called `testing`
+ var testingBase = 'src/testing/'; // transpiled test JS and map files
+ var testingSrcBase = 'src/testing/'; // test source TS files
+
+ config.set({
+ basePath: '',
+ frameworks: ['jasmine'],
+
+ plugins: [
+ require('karma-jasmine'),
+ require('karma-chrome-launcher'),
+ require('karma-jasmine-html-reporter')
+ ],
+
+ client: {
+ builtPaths: [appBase, testingBase], // add more spec base paths as needed
+ clearContext: false // leave Jasmine Spec Runner output visible in browser
+ },
+
+ customLaunchers: {
+ // From the CLI. Not used here but interesting
+ // chrome setup for travis CI using chromium
+ Chrome_travis_ci: {
+ base: 'Chrome',
+ flags: ['--no-sandbox']
+ }
+ },
+
+ files: [
+ // System.js for module loading
+ 'node_modules/systemjs/dist/system.src.js',
+
+ // Polyfills
+ 'node_modules/core-js/client/shim.js',
+
+ // zone.js
+ 'node_modules/zone.js/dist/zone.js',
+ 'node_modules/zone.js/dist/long-stack-trace-zone.js',
+ 'node_modules/zone.js/dist/proxy.js',
+ 'node_modules/zone.js/dist/sync-test.js',
+ 'node_modules/zone.js/dist/jasmine-patch.js',
+ 'node_modules/zone.js/dist/async-test.js',
+ 'node_modules/zone.js/dist/fake-async-test.js',
+
+ // RxJs
+ { pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
+ { pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
+
+ // Paths loaded via module imports:
+ // Angular itself
+ { pattern: 'node_modules/@angular/**/*.js', included: false, watched: false },
+ { pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: false },
+
+ { pattern: appBase + '/systemjs.config.js', included: false, watched: false },
+ { pattern: appBase + '/systemjs.config.extras.js', included: false, watched: false },
+ 'karma-test-shim.js', // optionally extend SystemJS mapping e.g., with barrels
+
+ // transpiled application & spec code paths loaded via module imports
+ { pattern: appBase + '**/*.js', included: false, watched: true },
+ { pattern: testingBase + '**/*.js', included: false, watched: true },
+
+
+ // Asset (HTML & CSS) paths loaded via Angular's component compiler
+ // (these paths need to be rewritten, see proxies section)
+ { pattern: appBase + '**/*.html', included: false, watched: true },
+ { pattern: appBase + '**/*.css', included: false, watched: true },
+
+ // Paths for debugging with source maps in dev tools
+ { pattern: appBase + '**/*.ts', included: false, watched: false },
+ { pattern: appBase + '**/*.js.map', included: false, watched: false },
+ { pattern: testingSrcBase + '**/*.ts', included: false, watched: false },
+ { pattern: testingBase + '**/*.js.map', included: false, watched: false}
+ ],
+
+ // Proxied base paths for loading assets
+ proxies: {
+ // required for modules fetched by SystemJS
+ '/base/src/node_modules/': '/base/node_modules/'
+ },
+
+ exclude: [],
+ preprocessors: {},
+ reporters: ['progress', 'kjhtml'],
+
+ port: 9876,
+ colors: true,
+ logLevel: config.LOG_INFO,
+ autoWatch: true,
+ browsers: ['Chrome'],
+ singleRun: false
+ })
+}
diff --git a/public/docs/_examples/testing/ts/plnkr.json b/public/docs/_examples/testing/ts/plnkr.json
new file mode 100644
index 0000000000..899867159f
--- /dev/null
+++ b/public/docs/_examples/testing/ts/plnkr.json
@@ -0,0 +1,18 @@
+{
+ "description": "Heroes Test App",
+ "basePath": "src/",
+ "files":[
+ "styles.css",
+ "systemjs.config.extras.js",
+
+ "app/**/*.css",
+ "app/**/*.html",
+ "app/**/*.ts",
+
+ "!app/bag/*.*",
+
+ "main.ts",
+ "index.html"
+ ],
+ "tags": ["testing"]
+}
diff --git a/public/docs/_examples/testing/ts/src/1st-specs.html b/public/docs/_examples/testing/ts/src/1st-specs.html
new file mode 100644
index 0000000000..5876a65b9a
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/1st-specs.html
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ 1st Specs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/app-specs.html b/public/docs/_examples/testing/ts/src/app-specs.html
new file mode 100644
index 0000000000..7b7292ed7c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app-specs.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+ Sample App Specs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/app/1st.spec.ts b/public/docs/_examples/testing/ts/src/app/1st.spec.ts
new file mode 100644
index 0000000000..63f1ab134c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/1st.spec.ts
@@ -0,0 +1,5 @@
+// #docplaster
+// #docregion
+describe('1st tests', () => {
+ it('true is true', () => expect(true).toBe(true));
+});
diff --git a/public/docs/_examples/testing/ts/src/app/about.component.spec.ts b/public/docs/_examples/testing/ts/src/app/about.component.spec.ts
new file mode 100644
index 0000000000..0909e74434
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/about.component.spec.ts
@@ -0,0 +1,27 @@
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+
+import { AboutComponent } from './about.component';
+import { HighlightDirective } from './shared/highlight.directive';
+
+let fixture: ComponentFixture;
+
+describe('AboutComponent (highlightDirective)', () => {
+ // #docregion tests
+ beforeEach(() => {
+ fixture = TestBed.configureTestingModule({
+ declarations: [ AboutComponent, HighlightDirective],
+ schemas: [ NO_ERRORS_SCHEMA ]
+ })
+ .createComponent(AboutComponent);
+ fixture.detectChanges(); // initial binding
+ });
+
+ it('should have skyblue ', () => {
+ const de = fixture.debugElement.query(By.css('h2'));
+ const bgColor = de.nativeElement.style.backgroundColor;
+ expect(bgColor).toBe('skyblue');
+ });
+ // #enddocregion tests
+});
diff --git a/public/docs/_examples/testing/ts/src/app/about.component.ts b/public/docs/_examples/testing/ts/src/app/about.component.ts
new file mode 100644
index 0000000000..90e7132b4c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/about.component.ts
@@ -0,0 +1,9 @@
+// #docregion
+import { Component } from '@angular/core';
+@Component({
+ template: `
+ About
+
+ All about this sample
`
+})
+export class AboutComponent { }
diff --git a/public/docs/_examples/testing/ts/src/app/app-routing.module.ts b/public/docs/_examples/testing/ts/src/app/app-routing.module.ts
new file mode 100644
index 0000000000..6096a513df
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/app-routing.module.ts
@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { RouterModule } from '@angular/router';
+
+import { AboutComponent } from './about.component';
+
+@NgModule({
+ imports: [
+ RouterModule.forRoot([
+ { path: '', redirectTo: 'dashboard', pathMatch: 'full'},
+ { path: 'about', component: AboutComponent },
+ { path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule'}
+ ])
+ ],
+ exports: [ RouterModule ] // re-export the module declarations
+})
+export class AppRoutingModule { };
diff --git a/public/docs/_examples/testing/ts/src/app/app.component.html b/public/docs/_examples/testing/ts/src/app/app.component.html
new file mode 100644
index 0000000000..232bcebb6d
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/app.component.html
@@ -0,0 +1,11 @@
+
+
+
+
+
+ Dashboard
+ Heroes
+ About
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/app/app.component.router.spec.ts b/public/docs/_examples/testing/ts/src/app/app.component.router.spec.ts
new file mode 100644
index 0000000000..226d6d1ff3
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/app.component.router.spec.ts
@@ -0,0 +1,198 @@
+// For more examples:
+// https://github.com/angular/angular/blob/master/modules/@angular/router/test/integration.spec.ts
+
+import { async, ComponentFixture, fakeAsync, TestBed, tick,
+} from '@angular/core/testing';
+
+import { RouterTestingModule } from '@angular/router/testing';
+import { SpyLocation } from '@angular/common/testing';
+
+import { click } from '../testing';
+
+// r - for relatively obscure router symbols
+import * as r from '@angular/router';
+import { Router, RouterLinkWithHref } from '@angular/router';
+
+import { By } from '@angular/platform-browser';
+import { DebugElement, Type } from '@angular/core';
+import { Location } from '@angular/common';
+
+import { AppModule } from './app.module';
+import { AppComponent } from './app.component';
+import { AboutComponent } from './about.component';
+import { DashboardHeroComponent } from './dashboard/dashboard-hero.component';
+import { TwainService } from './shared/twain.service';
+
+let comp: AppComponent;
+let fixture: ComponentFixture;
+let page: Page;
+let router: Router;
+let location: SpyLocation;
+
+describe('AppComponent & RouterTestingModule', () => {
+
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ AppModule, RouterTestingModule ]
+ })
+ .compileComponents();
+ }));
+
+ it('should navigate to "Dashboard" immediately', fakeAsync(() => {
+ createComponent();
+ expect(location.path()).toEqual('/dashboard', 'after initialNavigation()');
+ expectElementOf(DashboardHeroComponent);
+ }));
+
+ it('should navigate to "About" on click', fakeAsync(() => {
+ createComponent();
+ click(page.aboutLinkDe);
+ // page.aboutLinkDe.nativeElement.click(); // ok but fails in phantom
+
+ advance();
+ expectPathToBe('/about');
+ expectElementOf(AboutComponent);
+
+ page.expectEvents([
+ [r.NavigationStart, '/about'], [r.RoutesRecognized, '/about'],
+ [r.NavigationEnd, '/about']
+ ]);
+ }));
+
+ it('should navigate to "About" w/ browser location URL change', fakeAsync(() => {
+ createComponent();
+ location.simulateHashChange('/about');
+ // location.go('/about'); // also works ... except in plunker
+ advance();
+ expectPathToBe('/about');
+ expectElementOf(AboutComponent);
+ }));
+
+ // Can't navigate to lazy loaded modules with this technique
+ xit('should navigate to "Heroes" on click', fakeAsync(() => {
+ createComponent();
+ page.heroesLinkDe.nativeElement.click();
+ advance();
+ expectPathToBe('/heroes');
+ }));
+
+});
+
+
+///////////////
+import { NgModuleFactoryLoader } from '@angular/core';
+import { SpyNgModuleFactoryLoader } from '@angular/router/testing';
+
+import { HeroModule } from './hero/hero.module'; // should be lazy loaded
+import { HeroListComponent } from './hero/hero-list.component';
+
+let loader: SpyNgModuleFactoryLoader;
+
+///////// Can't get lazy loaded Heroes to work yet
+xdescribe('AppComponent & Lazy Loading', () => {
+
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ AppModule, RouterTestingModule ]
+ })
+ .compileComponents();
+ }));
+
+ beforeEach(fakeAsync(() => {
+ createComponent();
+ loader = TestBed.get(NgModuleFactoryLoader);
+ loader.stubbedModules = {expected: HeroModule};
+ router.resetConfig([{path: 'heroes', loadChildren: 'expected'}]);
+ }));
+
+ it('dummy', () => expect(true).toBe(true) );
+
+
+ it('should navigate to "Heroes" on click', async(() => {
+ page.heroesLinkDe.nativeElement.click();
+ advance();
+ expectPathToBe('/heroes');
+ expectElementOf(HeroListComponent);
+ }));
+
+ xit('can navigate to "Heroes" w/ browser location URL change', fakeAsync(() => {
+ location.go('/heroes');
+ advance();
+ expectPathToBe('/heroes');
+ expectElementOf(HeroListComponent);
+
+ page.expectEvents([
+ [r.NavigationStart, '/heroes'], [r.RoutesRecognized, '/heroes'],
+ [r.NavigationEnd, '/heroes']
+ ]);
+ }));
+});
+
+////// Helpers /////////
+
+/** Wait a tick, then detect changes */
+function advance(): void {
+ tick();
+ fixture.detectChanges();
+}
+
+function createComponent() {
+ fixture = TestBed.createComponent(AppComponent);
+ comp = fixture.componentInstance;
+
+ const injector = fixture.debugElement.injector;
+ location = injector.get(Location) as SpyLocation;
+ router = injector.get(Router);
+ router.initialNavigation();
+ spyOn(injector.get(TwainService), 'getQuote')
+ .and.returnValue(Promise.resolve('Test Quote')); // fakes it
+
+ advance();
+
+ page = new Page();
+}
+
+class Page {
+ aboutLinkDe: DebugElement;
+ dashboardLinkDe: DebugElement;
+ heroesLinkDe: DebugElement;
+ recordedEvents: any[] = [];
+
+ // for debugging
+ comp: AppComponent;
+ location: SpyLocation;
+ router: Router;
+ fixture: ComponentFixture;
+
+ expectEvents(pairs: any[]) {
+ const events = this.recordedEvents;
+ expect(events.length).toEqual(pairs.length, 'actual/expected events length mismatch');
+ for (let i = 0; i < events.length; ++i) {
+ expect((events[i].constructor).name).toBe(pairs[i][0].name, 'unexpected event name');
+ expect((events[i]).url).toBe(pairs[i][1], 'unexpected event url');
+ }
+ }
+
+ constructor() {
+ router.events.subscribe(e => this.recordedEvents.push(e));
+ const links = fixture.debugElement.queryAll(By.directive(RouterLinkWithHref));
+ this.aboutLinkDe = links[2];
+ this.dashboardLinkDe = links[0];
+ this.heroesLinkDe = links[1];
+
+ // for debugging
+ this.comp = comp;
+ this.fixture = fixture;
+ this.router = router;
+ }
+}
+
+function expectPathToBe(path: string, expectationFailOutput?: any) {
+ expect(location.path()).toEqual(path, expectationFailOutput || 'location.path()');
+}
+
+function expectElementOf(type: Type): any {
+ const el = fixture.debugElement.query(By.directive(type));
+ expect(el).toBeTruthy('expected an element for ' + type.name);
+ return el;
+}
diff --git a/public/docs/_examples/testing/ts/src/app/app.component.spec.ts b/public/docs/_examples/testing/ts/src/app/app.component.spec.ts
new file mode 100644
index 0000000000..20c40767c0
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/app.component.spec.ts
@@ -0,0 +1,148 @@
+// #docplaster
+import { async, ComponentFixture, TestBed
+} from '@angular/core/testing';
+
+import { DebugElement } from '@angular/core';
+import { By } from '@angular/platform-browser';
+
+ // #docregion setup-schemas
+ import { NO_ERRORS_SCHEMA } from '@angular/core';
+ // #enddocregion setup-schemas
+ // #docregion setup-stubs-w-imports
+ import { Component } from '@angular/core';
+ // #docregion setup-schemas
+ import { AppComponent } from './app.component';
+ // #enddocregion setup-schemas
+ import { BannerComponent } from './banner.component';
+ import { RouterLinkStubDirective } from '../testing';
+ // #docregion setup-schemas
+ import { RouterOutletStubComponent } from '../testing';
+
+ // #enddocregion setup-schemas
+ @Component({selector: 'app-welcome', template: ''})
+ class WelcomeStubComponent {}
+
+ // #enddocregion setup-stubs-w-imports
+
+let comp: AppComponent;
+let fixture: ComponentFixture;
+
+describe('AppComponent & TestModule', () => {
+ // #docregion setup-stubs, setup-stubs-w-imports
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ declarations: [
+ AppComponent,
+ BannerComponent, WelcomeStubComponent,
+ RouterLinkStubDirective, RouterOutletStubComponent
+ ]
+ })
+
+ .compileComponents()
+ .then(() => {
+ fixture = TestBed.createComponent(AppComponent);
+ comp = fixture.componentInstance;
+ });
+ }));
+ // #enddocregion setup-stubs, setup-stubs-w-imports
+ tests();
+});
+
+//////// Testing w/ NO_ERRORS_SCHEMA //////
+describe('AppComponent & NO_ERRORS_SCHEMA', () => {
+ // #docregion setup-schemas
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ AppComponent, RouterLinkStubDirective ],
+ schemas: [ NO_ERRORS_SCHEMA ]
+ })
+
+ .compileComponents()
+ .then(() => {
+ fixture = TestBed.createComponent(AppComponent);
+ comp = fixture.componentInstance;
+ });
+ }));
+ // #enddocregion setup-schemas
+ tests();
+});
+
+//////// Testing w/ real root module //////
+// Tricky because we are disabling the router and its configuration
+// Better to use RouterTestingModule
+import { AppModule } from './app.module';
+import { AppRoutingModule } from './app-routing.module';
+
+describe('AppComponent & AppModule', () => {
+
+ beforeEach( async(() => {
+
+ TestBed.configureTestingModule({
+ imports: [ AppModule ]
+ })
+
+ // Get rid of app's Router configuration otherwise many failures.
+ // Doing so removes Router declarations; add the Router stubs
+ .overrideModule(AppModule, {
+ remove: {
+ imports: [ AppRoutingModule ]
+ },
+ add: {
+ declarations: [ RouterLinkStubDirective, RouterOutletStubComponent ]
+ }
+ })
+
+ .compileComponents()
+
+ .then(() => {
+ fixture = TestBed.createComponent(AppComponent);
+ comp = fixture.componentInstance;
+ });
+ }));
+
+ tests();
+});
+
+function tests() {
+ let links: RouterLinkStubDirective[];
+ let linkDes: DebugElement[];
+
+ // #docregion test-setup
+ beforeEach(() => {
+ // trigger initial data binding
+ fixture.detectChanges();
+
+ // find DebugElements with an attached RouterLinkStubDirective
+ linkDes = fixture.debugElement
+ .queryAll(By.directive(RouterLinkStubDirective));
+
+ // get the attached link directive instances using the DebugElement injectors
+ links = linkDes
+ .map(de => de.injector.get(RouterLinkStubDirective) as RouterLinkStubDirective);
+ });
+ // #enddocregion test-setup
+
+ it('can instantiate it', () => {
+ expect(comp).not.toBeNull();
+ });
+
+ // #docregion tests
+ it('can get RouterLinks from template', () => {
+ expect(links.length).toBe(3, 'should have 3 links');
+ expect(links[0].linkParams).toBe('/dashboard', '1st link should go to Dashboard');
+ expect(links[1].linkParams).toBe('/heroes', '1st link should go to Heroes');
+ });
+
+ it('can click Heroes link in template', () => {
+ const heroesLinkDe = linkDes[1];
+ const heroesLink = links[1];
+
+ expect(heroesLink.navigatedTo).toBeNull('link should not have navigated yet');
+
+ heroesLinkDe.triggerEventHandler('click', null);
+ fixture.detectChanges();
+
+ expect(heroesLink.navigatedTo).toBe('/heroes');
+ });
+ // #enddocregion tests
+}
diff --git a/public/docs/_examples/testing/ts/src/app/app.component.ts b/public/docs/_examples/testing/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..5bd535a113
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/app.component.ts
@@ -0,0 +1,7 @@
+// #docregion
+import { Component } from '@angular/core';
+@Component({
+ selector: 'my-app',
+ templateUrl: './app.component.html'
+})
+export class AppComponent { }
diff --git a/public/docs/_examples/testing/ts/src/app/app.module.ts b/public/docs/_examples/testing/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..d3c288ad11
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/app.module.ts
@@ -0,0 +1,29 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+
+import { AppComponent } from './app.component';
+import { AppRoutingModule } from './app-routing.module';
+
+import { AboutComponent } from './about.component';
+import { BannerComponent } from './banner.component';
+import { HeroService,
+ UserService } from './model';
+import { TwainService } from './shared/twain.service';
+import { WelcomeComponent } from './welcome.component';
+
+
+import { DashboardModule } from './dashboard/dashboard.module';
+import { SharedModule } from './shared/shared.module';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ DashboardModule,
+ AppRoutingModule,
+ SharedModule
+ ],
+ providers: [ HeroService, TwainService, UserService ],
+ declarations: [ AppComponent, AboutComponent, BannerComponent, WelcomeComponent ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/testing/ts/src/app/bag/async-helper.spec.ts b/public/docs/_examples/testing/ts/src/app/bag/async-helper.spec.ts
new file mode 100644
index 0000000000..12c8a4fbbd
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/bag/async-helper.spec.ts
@@ -0,0 +1,68 @@
+// tslint:disable-next-line:no-unused-variable
+import { async, fakeAsync, tick } from '@angular/core/testing';
+
+import { Observable } from 'rxjs/Observable';
+
+describe('Angular async helper', () => {
+ let actuallyDone = false;
+
+ beforeEach(() => { actuallyDone = false; });
+
+ afterEach(() => { expect(actuallyDone).toBe(true, 'actuallyDone should be true'); });
+
+ it('should run normal test', () => { actuallyDone = true; });
+
+ it('should run normal async test', (done: DoneFn) => {
+ setTimeout(() => {
+ actuallyDone = true;
+ done();
+ }, 0);
+ });
+
+ it('should run async test with task',
+ async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
+
+ it('should run async test with successful promise', async(() => {
+ const p = new Promise(resolve => { setTimeout(resolve, 10); });
+ p.then(() => { actuallyDone = true; });
+ }));
+
+ it('should run async test with failed promise', async(() => {
+ const p = new Promise((resolve, reject) => { setTimeout(reject, 10); });
+ p.catch(() => { actuallyDone = true; });
+ }));
+
+ // Use done. Cannot use setInterval with async or fakeAsync
+ // See https://github.com/angular/angular/issues/10127
+ it('should run async test with successful delayed Observable', (done: any) => {
+ const source = Observable.of(true).delay(10);
+ source.subscribe(
+ val => actuallyDone = true,
+ err => fail(err),
+ done
+ );
+ });
+
+ // Cannot use setInterval from within an async zone test
+ // See https://github.com/angular/angular/issues/10127
+ // xit('should run async test with successful delayed Observable', async(() => {
+ // const source = Observable.of(true).delay(10);
+ // source.subscribe(
+ // val => actuallyDone = true,
+ // err => fail(err)
+ // );
+ // }));
+
+ // // Fail message: Error: 1 periodic timer(s) still in the queue
+ // // See https://github.com/angular/angular/issues/10127
+ // xit('should run async test with successful delayed Observable', fakeAsync(() => {
+ // const source = Observable.of(true).delay(10);
+ // source.subscribe(
+ // val => actuallyDone = true,
+ // err => fail(err)
+ // );
+
+ // tick();
+ // }));
+
+});
diff --git a/public/docs/_examples/testing/ts/src/app/bag/bag-external-template.html b/public/docs/_examples/testing/ts/src/app/bag/bag-external-template.html
new file mode 100644
index 0000000000..4c2b23755f
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/bag/bag-external-template.html
@@ -0,0 +1 @@
+from external template
diff --git a/public/docs/_examples/testing/ts/src/app/bag/bag-main.ts b/public/docs/_examples/testing/ts/src/app/bag/bag-main.ts
new file mode 100644
index 0000000000..27b78200ae
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/bag/bag-main.ts
@@ -0,0 +1,5 @@
+// main app entry point
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { BagModule } from './bag';
+
+platformBrowserDynamic().bootstrapModule(BagModule);
diff --git a/public/docs/_examples/testing/ts/src/app/bag/bag.no-testbed.spec.ts b/public/docs/_examples/testing/ts/src/app/bag/bag.no-testbed.spec.ts
new file mode 100644
index 0000000000..f29672ecf6
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/bag/bag.no-testbed.spec.ts
@@ -0,0 +1,130 @@
+// #docplaster
+import { DependentService, FancyService } from './bag';
+
+///////// Fakes /////////
+export class FakeFancyService extends FancyService {
+ value: string = 'faked value';
+}
+////////////////////////
+// #docregion FancyService
+// Straight Jasmine - no imports from Angular test libraries
+
+describe('FancyService without the TestBed', () => {
+ let service: FancyService;
+
+ beforeEach(() => { service = new FancyService(); });
+
+ it('#getValue should return real value', () => {
+ expect(service.getValue()).toBe('real value');
+ });
+
+ it('#getAsyncValue should return async value', (done: DoneFn) => {
+ service.getAsyncValue().then(value => {
+ expect(value).toBe('async value');
+ done();
+ });
+ });
+
+ // #docregion getTimeoutValue
+ it('#getTimeoutValue should return timeout value', (done: DoneFn) => {
+ service = new FancyService();
+ service.getTimeoutValue().then(value => {
+ expect(value).toBe('timeout value');
+ done();
+ });
+ });
+ // #enddocregion getTimeoutValue
+
+ it('#getObservableValue should return observable value', (done: DoneFn) => {
+ service.getObservableValue().subscribe(value => {
+ expect(value).toBe('observable value');
+ done();
+ });
+ });
+
+});
+// #enddocregion FancyService
+
+// DependentService requires injection of a FancyService
+// #docregion DependentService
+describe('DependentService without the TestBed', () => {
+ let service: DependentService;
+
+ it('#getValue should return real value by way of the real FancyService', () => {
+ service = new DependentService(new FancyService());
+ expect(service.getValue()).toBe('real value');
+ });
+
+ it('#getValue should return faked value by way of a fakeService', () => {
+ service = new DependentService(new FakeFancyService());
+ expect(service.getValue()).toBe('faked value');
+ });
+
+ it('#getValue should return faked value from a fake object', () => {
+ const fake = { getValue: () => 'fake value' };
+ service = new DependentService(fake as FancyService);
+ expect(service.getValue()).toBe('fake value');
+ });
+
+ it('#getValue should return stubbed value from a FancyService spy', () => {
+ const fancy = new FancyService();
+ const stubValue = 'stub value';
+ const spy = spyOn(fancy, 'getValue').and.returnValue(stubValue);
+ service = new DependentService(fancy);
+
+ expect(service.getValue()).toBe(stubValue, 'service returned stub value');
+ expect(spy.calls.count()).toBe(1, 'stubbed method was called once');
+ expect(spy.calls.mostRecent().returnValue).toBe(stubValue);
+ });
+});
+// #enddocregion DependentService
+
+// #docregion ReversePipe
+import { ReversePipe } from './bag';
+
+describe('ReversePipe', () => {
+ let pipe: ReversePipe;
+
+ beforeEach(() => { pipe = new ReversePipe(); });
+
+ it('transforms "abc" to "cba"', () => {
+ expect(pipe.transform('abc')).toBe('cba');
+ });
+
+ it('no change to palindrome: "able was I ere I saw elba"', () => {
+ const palindrome = 'able was I ere I saw elba';
+ expect(pipe.transform(palindrome)).toBe(palindrome);
+ });
+
+});
+// #enddocregion ReversePipe
+
+
+import { ButtonComponent } from './bag';
+// #docregion ButtonComp
+describe('ButtonComp', () => {
+ let comp: ButtonComponent;
+ beforeEach(() => comp = new ButtonComponent());
+
+ it('#isOn should be false initially', () => {
+ expect(comp.isOn).toBe(false);
+ });
+
+ it('#clicked() should set #isOn to true', () => {
+ comp.clicked();
+ expect(comp.isOn).toBe(true);
+ });
+
+ it('#clicked() should set #message to "is on"', () => {
+ comp.clicked();
+ expect(comp.message).toMatch(/is on/i);
+ });
+
+ it('#clicked() should toggle #isOn', () => {
+ comp.clicked();
+ expect(comp.isOn).toBe(true);
+ comp.clicked();
+ expect(comp.isOn).toBe(false);
+ });
+});
+// #enddocregion ButtonComp
diff --git a/public/docs/_examples/testing/ts/src/app/bag/bag.spec.ts b/public/docs/_examples/testing/ts/src/app/bag/bag.spec.ts
new file mode 100644
index 0000000000..d6fea7ca94
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/bag/bag.spec.ts
@@ -0,0 +1,681 @@
+// #docplaster
+import {
+ BagModule,
+ BankAccountComponent, BankAccountParentComponent,
+ ButtonComponent,
+ Child1Component, Child2Component, Child3Component,
+ FancyService,
+ ExternalTemplateComponent,
+ InputComponent,
+ IoComponent, IoParentComponent,
+ MyIfComponent, MyIfChildComponent, MyIfParentComponent,
+ NeedsContentComponent, ParentComponent,
+ TestProvidersComponent, TestViewProvidersComponent,
+ ReversePipeComponent, ShellComponent
+} from './bag';
+
+import { By } from '@angular/platform-browser';
+import { Component,
+ DebugElement,
+ Injectable } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+
+// Forms symbols imported only for a specific test below
+import { NgModel, NgControl } from '@angular/forms';
+
+import { async, ComponentFixture, fakeAsync, inject, TestBed, tick
+} from '@angular/core/testing';
+
+import { addMatchers, newEvent, click } from '../../testing';
+
+beforeEach( addMatchers );
+
+//////// Service Tests /////////////
+// #docregion FancyService
+describe('use inject helper in beforeEach', () => {
+ let service: FancyService;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({ providers: [FancyService] });
+
+ // `TestBed.get` returns the injectable or an
+ // alternative object (including null) if the service provider is not found.
+ // Of course it will be found in this case because we're providing it.
+ // #docregion testbed-get
+ service = TestBed.get(FancyService, null);
+ // #enddocregion testbed-get
+ });
+
+ it('should use FancyService', () => {
+ expect(service.getValue()).toBe('real value');
+ });
+
+ it('should use FancyService', () => {
+ expect(service.getValue()).toBe('real value');
+ });
+
+ it('test should wait for FancyService.getAsyncValue', async(() => {
+ service.getAsyncValue().then(
+ value => expect(value).toBe('async value')
+ );
+ }));
+
+ it('test should wait for FancyService.getTimeoutValue', async(() => {
+ service.getTimeoutValue().then(
+ value => expect(value).toBe('timeout value')
+ );
+ }));
+
+ it('test should wait for FancyService.getObservableValue', async(() => {
+ service.getObservableValue().subscribe(
+ value => expect(value).toBe('observable value')
+ );
+ }));
+
+ // Must use done. See https://github.com/angular/angular/issues/10127
+ it('test should wait for FancyService.getObservableDelayValue', (done: DoneFn) => {
+ service.getObservableDelayValue().subscribe(value => {
+ expect(value).toBe('observable delay value');
+ done();
+ });
+ });
+
+ it('should allow the use of fakeAsync', fakeAsync(() => {
+ let value: any;
+ service.getAsyncValue().then((val: any) => value = val);
+ tick(); // Trigger JS engine cycle until all promises resolve.
+ expect(value).toBe('async value');
+ }));
+});
+// #enddocregion FancyService
+
+describe('use inject within `it`', () => {
+ // #docregion getTimeoutValue
+ beforeEach(() => {
+ TestBed.configureTestingModule({ providers: [FancyService] });
+ });
+
+ // #enddocregion getTimeoutValue
+
+ it('should use modified providers',
+ inject([FancyService], (service: FancyService) => {
+ service.setValue('value modified in beforeEach');
+ expect(service.getValue()).toBe('value modified in beforeEach');
+ })
+ );
+
+ // #docregion getTimeoutValue
+ it('test should wait for FancyService.getTimeoutValue',
+ async(inject([FancyService], (service: FancyService) => {
+
+ service.getTimeoutValue().then(
+ value => expect(value).toBe('timeout value')
+ );
+ })));
+ // #enddocregion getTimeoutValue
+});
+
+describe('using async(inject) within beforeEach', () => {
+ let serviceValue: string;
+
+ beforeEach(() => {
+ TestBed.configureTestingModule({ providers: [FancyService] });
+ });
+
+ beforeEach( async(inject([FancyService], (service: FancyService) => {
+ service.getAsyncValue().then(value => serviceValue = value);
+ })));
+
+ it('should use asynchronously modified value ... in synchronous test', () => {
+ expect(serviceValue).toBe('async value');
+ });
+});
+
+
+/////////// Component Tests //////////////////
+
+describe('TestBed Component Tests', () => {
+
+ beforeEach( async(() => {
+ TestBed
+ .configureTestingModule({
+ imports: [BagModule],
+ })
+ // Compile everything in BagModule
+ .compileComponents();
+ }));
+
+ it('should create a component with inline template', () => {
+ const fixture = TestBed.createComponent(Child1Component);
+ fixture.detectChanges();
+
+ expect(fixture).toHaveText('Child');
+ });
+
+ it('should create a component with external template', () => {
+ const fixture = TestBed.createComponent(ExternalTemplateComponent);
+ fixture.detectChanges();
+
+ expect(fixture).toHaveText('from external template');
+ });
+
+ it('should allow changing members of the component', () => {
+ const fixture = TestBed.createComponent(MyIfComponent);
+
+ fixture.detectChanges();
+ expect(fixture).toHaveText('MyIf()');
+
+ fixture.componentInstance.showMore = true;
+ fixture.detectChanges();
+ expect(fixture).toHaveText('MyIf(More)');
+ });
+
+ it('should create a nested component bound to inputs/outputs', () => {
+ const fixture = TestBed.createComponent(IoParentComponent);
+
+ fixture.detectChanges();
+ const heroes = fixture.debugElement.queryAll(By.css('.hero'));
+ expect(heroes.length).toBeGreaterThan(0, 'has heroes');
+
+ const comp = fixture.componentInstance;
+ const hero = comp.heroes[0];
+
+ click(heroes[0]);
+ fixture.detectChanges();
+
+ const selected = fixture.debugElement.query(By.css('p'));
+ expect(selected).toHaveText(hero.name);
+ });
+
+ it('can access the instance variable of an `*ngFor` row component', () => {
+ const fixture = TestBed.createComponent(IoParentComponent);
+ const comp = fixture.componentInstance;
+ const heroName = comp.heroes[0].name; // first hero's name
+
+ fixture.detectChanges();
+ const ngForRow = fixture.debugElement.query(By.directive(IoComponent)); // first hero ngForRow
+
+ const hero = ngForRow.context['hero']; // the hero object passed into the row
+ expect(hero.name).toBe(heroName, 'ngRow.context.hero');
+
+ const rowComp = ngForRow.componentInstance;
+ // jasmine.any is an "instance-of-type" test.
+ expect(rowComp).toEqual(jasmine.any(IoComponent), 'component is IoComp');
+ expect(rowComp.hero.name).toBe(heroName, 'component.hero');
+ });
+
+
+ // #docregion ButtonComp
+ it('should support clicking a button', () => {
+ const fixture = TestBed.createComponent(ButtonComponent);
+ const btn = fixture.debugElement.query(By.css('button'));
+ const span = fixture.debugElement.query(By.css('span')).nativeElement;
+
+ fixture.detectChanges();
+ expect(span.textContent).toMatch(/is off/i, 'before click');
+
+ click(btn);
+ fixture.detectChanges();
+ expect(span.textContent).toMatch(/is on/i, 'after click');
+ });
+ // #enddocregion ButtonComp
+
+ // ngModel is async so we must wait for it with promise-based `whenStable`
+ it('should support entering text in input box (ngModel)', async(() => {
+ const expectedOrigName = 'John';
+ const expectedNewName = 'Sally';
+
+ const fixture = TestBed.createComponent(InputComponent);
+ fixture.detectChanges();
+
+ const comp = fixture.componentInstance;
+ const input = fixture.debugElement.query(By.css('input')).nativeElement;
+
+ expect(comp.name).toBe(expectedOrigName,
+ `At start name should be ${expectedOrigName} `);
+
+ // wait until ngModel binds comp.name to input box
+ fixture.whenStable().then(() => {
+ expect(input.value).toBe(expectedOrigName,
+ `After ngModel updates input box, input.value should be ${expectedOrigName} `);
+
+ // simulate user entering new name in input
+ input.value = expectedNewName;
+
+ // that change doesn't flow to the component immediately
+ expect(comp.name).toBe(expectedOrigName,
+ `comp.name should still be ${expectedOrigName} after value change, before binding happens`);
+
+ // dispatch a DOM event so that Angular learns of input value change.
+ // then wait while ngModel pushes input.box value to comp.name
+ input.dispatchEvent(newEvent('input'));
+ return fixture.whenStable();
+ })
+ .then(() => {
+ expect(comp.name).toBe(expectedNewName,
+ `After ngModel updates the model, comp.name should be ${expectedNewName} `);
+ });
+ }));
+
+ // fakeAsync version of ngModel input test enables sync test style
+ // synchronous `tick` replaces asynchronous promise-base `whenStable`
+ it('should support entering text in input box (ngModel) - fakeAsync', fakeAsync(() => {
+ const expectedOrigName = 'John';
+ const expectedNewName = 'Sally';
+
+ const fixture = TestBed.createComponent(InputComponent);
+ fixture.detectChanges();
+
+ const comp = fixture.componentInstance;
+ const input = fixture.debugElement.query(By.css('input')).nativeElement;
+
+ expect(comp.name).toBe(expectedOrigName,
+ `At start name should be ${expectedOrigName} `);
+
+ // wait until ngModel binds comp.name to input box
+ tick();
+ expect(input.value).toBe(expectedOrigName,
+ `After ngModel updates input box, input.value should be ${expectedOrigName} `);
+
+ // simulate user entering new name in input
+ input.value = expectedNewName;
+
+ // that change doesn't flow to the component immediately
+ expect(comp.name).toBe(expectedOrigName,
+ `comp.name should still be ${expectedOrigName} after value change, before binding happens`);
+
+ // dispatch a DOM event so that Angular learns of input value change.
+ // then wait a tick while ngModel pushes input.box value to comp.name
+ input.dispatchEvent(newEvent('input'));
+ tick();
+ expect(comp.name).toBe(expectedNewName,
+ `After ngModel updates the model, comp.name should be ${expectedNewName} `);
+ }));
+
+ // #docregion ReversePipeComp
+ it('ReversePipeComp should reverse the input text', fakeAsync(() => {
+ const inputText = 'the quick brown fox.';
+ const expectedText = '.xof nworb kciuq eht';
+
+ const fixture = TestBed.createComponent(ReversePipeComponent);
+ fixture.detectChanges();
+
+ const comp = fixture.componentInstance;
+ const input = fixture.debugElement.query(By.css('input')).nativeElement as HTMLInputElement;
+ const span = fixture.debugElement.query(By.css('span')).nativeElement as HTMLElement;
+
+ // simulate user entering new name in input
+ input.value = inputText;
+
+ // dispatch a DOM event so that Angular learns of input value change.
+ // then wait a tick while ngModel pushes input.box value to comp.text
+ // and Angular updates the output span
+ input.dispatchEvent(newEvent('input'));
+ tick();
+ fixture.detectChanges();
+ expect(span.textContent).toBe(expectedText, 'output span');
+ expect(comp.text).toBe(inputText, 'component.text');
+ }));
+ // #enddocregion ReversePipeComp
+
+ // Use this technique to find attached directives of any kind
+ it('can examine attached directives and listeners', () => {
+ const fixture = TestBed.createComponent(InputComponent);
+ fixture.detectChanges();
+
+ const inputEl = fixture.debugElement.query(By.css('input'));
+
+ expect(inputEl.providerTokens).toContain(NgModel, 'NgModel directive');
+
+ const ngControl = inputEl.injector.get(NgControl);
+ expect(ngControl).toEqual(jasmine.any(NgControl), 'NgControl directive');
+
+ expect(inputEl.listeners.length).toBeGreaterThan(2, 'several listeners attached');
+ });
+
+ // #docregion dom-attributes
+ it('BankAccountComponent should set attributes, styles, classes, and properties', () => {
+ const fixture = TestBed.createComponent(BankAccountParentComponent);
+ fixture.detectChanges();
+ const comp = fixture.componentInstance;
+
+ // the only child is debugElement of the BankAccount component
+ const el = fixture.debugElement.children[0];
+ const childComp = el.componentInstance as BankAccountComponent;
+ expect(childComp).toEqual(jasmine.any(BankAccountComponent));
+
+ expect(el.context).toBe(childComp, 'context is the child component');
+
+ expect(el.attributes['account']).toBe(childComp.id, 'account attribute');
+ expect(el.attributes['bank']).toBe(childComp.bank, 'bank attribute');
+
+ expect(el.classes['closed']).toBe(true, 'closed class');
+ expect(el.classes['open']).toBe(false, 'open class');
+
+ expect(el.styles['color']).toBe(comp.color, 'color style');
+ expect(el.styles['width']).toBe(comp.width + 'px', 'width style');
+ // #enddocregion dom-attributes
+
+ // Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future?
+ // expect(el.properties['customProperty']).toBe(true, 'customProperty');
+
+ // #docregion dom-attributes
+ });
+ // #enddocregion dom-attributes
+
+
+});
+
+describe('TestBed Component Overrides:', () => {
+
+ it('should override ChildComp\'s template', () => {
+
+ const fixture = TestBed.configureTestingModule({
+ declarations: [Child1Component],
+ })
+ .overrideComponent(Child1Component, {
+ set: { template: 'Fake ' }
+ })
+ .createComponent(Child1Component);
+
+ fixture.detectChanges();
+ expect(fixture).toHaveText('Fake');
+ });
+
+ it('should override TestProvidersComp\'s FancyService provider', () => {
+ const fixture = TestBed.configureTestingModule({
+ declarations: [TestProvidersComponent],
+ })
+ .overrideComponent(TestProvidersComponent, {
+ remove: { providers: [FancyService]},
+ add: { providers: [{ provide: FancyService, useClass: FakeFancyService }] },
+
+ // Or replace them all (this component has only one provider)
+ // set: { providers: [{ provide: FancyService, useClass: FakeFancyService }] },
+ })
+ .createComponent(TestProvidersComponent);
+
+ fixture.detectChanges();
+ expect(fixture).toHaveText('injected value: faked value', 'text');
+
+ // Explore the providerTokens
+ const tokens = fixture.debugElement.providerTokens;
+ expect(tokens).toContain(fixture.componentInstance.constructor, 'component ctor');
+ expect(tokens).toContain(TestProvidersComponent, 'TestProvidersComp');
+ expect(tokens).toContain(FancyService, 'FancyService');
+ });
+
+ it('should override TestViewProvidersComp\'s FancyService viewProvider', () => {
+ const fixture = TestBed.configureTestingModule({
+ declarations: [TestViewProvidersComponent],
+ })
+ .overrideComponent(TestViewProvidersComponent, {
+ // remove: { viewProviders: [FancyService]},
+ // add: { viewProviders: [{ provide: FancyService, useClass: FakeFancyService }] },
+
+ // Or replace them all (this component has only one viewProvider)
+ set: { viewProviders: [{ provide: FancyService, useClass: FakeFancyService }] },
+ })
+ .createComponent(TestViewProvidersComponent);
+
+ fixture.detectChanges();
+ expect(fixture).toHaveText('injected value: faked value');
+ });
+
+ it('injected provider should not be same as component\'s provider', () => {
+
+ // TestComponent is parent of TestProvidersComponent
+ @Component({ template: ' ' })
+ class TestComponent {}
+
+ // 3 levels of FancyService provider: module, TestCompomponent, TestProvidersComponent
+ const fixture = TestBed.configureTestingModule({
+ declarations: [TestComponent, TestProvidersComponent],
+ providers: [FancyService]
+ })
+ .overrideComponent(TestComponent, {
+ set: { providers: [{ provide: FancyService, useValue: {} }] }
+ })
+ .overrideComponent(TestProvidersComponent, {
+ set: { providers: [{ provide: FancyService, useClass: FakeFancyService }] }
+ })
+ .createComponent(TestComponent);
+
+ let testBedProvider: FancyService;
+ let tcProvider: {};
+ let tpcProvider: FakeFancyService;
+
+ // `inject` uses TestBed's injector
+ inject([FancyService], (s: FancyService) => testBedProvider = s)();
+ tcProvider = fixture.debugElement.injector.get(FancyService);
+ tpcProvider = fixture.debugElement.children[0].injector.get(FancyService) as FakeFancyService;
+
+ expect(testBedProvider).not.toBe(tcProvider, 'testBed/tc not same providers');
+ expect(testBedProvider).not.toBe(tpcProvider, 'testBed/tpc not same providers');
+
+ expect(testBedProvider instanceof FancyService).toBe(true, 'testBedProvider is FancyService');
+ expect(tcProvider).toEqual({}, 'tcProvider is {}');
+ expect(tpcProvider instanceof FakeFancyService).toBe(true, 'tpcProvider is FakeFancyService');
+ });
+
+ it('can access template local variables as references', () => {
+ const fixture = TestBed.configureTestingModule({
+ declarations: [ShellComponent, NeedsContentComponent, Child1Component, Child2Component, Child3Component],
+ })
+ .overrideComponent(ShellComponent, {
+ set: {
+ selector: 'test-shell',
+ template: `
+
+
+
+
+
+ !
+
+ `
+ }
+ })
+ .createComponent(ShellComponent);
+
+ fixture.detectChanges();
+
+ // NeedsContentComp is the child of ShellComp
+ const el = fixture.debugElement.children[0];
+ const comp = el.componentInstance;
+
+ expect(comp.children.toArray().length).toBe(4,
+ 'three different child components and an ElementRef with #content');
+
+ expect(el.references['nc']).toBe(comp, '#nc reference to component');
+
+ // #docregion custom-predicate
+ // Filter for DebugElements with a #content reference
+ const contentRefs = el.queryAll( de => de.references['content']);
+ // #enddocregion custom-predicate
+ expect(contentRefs.length).toBe(4, 'elements w/ a #content reference');
+ });
+
+});
+
+describe('Nested (one-deep) component override', () => {
+
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ParentComponent, FakeChildComponent]
+ })
+ .compileComponents();
+ }));
+
+ it('ParentComp should use Fake Child component', () => {
+ const fixture = TestBed.createComponent(ParentComponent);
+ fixture.detectChanges();
+ expect(fixture).toHaveText('Parent(Fake Child)');
+ });
+});
+
+describe('Nested (two-deep) component override', () => {
+
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ParentComponent, FakeChildWithGrandchildComponent, FakeGrandchildComponent]
+ })
+ .compileComponents();
+ }));
+
+ it('should use Fake Grandchild component', () => {
+ const fixture = TestBed.createComponent(ParentComponent);
+ fixture.detectChanges();
+ expect(fixture).toHaveText('Parent(Fake Child(Fake Grandchild))');
+ });
+});
+
+describe('Lifecycle hooks w/ MyIfParentComp', () => {
+ let fixture: ComponentFixture;
+ let parent: MyIfParentComponent;
+ let child: MyIfChildComponent;
+
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [FormsModule],
+ declarations: [MyIfChildComponent, MyIfParentComponent]
+ })
+ .compileComponents().then(() => {
+ fixture = TestBed.createComponent(MyIfParentComponent);
+ parent = fixture.componentInstance;
+ });
+ }));
+
+ it('should instantiate parent component', () => {
+ expect(parent).not.toBeNull('parent component should exist');
+ });
+
+ it('parent component OnInit should NOT be called before first detectChanges()', () => {
+ expect(parent.ngOnInitCalled).toBe(false);
+ });
+
+ it('parent component OnInit should be called after first detectChanges()', () => {
+ fixture.detectChanges();
+ expect(parent.ngOnInitCalled).toBe(true);
+ });
+
+ it('child component should exist after OnInit', () => {
+ fixture.detectChanges();
+ getChild();
+ expect(child instanceof MyIfChildComponent).toBe(true, 'should create child');
+ });
+
+ it('should have called child component\'s OnInit ', () => {
+ fixture.detectChanges();
+ getChild();
+ expect(child.ngOnInitCalled).toBe(true);
+ });
+
+ it('child component called OnChanges once', () => {
+ fixture.detectChanges();
+ getChild();
+ expect(child.ngOnChangesCounter).toBe(1);
+ });
+
+ it('changed parent value flows to child', () => {
+ fixture.detectChanges();
+ getChild();
+
+ parent.parentValue = 'foo';
+ fixture.detectChanges();
+
+ expect(child.ngOnChangesCounter).toBe(2,
+ 'expected 2 changes: initial value and changed value');
+ expect(child.childValue).toBe('foo',
+ 'childValue should eq changed parent value');
+ });
+
+ // must be async test to see child flow to parent
+ it('changed child value flows to parent', async(() => {
+ fixture.detectChanges();
+ getChild();
+
+ child.childValue = 'bar';
+
+ return new Promise(resolve => {
+ // Wait one JS engine turn!
+ setTimeout(() => resolve(), 0);
+ })
+ .then(() => {
+ fixture.detectChanges();
+
+ expect(child.ngOnChangesCounter).toBe(2,
+ 'expected 2 changes: initial value and changed value');
+ expect(parent.parentValue).toBe('bar',
+ 'parentValue should eq changed parent value');
+ });
+
+ }));
+
+ it('clicking "Close Child" triggers child OnDestroy', () => {
+ fixture.detectChanges();
+ getChild();
+
+ const btn = fixture.debugElement.query(By.css('button'));
+ click(btn);
+
+ fixture.detectChanges();
+ expect(child.ngOnDestroyCalled).toBe(true);
+ });
+
+ ////// helpers ///
+ /**
+ * Get the MyIfChildComp from parent; fail w/ good message if cannot.
+ */
+ function getChild() {
+
+ let childDe: DebugElement; // DebugElement that should hold the MyIfChildComp
+
+ // The Hard Way: requires detailed knowledge of the parent template
+ try {
+ childDe = fixture.debugElement.children[4].children[0];
+ } catch (err) { /* we'll report the error */ }
+
+ // DebugElement.queryAll: if we wanted all of many instances:
+ childDe = fixture.debugElement
+ .queryAll(function (de) { return de.componentInstance instanceof MyIfChildComponent; })[0];
+
+ // WE'LL USE THIS APPROACH !
+ // DebugElement.query: find first instance (if any)
+ childDe = fixture.debugElement
+ .query(function (de) { return de.componentInstance instanceof MyIfChildComponent; });
+
+ if (childDe && childDe.componentInstance) {
+ child = childDe.componentInstance;
+ } else {
+ fail('Unable to find MyIfChildComp within MyIfParentComp');
+ }
+
+ return child;
+ }
+});
+
+////////// Fakes ///////////
+
+@Component({
+ selector: 'child-1',
+ template: `Fake Child`
+})
+class FakeChildComponent { }
+
+@Component({
+ selector: 'child-1',
+ template: `Fake Child( )`
+})
+class FakeChildWithGrandchildComponent { }
+
+@Component({
+ selector: 'grandchild-1',
+ template: `Fake Grandchild`
+})
+class FakeGrandchildComponent { }
+
+@Injectable()
+class FakeFancyService extends FancyService {
+ value: string = 'faked value';
+}
diff --git a/public/docs/_examples/testing/ts/src/app/bag/bag.ts b/public/docs/_examples/testing/ts/src/app/bag/bag.ts
new file mode 100644
index 0000000000..cbf1c21136
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/bag/bag.ts
@@ -0,0 +1,454 @@
+/* tslint:disable:forin */
+import { Component, ContentChildren, Directive, EventEmitter,
+ Injectable, Input, Output, Optional,
+ HostBinding, HostListener,
+ OnInit, OnChanges, OnDestroy,
+ Pipe, PipeTransform,
+ SimpleChange } from '@angular/core';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+import 'rxjs/add/operator/delay';
+
+////////// The App: Services and Components for the tests. //////////////
+
+export class Hero {
+ name: string;
+}
+
+////////// Services ///////////////
+// #docregion FancyService
+@Injectable()
+export class FancyService {
+ protected value: string = 'real value';
+
+ getValue() { return this.value; }
+ setValue(value: string) { this.value = value; }
+
+ getAsyncValue() { return Promise.resolve('async value'); }
+
+ getObservableValue() { return Observable.of('observable value'); }
+
+ getTimeoutValue() {
+ return new Promise((resolve) => {
+ setTimeout(() => { resolve('timeout value'); }, 10);
+ });
+ }
+
+ getObservableDelayValue() {
+ return Observable.of('observable delay value').delay(10);
+ }
+}
+// #enddocregion FancyService
+
+// #docregion DependentService
+@Injectable()
+export class DependentService {
+ constructor(private dependentService: FancyService) { }
+ getValue() { return this.dependentService.getValue(); }
+}
+// #enddocregion DependentService
+
+/////////// Pipe ////////////////
+/*
+ * Reverse the input string.
+*/
+// #docregion ReversePipe
+@Pipe({ name: 'reverse' })
+export class ReversePipe implements PipeTransform {
+ transform(s: string) {
+ let r = '';
+ for (let i = s.length; i; ) { r += s[--i]; };
+ return r;
+ }
+}
+// #enddocregion ReversePipe
+
+//////////// Components /////////////
+@Component({
+ selector: 'bank-account',
+ template: `
+ Bank Name: {{bank}}
+ Account Id: {{id}}
+ `
+})
+export class BankAccountComponent {
+ @Input() bank: string;
+ @Input('account') id: string;
+
+ // Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future?
+ // constructor(private renderer: Renderer, private el: ElementRef ) {
+ // renderer.setElementProperty(el.nativeElement, 'customProperty', true);
+ // }
+}
+
+/** A component with attributes, styles, classes, and property setting */
+@Component({
+ selector: 'bank-account-parent',
+ template: `
+
+
+ `
+})
+export class BankAccountParentComponent {
+ width = 200;
+ color = 'red';
+ isClosed = true;
+}
+
+// #docregion ButtonComp
+@Component({
+ selector: 'button-comp',
+ template: `
+ Click me!
+ {{message}} `
+})
+export class ButtonComponent {
+ isOn = false;
+ clicked() { this.isOn = !this.isOn; }
+ get message() { return `The light is ${this.isOn ? 'On' : 'Off'}`; }
+}
+// #enddocregion ButtonComp
+
+@Component({
+ selector: 'child-1',
+ template: `Child-1({{text}}) `
+})
+export class Child1Component {
+ @Input() text = 'Original';
+}
+
+@Component({
+ selector: 'child-2',
+ template: 'Child-2({{text}})
'
+})
+export class Child2Component {
+ @Input() text: string;
+}
+
+@Component({
+ selector: 'child-3',
+ template: 'Child-3({{text}})
'
+})
+export class Child3Component {
+ @Input() text: string;
+}
+
+@Component({
+ selector: 'input-comp',
+ template: ` `
+})
+export class InputComponent {
+ name = 'John';
+}
+
+/* Prefer this metadata syntax */
+// @Directive({
+// selector: 'input[value]',
+// host: {
+// '[value]': 'value',
+// '(input)': 'valueChange.emit($event.target.value)'
+// },
+// inputs: ['value'],
+// outputs: ['valueChange']
+// })
+// export class InputValueBinderDirective {
+// value: any;
+// valueChange: EventEmitter = new EventEmitter();
+// }
+
+// As the style-guide recommends
+@Directive({ selector: 'input[value]' })
+export class InputValueBinderDirective {
+ @HostBinding()
+ @Input()
+ value: any;
+
+ @Output()
+ valueChange: EventEmitter = new EventEmitter();
+
+ @HostListener('input', ['$event.target.value'])
+ onInput(value: any) { this.valueChange.emit(value); }
+}
+
+@Component({
+ selector: 'input-value-comp',
+ template: `
+ Name: {{name}}
+ `
+})
+export class InputValueBinderComponent {
+ name = 'Sally'; // initial value
+}
+
+@Component({
+ selector: 'parent-comp',
+ template: `Parent( )`
+})
+export class ParentComponent { }
+
+@Component({
+ selector: 'io-comp',
+ template: `Original {{hero.name}}
`
+})
+export class IoComponent {
+ @Input() hero: Hero;
+ @Output() selected = new EventEmitter();
+ click() { this.selected.emit(this.hero); }
+}
+
+@Component({
+ selector: 'io-parent-comp',
+ template: `
+ Click to select a hero
+ The selected hero is {{selectedHero.name}}
+
+
+ `
+})
+export class IoParentComponent {
+ heroes: Hero[] = [ {name: 'Bob'}, {name: 'Carol'}, {name: 'Ted'}, {name: 'Alice'} ];
+ selectedHero: Hero;
+ onSelect(hero: Hero) { this.selectedHero = hero; }
+}
+
+@Component({
+ selector: 'my-if-comp',
+ template: `MyIf(More )`
+})
+export class MyIfComponent {
+ showMore = false;
+}
+
+@Component({
+ selector: 'my-service-comp',
+ template: `injected value: {{fancyService.value}}`,
+ providers: [FancyService]
+})
+export class TestProvidersComponent {
+ constructor(private fancyService: FancyService) {}
+}
+
+
+@Component({
+ selector: 'my-service-comp',
+ template: `injected value: {{fancyService.value}}`,
+ viewProviders: [FancyService]
+})
+export class TestViewProvidersComponent {
+ constructor(private fancyService: FancyService) {}
+}
+
+@Component({
+ selector: 'external-template-comp',
+ templateUrl: './bag-external-template.html'
+})
+export class ExternalTemplateComponent implements OnInit {
+ serviceValue: string;
+
+ constructor(@Optional() private service: FancyService) { }
+
+ ngOnInit() {
+ if (this.service) { this.serviceValue = this.service.getValue(); }
+ }
+}
+
+@Component({
+ selector: 'comp-w-ext-comp',
+ template: `
+ comp-w-ext-comp
+
+ `
+})
+export class InnerCompWithExternalTemplateComponent { }
+
+@Component({
+ selector: 'bad-template-comp',
+ templateUrl: './non-existant.html'
+})
+export class BadTemplateUrlComponent { }
+
+
+
+@Component({selector: 'needs-content', template: ' '})
+export class NeedsContentComponent {
+ // children with #content local variable
+ @ContentChildren('content') children: any;
+}
+
+///////// MyIfChildComp ////////
+@Component({
+ selector: 'my-if-child-1',
+
+ template: `
+ MyIfChildComp
+
+ Child value:
+
+ Change log:
+ {{i + 1}} - {{log}}
`
+})
+export class MyIfChildComponent implements OnInit, OnChanges, OnDestroy {
+ @Input() value = '';
+ @Output() valueChange = new EventEmitter();
+
+ get childValue() { return this.value; }
+ set childValue(v: string) {
+ if (this.value === v) { return; }
+ this.value = v;
+ this.valueChange.emit(v);
+ }
+
+ changeLog: string[] = [];
+
+ ngOnInitCalled = false;
+ ngOnChangesCounter = 0;
+ ngOnDestroyCalled = false;
+
+ ngOnInit() {
+ this.ngOnInitCalled = true;
+ this.changeLog.push('ngOnInit called');
+ }
+
+ ngOnDestroy() {
+ this.ngOnDestroyCalled = true;
+ this.changeLog.push('ngOnDestroy called');
+ }
+
+ ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
+ for (let propName in changes) {
+ this.ngOnChangesCounter += 1;
+ let prop = changes[propName];
+ let cur = JSON.stringify(prop.currentValue);
+ let prev = JSON.stringify(prop.previousValue);
+ this.changeLog.push(`${propName}: currentValue = ${cur}, previousValue = ${prev}`);
+ }
+ }
+}
+
+///////// MyIfParentComp ////////
+
+@Component({
+ selector: 'my-if-parent-comp',
+ template: `
+ MyIfParentComp
+ Parent value:
+
+
+ {{toggleLabel}} Child
+
+
+
+ `
+})
+export class MyIfParentComponent implements OnInit {
+ ngOnInitCalled = false;
+ parentValue = 'Hello, World';
+ showChild = false;
+ toggleLabel = 'Unknown';
+
+ ngOnInit() {
+ this.ngOnInitCalled = true;
+ this.clicked();
+ }
+
+ clicked() {
+ this.showChild = !this.showChild;
+ this.toggleLabel = this.showChild ? 'Close' : 'Show';
+ }
+}
+
+
+@Component({
+ selector: 'reverse-pipe-comp',
+ template: `
+
+ {{text | reverse}}
+ `
+})
+export class ReversePipeComponent {
+ text = 'my dog has fleas.';
+}
+
+@Component({template: 'Replace Me
'})
+export class ShellComponent { }
+
+@Component({
+ selector: 'bag-comp',
+ template: `
+ Specs Bag
+
+
+ Input/Output Component
+
+
+ External Template Component
+
+
+ Component With External Template Component
+
+
+ Reverse Pipe
+
+
+ InputValueBinder Directive
+
+
+ Button Component
+
+
+ Needs Content
+
+
+
+
+
+ !
+
+ `
+})
+export class BagComponent { }
+//////// Aggregations ////////////
+
+export const bagDeclarations = [
+ BagComponent,
+ BankAccountComponent, BankAccountParentComponent,
+ ButtonComponent,
+ Child1Component, Child2Component, Child3Component,
+ ExternalTemplateComponent, InnerCompWithExternalTemplateComponent,
+ InputComponent,
+ InputValueBinderDirective, InputValueBinderComponent,
+ IoComponent, IoParentComponent,
+ MyIfComponent, MyIfChildComponent, MyIfParentComponent,
+ NeedsContentComponent, ParentComponent,
+ TestProvidersComponent, TestViewProvidersComponent,
+ ReversePipe, ReversePipeComponent, ShellComponent
+];
+
+export const bagProviders = [DependentService, FancyService];
+
+////////////////////
+////////////
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+@NgModule({
+ imports: [BrowserModule, FormsModule],
+ declarations: bagDeclarations,
+ providers: bagProviders,
+ entryComponents: [BagComponent],
+ bootstrap: [BagComponent]
+})
+export class BagModule { }
+
diff --git a/public/docs/_examples/testing/ts/src/app/banner-inline.component.spec.ts b/public/docs/_examples/testing/ts/src/app/banner-inline.component.spec.ts
new file mode 100644
index 0000000000..7f35d6b956
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner-inline.component.spec.ts
@@ -0,0 +1,55 @@
+// #docplaster
+// #docregion imports
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { BannerComponent } from './banner-inline.component';
+// #enddocregion imports
+
+// #docregion setup
+describe('BannerComponent (inline template)', () => {
+
+ let comp: BannerComponent;
+ let fixture: ComponentFixture;
+ let de: DebugElement;
+ let el: HTMLElement;
+
+// #docregion before-each
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [ BannerComponent ], // declare the test component
+ });
+
+ fixture = TestBed.createComponent(BannerComponent);
+
+ comp = fixture.componentInstance; // BannerComponent test instance
+
+ // query for the title by CSS element selector
+ de = fixture.debugElement.query(By.css('h1'));
+ el = de.nativeElement;
+ });
+// #enddocregion before-each
+// #enddocregion setup
+
+ // #docregion test-w-o-detect-changes
+ it('no title in the DOM until manually call `detectChanges`', () => {
+ expect(el.textContent).toEqual('');
+ });
+ // #enddocregion test-w-o-detect-changes
+
+ // #docregion tests
+ it('should display original title', () => {
+ fixture.detectChanges();
+ expect(el.textContent).toContain(comp.title);
+ });
+
+ it('should display a different test title', () => {
+ comp.title = 'Test Title';
+ fixture.detectChanges();
+ expect(el.textContent).toContain('Test Title');
+ });
+ // #enddocregion tests
+// #docregion setup
+});
+// #enddocregion setup
diff --git a/public/docs/_examples/testing/ts/src/app/banner-inline.component.ts b/public/docs/_examples/testing/ts/src/app/banner-inline.component.ts
new file mode 100644
index 0000000000..7ec4ef6999
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner-inline.component.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-banner',
+ template: '{{title}} '
+})
+export class BannerComponent {
+ title = 'Test Tour of Heroes';
+}
+
diff --git a/public/docs/_examples/testing/ts/src/app/banner.component.css b/public/docs/_examples/testing/ts/src/app/banner.component.css
new file mode 100644
index 0000000000..cd09a55b3c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner.component.css
@@ -0,0 +1 @@
+h1 { color: green; font-size: 350%}
diff --git a/public/docs/_examples/testing/ts/src/app/banner.component.detect-changes.spec.ts b/public/docs/_examples/testing/ts/src/app/banner.component.detect-changes.spec.ts
new file mode 100644
index 0000000000..412f5be586
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner.component.detect-changes.spec.ts
@@ -0,0 +1,59 @@
+// #docplaster
+// #docregion
+// #docregion import-async
+import { async } from '@angular/core/testing';
+// #enddocregion import-async
+// #docregion import-ComponentFixtureAutoDetect
+import { ComponentFixtureAutoDetect } from '@angular/core/testing';
+// #enddocregion import-ComponentFixtureAutoDetect
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { BannerComponent } from './banner.component';
+
+describe('BannerComponent (AutoChangeDetect)', () => {
+ let comp: BannerComponent;
+ let fixture: ComponentFixture;
+ let de: DebugElement;
+ let el: HTMLElement;
+
+ beforeEach(async(() => {
+ // #docregion auto-detect
+ TestBed.configureTestingModule({
+ declarations: [ BannerComponent ],
+ providers: [
+ { provide: ComponentFixtureAutoDetect, useValue: true }
+ ]
+ })
+ // #enddocregion auto-detect
+ .compileComponents();
+ }));
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(BannerComponent);
+ comp = fixture.componentInstance;
+ de = fixture.debugElement.query(By.css('h1'));
+ el = de.nativeElement;
+ });
+
+ // #docregion auto-detect-tests
+ it('should display original title', () => {
+ // Hooray! No `fixture.detectChanges()` needed
+ expect(el.textContent).toContain(comp.title);
+ });
+
+ it('should still see original title after comp.title change', () => {
+ const oldTitle = comp.title;
+ comp.title = 'Test Title';
+ // Displayed title is old because Angular didn't hear the change :(
+ expect(el.textContent).toContain(oldTitle);
+ });
+
+ it('should display updated title after detectChanges', () => {
+ comp.title = 'Test Title';
+ fixture.detectChanges(); // detect changes explicitly
+ expect(el.textContent).toContain(comp.title);
+ });
+ // #enddocregion auto-detect-tests
+});
diff --git a/public/docs/_examples/testing/ts/src/app/banner.component.html b/public/docs/_examples/testing/ts/src/app/banner.component.html
new file mode 100644
index 0000000000..6213adcb47
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner.component.html
@@ -0,0 +1 @@
+{{title}}
diff --git a/public/docs/_examples/testing/ts/src/app/banner.component.spec.ts b/public/docs/_examples/testing/ts/src/app/banner.component.spec.ts
new file mode 100644
index 0000000000..a564c45c85
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner.component.spec.ts
@@ -0,0 +1,53 @@
+// #docplaster
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { BannerComponent } from './banner.component';
+
+describe('BannerComponent (templateUrl)', () => {
+
+ let comp: BannerComponent;
+ let fixture: ComponentFixture;
+ let de: DebugElement;
+ let el: HTMLElement;
+
+ // #docregion async-before-each
+ // async beforeEach
+ beforeEach(async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ BannerComponent ], // declare the test component
+ })
+ .compileComponents(); // compile template and css
+ }));
+ // #enddocregion async-before-each
+
+ // #docregion sync-before-each
+ // synchronous beforeEach
+ beforeEach(() => {
+ fixture = TestBed.createComponent(BannerComponent);
+
+ comp = fixture.componentInstance; // BannerComponent test instance
+
+ // query for the title by CSS element selector
+ de = fixture.debugElement.query(By.css('h1'));
+ el = de.nativeElement;
+ });
+ // #enddocregion sync-before-each
+
+ it('no title in the DOM until manually call `detectChanges`', () => {
+ expect(el.textContent).toEqual('');
+ });
+
+ it('should display original title', () => {
+ fixture.detectChanges();
+ expect(el.textContent).toContain(comp.title);
+ });
+
+ it('should display a different test title', () => {
+ comp.title = 'Test Title';
+ fixture.detectChanges();
+ expect(el.textContent).toContain('Test Title');
+ });
+
+});
diff --git a/public/docs/_examples/testing/ts/src/app/banner.component.ts b/public/docs/_examples/testing/ts/src/app/banner.component.ts
new file mode 100644
index 0000000000..4355a40867
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/banner.component.ts
@@ -0,0 +1,12 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'app-banner',
+ templateUrl: './banner.component.html',
+ styleUrls: ['./banner.component.css']
+})
+export class BannerComponent {
+ title = 'Test Tour of Heroes';
+}
+
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.css b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.css
new file mode 100644
index 0000000000..eb54d181d8
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.css
@@ -0,0 +1,28 @@
+.hero {
+ padding: 20px;
+ position: relative;
+ text-align: center;
+ color: #eee;
+ max-height: 120px;
+ min-width: 120px;
+ background-color: #607D8B;
+ border-radius: 2px;
+}
+
+.hero:hover {
+ background-color: #EEE;
+ cursor: pointer;
+ color: #607d8b;
+}
+
+@media (max-width: 600px) {
+ .hero {
+ font-size: 10px;
+ max-height: 75px; }
+}
+
+@media (max-width: 1024px) {
+ .hero {
+ min-width: 60px;
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.html b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.html
new file mode 100644
index 0000000000..ff49bd17a5
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.html
@@ -0,0 +1,4 @@
+
+
+ {{hero.name | uppercase}}
+
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.spec.ts b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.spec.ts
new file mode 100644
index 0000000000..40c01571e6
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.spec.ts
@@ -0,0 +1,124 @@
+import { async, ComponentFixture, TestBed
+} from '@angular/core/testing';
+
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { addMatchers, click } from '../../testing';
+
+import { Hero } from '../model/hero';
+import { DashboardHeroComponent } from './dashboard-hero.component';
+
+beforeEach( addMatchers );
+
+describe('DashboardHeroComponent when tested directly', () => {
+
+ let comp: DashboardHeroComponent;
+ let expectedHero: Hero;
+ let fixture: ComponentFixture;
+ let heroEl: DebugElement;
+
+ // #docregion setup, compile-components
+ // async beforeEach
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DashboardHeroComponent ],
+ })
+ .compileComponents(); // compile template and css
+ }));
+ // #enddocregion compile-components
+
+ // synchronous beforeEach
+ beforeEach(() => {
+ fixture = TestBed.createComponent(DashboardHeroComponent);
+ comp = fixture.componentInstance;
+ heroEl = fixture.debugElement.query(By.css('.hero')); // find hero element
+
+ // pretend that it was wired to something that supplied a hero
+ expectedHero = new Hero(42, 'Test Name');
+ comp.hero = expectedHero;
+ fixture.detectChanges(); // trigger initial data binding
+ });
+ // #enddocregion setup
+
+ // #docregion name-test
+ it('should display hero name', () => {
+ const expectedPipedName = expectedHero.name.toUpperCase();
+ expect(heroEl.nativeElement.textContent).toContain(expectedPipedName);
+ });
+ // #enddocregion name-test
+
+ // #docregion click-test
+ it('should raise selected event when clicked', () => {
+ let selectedHero: Hero;
+ comp.selected.subscribe((hero: Hero) => selectedHero = hero);
+
+ // #docregion trigger-event-handler
+ heroEl.triggerEventHandler('click', null);
+ // #enddocregion trigger-event-handler
+ expect(selectedHero).toBe(expectedHero);
+ });
+ // #enddocregion click-test
+
+ // #docregion click-test-2
+ it('should raise selected event when clicked', () => {
+ let selectedHero: Hero;
+ comp.selected.subscribe((hero: Hero) => selectedHero = hero);
+
+ click(heroEl); // triggerEventHandler helper
+ expect(selectedHero).toBe(expectedHero);
+ });
+ // #enddocregion click-test-2
+});
+
+//////////////////
+
+describe('DashboardHeroComponent when inside a test host', () => {
+ let testHost: TestHostComponent;
+ let fixture: ComponentFixture;
+ let heroEl: DebugElement;
+
+ // #docregion test-host-setup
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DashboardHeroComponent, TestHostComponent ], // declare both
+ }).compileComponents();
+ }));
+
+ beforeEach(() => {
+ // create TestHostComponent instead of DashboardHeroComponent
+ fixture = TestBed.createComponent(TestHostComponent);
+ testHost = fixture.componentInstance;
+ heroEl = fixture.debugElement.query(By.css('.hero')); // find hero
+ fixture.detectChanges(); // trigger initial data binding
+ });
+ // #enddocregion test-host-setup
+
+ // #docregion test-host-tests
+ it('should display hero name', () => {
+ const expectedPipedName = testHost.hero.name.toUpperCase();
+ expect(heroEl.nativeElement.textContent).toContain(expectedPipedName);
+ });
+
+ it('should raise selected event when clicked', () => {
+ click(heroEl);
+ // selected hero should be the same data bound hero
+ expect(testHost.selectedHero).toBe(testHost.hero);
+ });
+ // #enddocregion test-host-tests
+});
+
+////// Test Host Component //////
+import { Component } from '@angular/core';
+
+// #docregion test-host
+@Component({
+ template: `
+ `
+})
+class TestHostComponent {
+ hero = new Hero(42, 'Test Name');
+ selectedHero: Hero;
+ onSelected(hero: Hero) { this.selectedHero = hero; }
+}
+// #enddocregion test-host
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.ts b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.ts
new file mode 100644
index 0000000000..9f6be16d7d
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard-hero.component.ts
@@ -0,0 +1,17 @@
+// #docregion
+import { Component, EventEmitter, Input, Output } from '@angular/core';
+
+import { Hero } from '../model';
+
+// #docregion component
+@Component({
+ selector: 'dashboard-hero',
+ templateUrl: './dashboard-hero.component.html',
+ styleUrls: [ './dashboard-hero.component.css' ]
+})
+export class DashboardHeroComponent {
+ @Input() hero: Hero;
+ @Output() selected = new EventEmitter();
+ click() { this.selected.emit(this.hero); }
+}
+// #enddocregion component
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.css b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.css
new file mode 100644
index 0000000000..98130b61c6
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.css
@@ -0,0 +1,35 @@
+[class*='col-'] {
+ float: left;
+}
+*, *:after, *:before {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+h3 {
+ text-align: center; margin-bottom: 0;
+}
+[class*='col-'] {
+ padding-right: 20px;
+ padding-bottom: 20px;
+}
+[class*='col-']:last-of-type {
+ padding-right: 0;
+}
+.grid {
+ margin: 0;
+}
+.col-1-4 {
+ width: 25%;
+}
+.grid-pad {
+ padding: 10px 0;
+}
+.grid-pad > [class*='col-']:last-of-type {
+ padding-right: 20px;
+}
+@media (max-width: 1024px) {
+ .grid {
+ margin: 0;
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.html b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.html
new file mode 100644
index 0000000000..b26e16b828
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.html
@@ -0,0 +1,9 @@
+{{title}}
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.no-testbed.spec.ts b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.no-testbed.spec.ts
new file mode 100644
index 0000000000..125e5193b7
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.no-testbed.spec.ts
@@ -0,0 +1,57 @@
+import { Router } from '@angular/router';
+
+import { DashboardComponent } from './dashboard.component';
+import { Hero } from '../model';
+
+import { addMatchers } from '../../testing';
+import { FakeHeroService } from '../model/testing';
+
+class FakeRouter {
+ navigateByUrl(url: string) { return url; }
+}
+
+describe('DashboardComponent: w/o Angular TestBed', () => {
+ let comp: DashboardComponent;
+ let heroService: FakeHeroService;
+ let router: Router;
+
+ beforeEach(() => {
+ addMatchers();
+ router = new FakeRouter() as any as Router;
+ heroService = new FakeHeroService();
+ comp = new DashboardComponent(router, heroService);
+ });
+
+ it('should NOT have heroes before calling OnInit', () => {
+ expect(comp.heroes.length).toBe(0,
+ 'should not have heroes before OnInit');
+ });
+
+ it('should NOT have heroes immediately after OnInit', () => {
+ comp.ngOnInit(); // ngOnInit -> getHeroes
+ expect(comp.heroes.length).toBe(0,
+ 'should not have heroes until service promise resolves');
+ });
+
+ it('should HAVE heroes after HeroService gets them', (done: DoneFn) => {
+ comp.ngOnInit(); // ngOnInit -> getHeroes
+ heroService.lastPromise // the one from getHeroes
+ .then(() => {
+ // throw new Error('deliberate error'); // see it fail gracefully
+ expect(comp.heroes.length).toBeGreaterThan(0,
+ 'should have heroes after service promise resolves');
+ })
+ .then(done, done.fail);
+ });
+
+ it('should tell ROUTER to navigate by hero id', () => {
+ const hero = new Hero(42, 'Abbracadabra');
+ const spy = spyOn(router, 'navigateByUrl');
+
+ comp.gotoDetail(hero);
+
+ const navArgs = spy.calls.mostRecent().args[0];
+ expect(navArgs).toBe('/heroes/42', 'should nav to HeroDetail for Hero 42');
+ });
+
+});
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.spec.ts b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.spec.ts
new file mode 100644
index 0000000000..0b0f9e213a
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.spec.ts
@@ -0,0 +1,147 @@
+// #docplaster
+import { async, inject, ComponentFixture, TestBed
+} from '@angular/core/testing';
+
+import { addMatchers, click } from '../../testing';
+import { HeroService } from '../model';
+import { FakeHeroService } from '../model/testing';
+
+import { By } from '@angular/platform-browser';
+import { Router } from '@angular/router';
+
+import { DashboardComponent } from './dashboard.component';
+import { DashboardModule } from './dashboard.module';
+
+// #docregion router-stub
+class RouterStub {
+ navigateByUrl(url: string) { return url; }
+}
+// #enddocregion router-stub
+
+beforeEach ( addMatchers );
+
+let comp: DashboardComponent;
+let fixture: ComponentFixture;
+
+//////// Deep ////////////////
+
+describe('DashboardComponent (deep)', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ imports: [ DashboardModule ]
+ });
+ });
+
+ compileAndCreate();
+
+ tests(clickForDeep);
+
+ function clickForDeep() {
+ // get first DebugElement
+ const heroEl = fixture.debugElement.query(By.css('.hero'));
+ click(heroEl);
+ }
+});
+
+//////// Shallow ////////////////
+
+import { NO_ERRORS_SCHEMA } from '@angular/core';
+
+describe('DashboardComponent (shallow)', () => {
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [ DashboardComponent ],
+ schemas: [NO_ERRORS_SCHEMA]
+ });
+ });
+
+ compileAndCreate();
+
+ tests(clickForShallow);
+
+ function clickForShallow() {
+ // get first
DebugElement
+ const heroEl = fixture.debugElement.query(By.css('dashboard-hero'));
+ heroEl.triggerEventHandler('selected', comp.heroes[0]);
+ }
+});
+
+/** Add TestBed providers, compile, and create DashboardComponent */
+function compileAndCreate() {
+ // #docregion compile-and-create-body
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ providers: [
+ { provide: HeroService, useClass: FakeHeroService },
+ { provide: Router, useClass: RouterStub }
+ ]
+ })
+ .compileComponents().then(() => {
+ fixture = TestBed.createComponent(DashboardComponent);
+ comp = fixture.componentInstance;
+ });
+ // #enddocregion compile-and-create-body
+ }));
+}
+
+/**
+ * The (almost) same tests for both.
+ * Only change: the way that the first hero is clicked
+ */
+function tests(heroClick: Function) {
+
+ it('should NOT have heroes before ngOnInit', () => {
+ expect(comp.heroes.length).toBe(0,
+ 'should not have heroes before ngOnInit');
+ });
+
+ it('should NOT have heroes immediately after ngOnInit', () => {
+ fixture.detectChanges(); // runs initial lifecycle hooks
+
+ expect(comp.heroes.length).toBe(0,
+ 'should not have heroes until service promise resolves');
+ });
+
+ describe('after get dashboard heroes', () => {
+
+ // Trigger component so it gets heroes and binds to them
+ beforeEach( async(() => {
+ fixture.detectChanges(); // runs ngOnInit -> getHeroes
+ fixture.whenStable() // No need for the `lastPromise` hack!
+ .then(() => fixture.detectChanges()); // bind to heroes
+ }));
+
+ it('should HAVE heroes', () => {
+ expect(comp.heroes.length).toBeGreaterThan(0,
+ 'should have heroes after service promise resolves');
+ });
+
+ it('should DISPLAY heroes', () => {
+ // Find and examine the displayed heroes
+ // Look for them in the DOM by css class
+ const heroes = fixture.debugElement.queryAll(By.css('dashboard-hero'));
+ expect(heroes.length).toBe(4, 'should display 4 heroes');
+ });
+
+ // #docregion navigate-test, inject
+ it('should tell ROUTER to navigate when hero clicked',
+ inject([Router], (router: Router) => { // ...
+ // #enddocregion inject
+
+ const spy = spyOn(router, 'navigateByUrl');
+
+ heroClick(); // trigger click on first inner
+
+ // args passed to router.navigateByUrl()
+ const navArgs = spy.calls.first().args[0];
+
+ // expecting to navigate to id of the component's first hero
+ const id = comp.heroes[0].id;
+ expect(navArgs).toBe('/heroes/' + id,
+ 'should nav to HeroDetail for first hero');
+ // #docregion inject
+ }));
+ // #enddocregion navigate-test, inject
+ });
+}
+
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.ts b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.ts
new file mode 100644
index 0000000000..3edaa0c0ab
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.component.ts
@@ -0,0 +1,40 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Hero, HeroService } from '../model';
+
+@Component({
+ selector: 'app-dashboard',
+ templateUrl: './dashboard.component.html',
+ styleUrls: [ './dashboard.component.css' ]
+})
+export class DashboardComponent implements OnInit {
+
+ heroes: Hero[] = [];
+
+ // #docregion ctor
+ constructor(
+ private router: Router,
+ private heroService: HeroService) {
+ }
+ // #enddocregion ctor
+
+ ngOnInit() {
+ this.heroService.getHeroes()
+ .then(heroes => this.heroes = heroes.slice(1, 5));
+ }
+
+ // #docregion goto-detail
+ gotoDetail(hero: Hero) {
+ let url = `/heroes/${hero.id}`;
+ this.router.navigateByUrl(url);
+ }
+ // #enddocregion goto-detail
+
+ get title() {
+ let cnt = this.heroes.length;
+ return cnt === 0 ? 'No Heroes' :
+ cnt === 1 ? 'Top Hero' : `Top ${cnt} Heroes`;
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.module.ts b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.module.ts
new file mode 100644
index 0000000000..8a70c851a0
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/dashboard/dashboard.module.ts
@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { SharedModule } from '../shared/shared.module';
+
+import { DashboardComponent } from './dashboard.component';
+import { DashboardHeroComponent } from './dashboard-hero.component';
+
+const routes: Routes = [
+ { path: 'dashboard', component: DashboardComponent },
+];
+
+@NgModule({
+ imports: [
+ SharedModule,
+ RouterModule.forChild(routes)
+ ],
+ declarations: [ DashboardComponent, DashboardHeroComponent ]
+})
+export class DashboardModule { }
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.css b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.css
new file mode 100644
index 0000000000..f6139ba274
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.css
@@ -0,0 +1,29 @@
+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;
+}
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.html b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.html
new file mode 100644
index 0000000000..7e86a668f6
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.html
@@ -0,0 +1,12 @@
+
+
+
{{hero.name | titlecase}} Details
+
+ id: {{hero.id}}
+
+ name:
+
+
+
Save
+
Cancel
+
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.no-testbed.spec.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.no-testbed.spec.ts
new file mode 100644
index 0000000000..422dae6f77
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.no-testbed.spec.ts
@@ -0,0 +1,58 @@
+import { HeroDetailComponent } from './hero-detail.component';
+import { Hero } from '../model';
+
+import { ActivatedRouteStub } from '../../testing';
+
+////////// Tests ////////////////////
+
+describe('HeroDetailComponent - no TestBed', () => {
+ let activatedRoute: ActivatedRouteStub;
+ let comp: HeroDetailComponent;
+ let expectedHero: Hero;
+ let hds: any;
+ let router: any;
+
+ beforeEach((done: any) => {
+ expectedHero = new Hero(42, 'Bubba');
+ activatedRoute = new ActivatedRouteStub();
+ activatedRoute.testParams = { id: expectedHero.id };
+
+ router = jasmine.createSpyObj('router', ['navigate']);
+
+ hds = jasmine.createSpyObj('HeroDetailService', ['getHero', 'saveHero']);
+ hds.getHero.and.returnValue(Promise.resolve(expectedHero));
+ hds.saveHero.and.returnValue(Promise.resolve(expectedHero));
+
+ comp = new HeroDetailComponent(hds,
activatedRoute, router);
+ comp.ngOnInit();
+
+ // OnInit calls HDS.getHero; wait for it to get the fake hero
+ hds.getHero.calls.first().returnValue.then(done);
+ });
+
+ it('should expose the hero retrieved from the service', () => {
+ expect(comp.hero).toBe(expectedHero);
+ });
+
+ it('should navigate when click cancel', () => {
+ comp.cancel();
+ expect(router.navigate.calls.any()).toBe(true, 'router.navigate called');
+ });
+
+ it('should save when click save', () => {
+ comp.save();
+ expect(hds.saveHero.calls.any()).toBe(true, 'HeroDetailService.save called');
+ expect(router.navigate.calls.any()).toBe(false, 'router.navigate not called yet');
+ });
+
+ it('should navigate when click save resolves', (done: any) => {
+ comp.save();
+ // waits for async save to complete before navigating
+ hds.saveHero.calls.first().returnValue
+ .then(() => {
+ expect(router.navigate.calls.any()).toBe(true, 'router.navigate called');
+ done();
+ });
+ });
+
+});
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.spec.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.spec.ts
new file mode 100644
index 0000000000..80b6450ac5
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.spec.ts
@@ -0,0 +1,364 @@
+// #docplaster
+import {
+ async, ComponentFixture, fakeAsync, inject, TestBed, tick
+} from '@angular/core/testing';
+
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import {
+ ActivatedRoute, ActivatedRouteStub, click, newEvent, Router, RouterStub
+} from '../../testing';
+
+import { Hero } from '../model';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroDetailService } from './hero-detail.service';
+import { HeroModule } from './hero.module';
+
+////// Testing Vars //////
+let activatedRoute: ActivatedRouteStub;
+let comp: HeroDetailComponent;
+let fixture: ComponentFixture;
+let page: Page;
+
+////// Tests //////
+describe('HeroDetailComponent', () => {
+ beforeEach(() => {
+ activatedRoute = new ActivatedRouteStub();
+ });
+ describe('with HeroModule setup', heroModuleSetup);
+ describe('when override its provided HeroDetailService', overrideSetup);
+ describe('with FormsModule setup', formsModuleSetup);
+ describe('with SharedModule setup', sharedModuleSetup);
+});
+
+////////////////////
+function overrideSetup() {
+ // #docregion hds-spy
+ class HeroDetailServiceSpy {
+ testHero = new Hero(42, 'Test Hero');
+
+ getHero = jasmine.createSpy('getHero').and.callFake(
+ () => Promise
+ .resolve(true)
+ .then(() => Object.assign({}, this.testHero))
+ );
+
+ saveHero = jasmine.createSpy('saveHero').and.callFake(
+ (hero: Hero) => Promise
+ .resolve(true)
+ .then(() => Object.assign(this.testHero, hero))
+ );
+ }
+ // #enddocregion hds-spy
+
+ // the `id` value is irrelevant because ignored by service stub
+ beforeEach(() => activatedRoute.testParams = { id: 99999 } );
+
+ // #docregion setup-override
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ HeroModule ],
+ providers: [
+ { provide: ActivatedRoute, useValue: activatedRoute },
+ { provide: Router, useClass: RouterStub},
+ // #enddocregion setup-override
+ // HeroDetailService at this level is IRRELEVANT!
+ { provide: HeroDetailService, useValue: {} }
+ // #docregion setup-override
+ ]
+ })
+
+ // Override component's own provider
+ // #docregion override-component-method
+ .overrideComponent(HeroDetailComponent, {
+ set: {
+ providers: [
+ { provide: HeroDetailService, useClass: HeroDetailServiceSpy }
+ ]
+ }
+ })
+ // #enddocregion override-component-method
+
+ .compileComponents();
+ }));
+ // #enddocregion setup-override
+
+ // #docregion override-tests
+ let hdsSpy: HeroDetailServiceSpy;
+
+ beforeEach( async(() => {
+ createComponent();
+ // get the component's injected HeroDetailServiceSpy
+ hdsSpy = fixture.debugElement.injector.get(HeroDetailService) as any;
+ }));
+
+ it('should have called `getHero`', () => {
+ expect(hdsSpy.getHero.calls.count()).toBe(1, 'getHero called once');
+ });
+
+ it('should display stub hero\'s name', () => {
+ expect(page.nameDisplay.textContent).toBe(hdsSpy.testHero.name);
+ });
+
+ it('should save stub hero change', fakeAsync(() => {
+ const origName = hdsSpy.testHero.name;
+ const newName = 'New Name';
+
+ page.nameInput.value = newName;
+ page.nameInput.dispatchEvent(newEvent('input')); // tell Angular
+
+ expect(comp.hero.name).toBe(newName, 'component hero has new name');
+ expect(hdsSpy.testHero.name).toBe(origName, 'service hero unchanged before save');
+
+ click(page.saveBtn);
+ expect(hdsSpy.saveHero.calls.count()).toBe(1, 'saveHero called once');
+
+ tick(); // wait for async save to complete
+ expect(hdsSpy.testHero.name).toBe(newName, 'service hero has new name after save');
+ expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
+ }));
+ // #enddocregion override-tests
+
+ it('fixture injected service is not the component injected service',
+ inject([HeroDetailService], (service: HeroDetailService) => {
+
+ expect(service).toEqual({}, 'service injected from fixture');
+ expect(hdsSpy).toBeTruthy('service injected into component');
+ }));
+}
+
+////////////////////
+import { HEROES, FakeHeroService } from '../model/testing';
+import { HeroService } from '../model';
+
+const firstHero = HEROES[0];
+
+function heroModuleSetup() {
+ // #docregion setup-hero-module
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ HeroModule ],
+ // #enddocregion setup-hero-module
+ // declarations: [ HeroDetailComponent ], // NO! DOUBLE DECLARATION
+ // #docregion setup-hero-module
+ providers: [
+ { provide: ActivatedRoute, useValue: activatedRoute },
+ { provide: HeroService, useClass: FakeHeroService },
+ { provide: Router, useClass: RouterStub},
+ ]
+ })
+ .compileComponents();
+ }));
+ // #enddocregion setup-hero-module
+
+ // #docregion route-good-id
+ describe('when navigate to existing hero', () => {
+ let expectedHero: Hero;
+
+ beforeEach( async(() => {
+ expectedHero = firstHero;
+ activatedRoute.testParams = { id: expectedHero.id };
+ createComponent();
+ }));
+
+ // #docregion selected-tests
+ it('should display that hero\'s name', () => {
+ expect(page.nameDisplay.textContent).toBe(expectedHero.name);
+ });
+ // #enddocregion route-good-id
+
+ it('should navigate when click cancel', () => {
+ click(page.cancelBtn);
+ expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
+ });
+
+ it('should save when click save but not navigate immediately', () => {
+ // Get service injected into component and spy on its`saveHero` method.
+ // It delegates to fake `HeroService.updateHero` which delivers a safe test result.
+ const hds = fixture.debugElement.injector.get(HeroDetailService);
+ const saveSpy = spyOn(hds, 'saveHero').and.callThrough();
+
+ click(page.saveBtn);
+ expect(saveSpy.calls.any()).toBe(true, 'HeroDetailService.save called');
+ expect(page.navSpy.calls.any()).toBe(false, 'router.navigate not called');
+ });
+
+ it('should navigate when click save and save resolves', fakeAsync(() => {
+ click(page.saveBtn);
+ tick(); // wait for async save to complete
+ expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
+ }));
+
+ // #docregion title-case-pipe
+ it('should convert hero name to Title Case', () => {
+ const inputName = 'quick BROWN fox';
+ const titleCaseName = 'Quick Brown Fox';
+
+ // simulate user entering new name into the input box
+ page.nameInput.value = inputName;
+
+ // dispatch a DOM event so that Angular learns of input value change.
+ page.nameInput.dispatchEvent(newEvent('input'));
+
+ // Tell Angular to update the output span through the title pipe
+ fixture.detectChanges();
+
+ expect(page.nameDisplay.textContent).toBe(titleCaseName);
+ });
+ // #enddocregion title-case-pipe
+ // #enddocregion selected-tests
+ // #docregion route-good-id
+ });
+ // #enddocregion route-good-id
+
+ // #docregion route-no-id
+ describe('when navigate with no hero id', () => {
+ beforeEach( async( createComponent ));
+
+ it('should have hero.id === 0', () => {
+ expect(comp.hero.id).toBe(0);
+ });
+
+ it('should display empty hero name', () => {
+ expect(page.nameDisplay.textContent).toBe('');
+ });
+ });
+ // #enddocregion route-no-id
+
+ // #docregion route-bad-id
+ describe('when navigate to non-existant hero id', () => {
+ beforeEach( async(() => {
+ activatedRoute.testParams = { id: 99999 };
+ createComponent();
+ }));
+
+ it('should try to navigate back to hero list', () => {
+ expect(page.gotoSpy.calls.any()).toBe(true, 'comp.gotoList called');
+ expect(page.navSpy.calls.any()).toBe(true, 'router.navigate called');
+ });
+ });
+ // #enddocregion route-bad-id
+
+ // Why we must use `fixture.debugElement.injector` in `Page()`
+ it('cannot use `inject` to get component\'s provided HeroDetailService', () => {
+ let service: HeroDetailService;
+ fixture = TestBed.createComponent(HeroDetailComponent);
+ expect(
+ // Throws because `inject` only has access to TestBed's injector
+ // which is an ancestor of the component's injector
+ inject([HeroDetailService], (hds: HeroDetailService) => service = hds )
+ )
+ .toThrowError(/No provider for HeroDetailService/);
+
+ // get `HeroDetailService` with component's own injector
+ service = fixture.debugElement.injector.get(HeroDetailService);
+ expect(service).toBeDefined('debugElement.injector');
+ });
+}
+
+/////////////////////
+import { FormsModule } from '@angular/forms';
+import { TitleCasePipe } from '../shared/title-case.pipe';
+
+function formsModuleSetup() {
+ // #docregion setup-forms-module
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ FormsModule ],
+ declarations: [ HeroDetailComponent, TitleCasePipe ],
+ providers: [
+ { provide: ActivatedRoute, useValue: activatedRoute },
+ { provide: HeroService, useClass: FakeHeroService },
+ { provide: Router, useClass: RouterStub},
+ ]
+ })
+ .compileComponents();
+ }));
+ // #enddocregion setup-forms-module
+
+ it('should display 1st hero\'s name', fakeAsync(() => {
+ const expectedHero = firstHero;
+ activatedRoute.testParams = { id: expectedHero.id };
+ createComponent().then(() => {
+ expect(page.nameDisplay.textContent).toBe(expectedHero.name);
+ });
+ }));
+}
+
+///////////////////////
+import { SharedModule } from '../shared/shared.module';
+
+function sharedModuleSetup() {
+ // #docregion setup-shared-module
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ SharedModule ],
+ declarations: [ HeroDetailComponent ],
+ providers: [
+ { provide: ActivatedRoute, useValue: activatedRoute },
+ { provide: HeroService, useClass: FakeHeroService },
+ { provide: Router, useClass: RouterStub},
+ ]
+ })
+ .compileComponents();
+ }));
+ // #enddocregion setup-shared-module
+
+ it('should display 1st hero\'s name', fakeAsync(() => {
+ const expectedHero = firstHero;
+ activatedRoute.testParams = { id: expectedHero.id };
+ createComponent().then(() => {
+ expect(page.nameDisplay.textContent).toBe(expectedHero.name);
+ });
+ }));
+}
+
+/////////// Helpers /////
+
+// #docregion create-component
+/** Create the HeroDetailComponent, initialize it, set test variables */
+function createComponent() {
+ fixture = TestBed.createComponent(HeroDetailComponent);
+ comp = fixture.componentInstance;
+ page = new Page();
+
+ // 1st change detection triggers ngOnInit which gets a hero
+ fixture.detectChanges();
+ return fixture.whenStable().then(() => {
+ // 2nd change detection displays the async-fetched hero
+ fixture.detectChanges();
+ page.addPageElements();
+ });
+}
+// #enddocregion create-component
+
+// #docregion page
+class Page {
+ gotoSpy: jasmine.Spy;
+ navSpy: jasmine.Spy;
+
+ saveBtn: DebugElement;
+ cancelBtn: DebugElement;
+ nameDisplay: HTMLElement;
+ nameInput: HTMLInputElement;
+
+ constructor() {
+ const router = TestBed.get(Router); // get router from root injector
+ this.gotoSpy = spyOn(comp, 'gotoList').and.callThrough();
+ this.navSpy = spyOn(router, 'navigate');
+ }
+
+ /** Add page elements after hero arrives */
+ addPageElements() {
+ if (comp.hero) {
+ // have a hero so these elements are now in the DOM
+ const buttons = fixture.debugElement.queryAll(By.css('button'));
+ this.saveBtn = buttons[0];
+ this.cancelBtn = buttons[1];
+ this.nameDisplay = fixture.debugElement.query(By.css('span')).nativeElement;
+ this.nameInput = fixture.debugElement.query(By.css('input')).nativeElement;
+ }
+ }
+}
+// #enddocregion page
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.ts
new file mode 100644
index 0000000000..25f13b0cd5
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.component.ts
@@ -0,0 +1,63 @@
+/* tslint:disable:member-ordering */
+// #docplaster
+import { Component, Input, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import 'rxjs/add/operator/map';
+
+import { Hero } from '../model';
+import { HeroDetailService } from './hero-detail.service';
+
+// #docregion prototype
+@Component({
+ selector: 'app-hero-detail',
+ templateUrl: './hero-detail.component.html',
+ styleUrls: ['./hero-detail.component.css' ],
+ providers: [ HeroDetailService ]
+})
+export class HeroDetailComponent implements OnInit {
+ // #docregion ctor
+ constructor(
+ private heroDetailService: HeroDetailService,
+ private route: ActivatedRoute,
+ private router: Router) {
+ }
+ // #enddocregion ctor
+// #enddocregion prototype
+
+ @Input() hero: Hero;
+
+ // #docregion ng-on-init
+ ngOnInit(): void {
+ // get hero when `id` param changes
+ this.route.params.subscribe(p => this.getHero(p && p['id']));
+ }
+ // #enddocregion ng-on-init
+
+ private getHero(id: string): void {
+ // when no id or id===0, create new hero
+ if (!id) {
+ this.hero = new Hero();
+ return;
+ }
+
+ this.heroDetailService.getHero(id).then(hero => {
+ if (hero) {
+ this.hero = hero;
+ } else {
+ this.gotoList(); // id not found; navigate to list
+ }
+ });
+ }
+
+ save(): void {
+ this.heroDetailService.saveHero(this.hero).then(() => this.gotoList());
+ }
+
+ cancel() { this.gotoList(); }
+
+ gotoList() {
+ this.router.navigate(['../'], {relativeTo: this.route});
+ }
+// #docregion prototype
+}
+// #enddocregion prototype
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-detail.service.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.service.ts
new file mode 100644
index 0000000000..6239ae5b80
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-detail.service.ts
@@ -0,0 +1,26 @@
+import { Injectable } from '@angular/core';
+
+import { Hero, HeroService } from '../model';
+
+// #docregion prototype
+@Injectable()
+export class HeroDetailService {
+ constructor(private heroService: HeroService) { }
+// #enddocregion prototype
+
+ // Returns a clone which caller may modify safely
+ getHero(id: number | string): Promise {
+ if (typeof id === 'string') {
+ id = parseInt(id as string, 10);
+ }
+ return this.heroService.getHero(id).then(hero => {
+ return hero ? Object.assign({}, hero) : null; // clone or null
+ });
+ }
+
+ saveHero(hero: Hero) {
+ return this.heroService.updateHero(hero);
+ }
+// #docregion prototype
+}
+// #enddocregion prototype
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.css b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.css
new file mode 100644
index 0000000000..d939ab565d
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.css
@@ -0,0 +1,59 @@
+.selected {
+ background-color: #CFD8DC !important;
+ color: white;
+}
+.heroes {
+ margin: 0 0 2em 0;
+ list-style-type: none;
+ padding: 0;
+ width: 10em;
+}
+.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;
+}
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.html b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.html
new file mode 100644
index 0000000000..cd37301fd6
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.html
@@ -0,0 +1,8 @@
+My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.spec.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.spec.ts
new file mode 100644
index 0000000000..dbf9d37d71
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.spec.ts
@@ -0,0 +1,139 @@
+import { async, ComponentFixture, fakeAsync, TestBed, tick
+} from '@angular/core/testing';
+
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { addMatchers, newEvent, Router, RouterStub
+} from '../../testing';
+
+import { HEROES, FakeHeroService } from '../model/testing';
+
+import { HeroModule } from './hero.module';
+import { HeroListComponent } from './hero-list.component';
+import { HighlightDirective } from '../shared/highlight.directive';
+import { HeroService } from '../model';
+
+let comp: HeroListComponent;
+let fixture: ComponentFixture;
+let page: Page;
+
+/////// Tests //////
+
+describe('HeroListComponent', () => {
+
+ beforeEach( async(() => {
+ addMatchers();
+ TestBed.configureTestingModule({
+ imports: [HeroModule],
+ providers: [
+ { provide: HeroService, useClass: FakeHeroService },
+ { provide: Router, useClass: RouterStub}
+ ]
+ })
+ .compileComponents()
+ .then(createComponent);
+ }));
+
+ it('should display heroes', () => {
+ expect(page.heroRows.length).toBeGreaterThan(0);
+ });
+
+ it('1st hero should match 1st test hero', () => {
+ const expectedHero = HEROES[0];
+ const actualHero = page.heroRows[0].textContent;
+ expect(actualHero).toContain(expectedHero.id, 'hero.id');
+ expect(actualHero).toContain(expectedHero.name, 'hero.name');
+ });
+
+ it('should select hero on click', fakeAsync(() => {
+ const expectedHero = HEROES[1];
+ const li = page.heroRows[1];
+ li.dispatchEvent(newEvent('click'));
+ tick();
+ // `.toEqual` because selectedHero is clone of expectedHero; see FakeHeroService
+ expect(comp.selectedHero).toEqual(expectedHero);
+ }));
+
+ it('should navigate to selected hero detail on click', fakeAsync(() => {
+ const expectedHero = HEROES[1];
+ const li = page.heroRows[1];
+ li.dispatchEvent(newEvent('click'));
+ tick();
+
+ // should have navigated
+ expect(page.navSpy.calls.any()).toBe(true, 'navigate called');
+
+ // composed hero detail will be URL like 'heroes/42'
+ // expect link array with the route path and hero id
+ // first argument to router.navigate is link array
+ const navArgs = page.navSpy.calls.first().args[0];
+ expect(navArgs[0]).toContain('heroes', 'nav to heroes detail URL');
+ expect(navArgs[1]).toBe(expectedHero.id, 'expected hero.id');
+
+ }));
+
+ it('should find `HighlightDirective` with `By.directive', () => {
+ // #docregion by
+ // Can find DebugElement either by css selector or by directive
+ const h2 = fixture.debugElement.query(By.css('h2'));
+ const directive = fixture.debugElement.query(By.directive(HighlightDirective));
+ // #enddocregion by
+ expect(h2).toBe(directive);
+ });
+
+ it('should color header with `HighlightDirective`', () => {
+ const h2 = page.highlightDe.nativeElement as HTMLElement;
+ const bgColor = h2.style.backgroundColor;
+
+ // different browsers report color values differently
+ const isExpectedColor = bgColor === 'gold' || bgColor === 'rgb(255, 215, 0)';
+ expect(isExpectedColor).toBe(true, 'backgroundColor');
+ });
+
+ it('the `HighlightDirective` is among the element\'s providers', () => {
+ expect(page.highlightDe.providerTokens).toContain(HighlightDirective, 'HighlightDirective');
+ });
+});
+
+/////////// Helpers /////
+
+/** Create the component and set the `page` test variables */
+function createComponent() {
+ fixture = TestBed.createComponent(HeroListComponent);
+ comp = fixture.componentInstance;
+
+ // change detection triggers ngOnInit which gets a hero
+ fixture.detectChanges();
+
+ return fixture.whenStable().then(() => {
+ // got the heroes and updated component
+ // change detection updates the view
+ fixture.detectChanges();
+ page = new Page();
+ });
+}
+
+class Page {
+ /** Hero line elements */
+ heroRows: HTMLLIElement[];
+
+ /** Highlighted element */
+ highlightDe: DebugElement;
+
+ /** Spy on router navigate method */
+ navSpy: jasmine.Spy;
+
+ constructor() {
+ this.heroRows = fixture.debugElement.queryAll(By.css('li')).map(de => de.nativeElement);
+
+ // Find the first element with an attached HighlightDirective
+ this.highlightDe = fixture.debugElement.query(By.directive(HighlightDirective));
+
+ // Get the component's injected router and spy on it
+ const router = fixture.debugElement.injector.get(Router);
+ this.navSpy = spyOn(router, 'navigate');
+ };
+}
+
+
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.ts
new file mode 100644
index 0000000000..c61ff23f0e
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-list.component.ts
@@ -0,0 +1,27 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Hero, HeroService } from '../model';
+
+@Component({
+ selector: 'app-heroes',
+ templateUrl: './hero-list.component.html',
+ styleUrls: [ './hero-list.component.css' ]
+})
+export class HeroListComponent implements OnInit {
+ heroes: Promise;
+ selectedHero: Hero;
+
+ constructor(
+ private router: Router,
+ private heroService: HeroService) { }
+
+ ngOnInit() {
+ this.heroes = this.heroService.getHeroes();
+ }
+
+ onSelect(hero: Hero) {
+ this.selectedHero = hero;
+ this.router.navigate(['../heroes', this.selectedHero.id ]);
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero-routing.module.ts b/public/docs/_examples/testing/ts/src/app/hero/hero-routing.module.ts
new file mode 100644
index 0000000000..59ec14474c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero-routing.module.ts
@@ -0,0 +1,18 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+import { HeroListComponent } from './hero-list.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+const routes: Routes = [
+ { path: '', component: HeroListComponent },
+ { path: ':id', component: HeroDetailComponent }
+];
+
+export const routedComponents = [HeroDetailComponent, HeroListComponent];
+
+@NgModule({
+ imports: [RouterModule.forChild(routes)],
+ exports: [RouterModule]
+})
+export class HeroRoutingModule {}
diff --git a/public/docs/_examples/testing/ts/src/app/hero/hero.module.ts b/public/docs/_examples/testing/ts/src/app/hero/hero.module.ts
new file mode 100644
index 0000000000..dfe33cc199
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/hero/hero.module.ts
@@ -0,0 +1,9 @@
+import { NgModule } from '@angular/core';
+import { SharedModule } from '../shared/shared.module';
+import { routedComponents, HeroRoutingModule } from './hero-routing.module';
+
+@NgModule({
+ imports: [ SharedModule, HeroRoutingModule ],
+ declarations: [ routedComponents ]
+})
+export class HeroModule { }
diff --git a/public/docs/_examples/testing/ts/src/app/model/hero.service.ts b/public/docs/_examples/testing/ts/src/app/model/hero.service.ts
new file mode 100644
index 0000000000..667d47312b
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/hero.service.ts
@@ -0,0 +1,30 @@
+import { Injectable } from '@angular/core';
+
+import { Hero } from './hero';
+import { HEROES } from './test-heroes';
+
+@Injectable()
+/** Dummy HeroService. Pretend it makes real http requests */
+export class HeroService {
+ getHeroes() {
+ return Promise.resolve(HEROES);
+ }
+
+ getHero(id: number | string): Promise {
+ if (typeof id === 'string') {
+ id = parseInt(id as string, 10);
+ }
+ return this.getHeroes().then(
+ heroes => heroes.find(hero => hero.id === id)
+ );
+ }
+
+ updateHero(hero: Hero): Promise {
+ return this.getHero(hero.id).then(h => {
+ if (!h) {
+ throw new Error(`Hero ${hero.id} not found`);
+ }
+ return Object.assign(h, hero);
+ });
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/model/hero.spec.ts b/public/docs/_examples/testing/ts/src/app/model/hero.spec.ts
new file mode 100644
index 0000000000..e8acf913f2
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/hero.spec.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Hero } from './hero';
+
+describe('Hero', () => {
+ it('has name', () => {
+ const hero = new Hero(1, 'Super Cat');
+ expect(hero.name).toBe('Super Cat');
+ });
+
+ it('has id', () => {
+ const hero = new Hero(1, 'Super Cat');
+ expect(hero.id).toBe(1);
+ });
+
+ it('can clone itself', () => {
+ const hero = new Hero(1, 'Super Cat');
+ const clone = hero.clone();
+ expect(hero).toEqual(clone);
+ });
+});
diff --git a/public/docs/_examples/testing/ts/src/app/model/hero.ts b/public/docs/_examples/testing/ts/src/app/model/hero.ts
new file mode 100644
index 0000000000..6a98f0dfdc
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/hero.ts
@@ -0,0 +1,4 @@
+export class Hero {
+ constructor(public id = 0, public name = '') { }
+ clone() { return new Hero(this.id, this.name); }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/model/http-hero.service.spec.ts b/public/docs/_examples/testing/ts/src/app/model/http-hero.service.spec.ts
new file mode 100644
index 0000000000..c16b421274
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/http-hero.service.spec.ts
@@ -0,0 +1,127 @@
+import {
+ async, inject, TestBed
+} from '@angular/core/testing';
+
+import {
+ MockBackend,
+ MockConnection
+} from '@angular/http/testing';
+
+import {
+ HttpModule, Http, XHRBackend, Response, ResponseOptions
+} from '@angular/http';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/of';
+
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/do';
+import 'rxjs/add/operator/toPromise';
+
+import { Hero } from './hero';
+import { HttpHeroService as HeroService } from './http-hero.service';
+
+const makeHeroData = () => [
+ { id: 1, name: 'Windstorm' },
+ { id: 2, name: 'Bombasto' },
+ { id: 3, name: 'Magneta' },
+ { id: 4, name: 'Tornado' }
+] as Hero[];
+
+//////// Tests /////////////
+describe('Http-HeroService (mockBackend)', () => {
+
+ beforeEach( async(() => {
+ TestBed.configureTestingModule({
+ imports: [ HttpModule ],
+ providers: [
+ HeroService,
+ { provide: XHRBackend, useClass: MockBackend }
+ ]
+ })
+ .compileComponents();
+ }));
+
+ it('can instantiate service when inject service',
+ inject([HeroService], (service: HeroService) => {
+ expect(service instanceof HeroService).toBe(true);
+ }));
+
+
+
+ it('can instantiate service with "new"', inject([Http], (http: Http) => {
+ expect(http).not.toBeNull('http should be provided');
+ let service = new HeroService(http);
+ expect(service instanceof HeroService).toBe(true, 'new service should be ok');
+ }));
+
+
+ it('can provide the mockBackend as XHRBackend',
+ inject([XHRBackend], (backend: MockBackend) => {
+ expect(backend).not.toBeNull('backend should be provided');
+ }));
+
+ describe('when getHeroes', () => {
+ let backend: MockBackend;
+ let service: HeroService;
+ let fakeHeroes: Hero[];
+ let response: Response;
+
+ beforeEach(inject([Http, XHRBackend], (http: Http, be: MockBackend) => {
+ backend = be;
+ service = new HeroService(http);
+ fakeHeroes = makeHeroData();
+ let options = new ResponseOptions({status: 200, body: {data: fakeHeroes}});
+ response = new Response(options);
+ }));
+
+ it('should have expected fake heroes (then)', async(inject([], () => {
+ backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
+
+ service.getHeroes().toPromise()
+ // .then(() => Promise.reject('deliberate'))
+ .then(heroes => {
+ expect(heroes.length).toBe(fakeHeroes.length,
+ 'should have expected no. of heroes');
+ });
+ })));
+
+ it('should have expected fake heroes (Observable.do)', async(inject([], () => {
+ backend.connections.subscribe((c: MockConnection) => c.mockRespond(response));
+
+ service.getHeroes()
+ .do(heroes => {
+ expect(heroes.length).toBe(fakeHeroes.length,
+ 'should have expected no. of heroes');
+ })
+ .toPromise();
+ })));
+
+
+ it('should be OK returning no heroes', async(inject([], () => {
+ let resp = new Response(new ResponseOptions({status: 200, body: {data: []}}));
+ backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp));
+
+ service.getHeroes()
+ .do(heroes => {
+ expect(heroes.length).toBe(0, 'should have no heroes');
+ })
+ .toPromise();
+ })));
+
+ it('should treat 404 as an Observable error', async(inject([], () => {
+ let resp = new Response(new ResponseOptions({status: 404}));
+ backend.connections.subscribe((c: MockConnection) => c.mockRespond(resp));
+
+ service.getHeroes()
+ .do(heroes => {
+ fail('should not respond with heroes');
+ })
+ .catch(err => {
+ expect(err).toMatch(/Bad response status/, 'should catch bad response status code');
+ return Observable.of(null); // failure is the expected test result
+ })
+ .toPromise();
+ })));
+ });
+});
diff --git a/public/docs/_examples/testing/ts/src/app/model/http-hero.service.ts b/public/docs/_examples/testing/ts/src/app/model/http-hero.service.ts
new file mode 100644
index 0000000000..a5fe46b801
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/http-hero.service.ts
@@ -0,0 +1,68 @@
+// #docplaster
+// #docregion
+import { Injectable } from '@angular/core';
+import { Http, Response } from '@angular/http';
+import { Headers, RequestOptions } from '@angular/http';
+import { Hero } from './hero';
+
+import { Observable } from 'rxjs/Observable';
+import 'rxjs/add/observable/throw';
+
+import 'rxjs/add/operator/do';
+import 'rxjs/add/operator/catch';
+import 'rxjs/add/operator/map';
+
+@Injectable()
+export class HttpHeroService {
+ private _heroesUrl = 'app/heroes'; // URL to web api
+
+ constructor (private http: Http) {}
+
+ getHeroes (): Observable {
+ return this.http.get(this._heroesUrl)
+ .map(this.extractData)
+ // .do(data => console.log(data)) // eyeball results in the console
+ .catch(this.handleError);
+ }
+
+ getHero(id: number | string) {
+ return this.http
+ .get('app/heroes/?id=${id}')
+ .map((r: Response) => r.json().data as Hero[]);
+ }
+
+ addHero (name: string): Observable {
+ let body = JSON.stringify({ name });
+ let headers = new Headers({ 'Content-Type': 'application/json' });
+ let options = new RequestOptions({ headers: headers });
+
+ return this.http.post(this._heroesUrl, body, options)
+ .map(this.extractData)
+ .catch(this.handleError);
+ }
+
+ updateHero (hero: Hero): Observable {
+ let body = JSON.stringify(hero);
+ let headers = new Headers({ 'Content-Type': 'application/json' });
+ let options = new RequestOptions({ headers: headers });
+
+ return this.http.put(this._heroesUrl, body, options)
+ .map(this.extractData)
+ .catch(this.handleError);
+ }
+
+ private extractData(res: Response) {
+ if (res.status < 200 || res.status >= 300) {
+ throw new Error('Bad response status: ' + res.status);
+ }
+ let body = res.json();
+ return body.data || { };
+ }
+
+ private handleError (error: any) {
+ // In a real world app, we might send the error to remote logging infrastructure
+ let errMsg = error.message || 'Server error';
+ console.error(errMsg); // log to console instead
+ return Observable.throw(errMsg);
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/model/index.ts b/public/docs/_examples/testing/ts/src/app/model/index.ts
new file mode 100644
index 0000000000..227004d5be
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/index.ts
@@ -0,0 +1,7 @@
+// Model barrel
+export * from './hero';
+export * from './hero.service';
+export * from './http-hero.service';
+export * from './test-heroes';
+
+export * from './user.service';
diff --git a/public/docs/_examples/testing/ts/src/app/model/test-heroes.ts b/public/docs/_examples/testing/ts/src/app/model/test-heroes.ts
new file mode 100644
index 0000000000..d40ce5d564
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/test-heroes.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Hero } from './hero';
+
+export var HEROES: Hero[] = [
+ new Hero(11, 'Mr. Nice'),
+ new Hero(12, 'Narco'),
+ new Hero(13, 'Bombasto'),
+ new Hero(14, 'Celeritas'),
+ new Hero(15, 'Magneta'),
+ new Hero(16, 'RubberMan')
+];
diff --git a/public/docs/_examples/testing/ts/src/app/model/testing/fake-hero.service.ts b/public/docs/_examples/testing/ts/src/app/model/testing/fake-hero.service.ts
new file mode 100644
index 0000000000..79a865cc44
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/testing/fake-hero.service.ts
@@ -0,0 +1,41 @@
+// re-export for tester convenience
+export { Hero } from '../hero';
+export { HeroService } from '../hero.service';
+
+import { Hero } from '../hero';
+import { HeroService } from '../hero.service';
+
+export var HEROES: Hero[] = [
+ new Hero(41, 'Bob'),
+ new Hero(42, 'Carol'),
+ new Hero(43, 'Ted'),
+ new Hero(44, 'Alice'),
+ new Hero(45, 'Speedy'),
+ new Hero(46, 'Stealthy')
+];
+
+export class FakeHeroService implements HeroService {
+
+ heroes = HEROES.map(h => h.clone());
+ lastPromise: Promise; // remember so we can spy on promise calls
+
+ getHero(id: number | string) {
+ if (typeof id === 'string') {
+ id = parseInt(id as string, 10);
+ }
+ let hero = this.heroes.find(h => h.id === id);
+ return this.lastPromise = Promise.resolve(hero);
+ }
+
+ getHeroes() {
+ return this.lastPromise = Promise.resolve(this.heroes);
+ }
+
+ updateHero(hero: Hero): Promise {
+ return this.lastPromise = this.getHero(hero.id).then(h => {
+ return h ?
+ Object.assign(h, hero) :
+ Promise.reject(`Hero ${hero.id} not found`) as any as Promise;
+ });
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/model/testing/index.ts b/public/docs/_examples/testing/ts/src/app/model/testing/index.ts
new file mode 100644
index 0000000000..6da76e67db
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/testing/index.ts
@@ -0,0 +1 @@
+export * from './fake-hero.service';
diff --git a/public/docs/_examples/testing/ts/src/app/model/user.service.ts b/public/docs/_examples/testing/ts/src/app/model/user.service.ts
new file mode 100644
index 0000000000..280efefeec
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/model/user.service.ts
@@ -0,0 +1,7 @@
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class UserService {
+ isLoggedIn = true;
+ user = {name: 'Sam Spade'};
+}
diff --git a/public/docs/_examples/testing/ts/src/app/shared/highlight.directive.spec.ts b/public/docs/_examples/testing/ts/src/app/shared/highlight.directive.spec.ts
new file mode 100644
index 0000000000..b990f20b69
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/highlight.directive.spec.ts
@@ -0,0 +1,104 @@
+import { Component, DebugElement } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+
+import { HighlightDirective } from './highlight.directive';
+import { newEvent } from '../../testing';
+
+// #docregion test-component
+@Component({
+ template: `
+ Something Yellow
+ The Default (Gray)
+ No Highlight
+ `
+})
+class TestComponent { }
+// #enddocregion test-component
+
+describe('HighlightDirective', () => {
+
+ let fixture: ComponentFixture;
+ let des: DebugElement[]; // the three elements w/ the directive
+ let bareH2: DebugElement; // the w/o the directive
+
+ // #docregion selected-tests
+ beforeEach(() => {
+ fixture = TestBed.configureTestingModule({
+ declarations: [ HighlightDirective, TestComponent ]
+ })
+ .createComponent(TestComponent);
+
+ fixture.detectChanges(); // initial binding
+
+ // all elements with an attached HighlightDirective
+ des = fixture.debugElement.queryAll(By.directive(HighlightDirective));
+
+ // the h2 without the HighlightDirective
+ bareH2 = fixture.debugElement.query(By.css('h2:not([highlight])'));
+ });
+
+ // color tests
+ it('should have three highlighted elements', () => {
+ expect(des.length).toBe(3);
+ });
+
+ it('should color 1st background "yellow"', () => {
+ const bgColor = des[0].nativeElement.style.backgroundColor;
+ expect(bgColor).toBe('yellow');
+ });
+
+ it('should color 2nd background w/ default color', () => {
+ const dir = des[1].injector.get(HighlightDirective) as HighlightDirective;
+ const bgColor = des[1].nativeElement.style.backgroundColor;
+ expect(bgColor).toBe(dir.defaultColor);
+ });
+
+ it('should bind background to value color', () => {
+ // easier to work with nativeElement
+ const input = des[2].nativeElement as HTMLInputElement;
+ expect(input.style.backgroundColor).toBe('cyan', 'initial backgroundColor');
+
+ // dispatch a DOM event so that Angular responds to the input value change.
+ input.value = 'green';
+ input.dispatchEvent(newEvent('input'));
+ fixture.detectChanges();
+
+ expect(input.style.backgroundColor).toBe('green', 'changed backgroundColor');
+ });
+
+
+ it('bare should not have a customProperty', () => {
+ expect(bareH2.properties['customProperty']).toBeUndefined();
+ });
+ // #enddocregion selected-tests
+
+ // Removed on 12/02/2016 when ceased public discussion of the `Renderer`. Revive in future?
+ // // customProperty tests
+ // it('all highlighted elements should have a true customProperty', () => {
+ // const allTrue = des.map(de => !!de.properties['customProperty']).every(v => v === true);
+ // expect(allTrue).toBe(true);
+ // });
+
+ // injected directive
+ // attached HighlightDirective can be injected
+ it('can inject `HighlightDirective` in 1st ', () => {
+ const dir = des[0].injector.get(HighlightDirective);
+ expect(dir).toBeTruthy();
+ });
+
+ it('cannot inject `HighlightDirective` in 3rd ', () => {
+ const dir = bareH2.injector.get(HighlightDirective, null);
+ expect(dir).toBe(null);
+ });
+
+ // DebugElement.providerTokens
+ // attached HighlightDirective should be listed in the providerTokens
+ it('should have `HighlightDirective` in 1st providerTokens', () => {
+ expect(des[0].providerTokens).toContain(HighlightDirective);
+ });
+
+ it('should not have `HighlightDirective` in 3rd providerTokens', () => {
+ expect(bareH2.providerTokens).not.toContain(HighlightDirective);
+ });
+});
diff --git a/public/docs/_examples/testing/ts/src/app/shared/highlight.directive.ts b/public/docs/_examples/testing/ts/src/app/shared/highlight.directive.ts
new file mode 100644
index 0000000000..20901878c4
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/highlight.directive.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Directive, ElementRef, Input, OnChanges } from '@angular/core';
+
+@Directive({ selector: '[highlight]' })
+/** Set backgroundColor for the attached element to highlight color
+ * and set the element's customProperty to true */
+export class HighlightDirective implements OnChanges {
+
+ defaultColor = 'rgb(211, 211, 211)'; // lightgray
+
+ @Input('highlight') bgColor: string;
+
+ constructor(private el: ElementRef) {
+ el.nativeElement.style.customProperty = true;
+ }
+
+ ngOnChanges() {
+ this.el.nativeElement.style.backgroundColor = this.bgColor || this.defaultColor;
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/shared/shared.module.ts b/public/docs/_examples/testing/ts/src/app/shared/shared.module.ts
new file mode 100644
index 0000000000..17c41c0410
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/shared.module.ts
@@ -0,0 +1,15 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { HighlightDirective } from './highlight.directive';
+import { TitleCasePipe } from './title-case.pipe';
+import { TwainComponent } from './twain.component';
+
+@NgModule({
+ imports: [ CommonModule ],
+ exports: [ CommonModule, FormsModule,
+ HighlightDirective, TitleCasePipe, TwainComponent ],
+ declarations: [ HighlightDirective, TitleCasePipe, TwainComponent ]
+})
+export class SharedModule { }
diff --git a/public/docs/_examples/testing/ts/src/app/shared/title-case.pipe.spec.ts b/public/docs/_examples/testing/ts/src/app/shared/title-case.pipe.spec.ts
new file mode 100644
index 0000000000..5dfc5d91b0
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/title-case.pipe.spec.ts
@@ -0,0 +1,34 @@
+// #docplaster
+// #docregion
+import { TitleCasePipe } from './title-case.pipe';
+
+// #docregion excerpt, mini-excerpt
+describe('TitleCasePipe', () => {
+ // This pipe is a pure, stateless function so no need for BeforeEach
+ let pipe = new TitleCasePipe();
+
+ it('transforms "abc" to "Abc"', () => {
+ expect(pipe.transform('abc')).toBe('Abc');
+ });
+// #enddocregion mini-excerpt
+
+ it('transforms "abc def" to "Abc Def"', () => {
+ expect(pipe.transform('abc def')).toBe('Abc Def');
+ });
+
+ // ... more tests ...
+// #enddocregion excerpt
+ it('leaves "Abc Def" unchanged', () => {
+ expect(pipe.transform('Abc Def')).toBe('Abc Def');
+ });
+
+ it('transforms "abc-def" to "Abc-def"', () => {
+ expect(pipe.transform('abc-def')).toBe('Abc-def');
+ });
+
+ it('transforms " abc def" to " Abc Def" (preserves spaces) ', () => {
+ expect(pipe.transform(' abc def')).toBe(' Abc Def');
+ });
+// #docregion excerpt, mini-excerpt
+});
+// #enddocregion excerpt, mini-excerpt
diff --git a/public/docs/_examples/testing/ts/src/app/shared/title-case.pipe.ts b/public/docs/_examples/testing/ts/src/app/shared/title-case.pipe.ts
new file mode 100644
index 0000000000..df2567778d
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/title-case.pipe.ts
@@ -0,0 +1,11 @@
+// #docregion
+import { Pipe, PipeTransform } from '@angular/core';
+
+@Pipe({name: 'titlecase', pure: false})
+/** Transform to Title Case: uppercase the first letter of the words in a string.*/
+export class TitleCasePipe implements PipeTransform {
+ transform(input: string): string {
+ return input.length === 0 ? '' :
+ input.replace(/\w\S*/g, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase() ));
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/shared/twain.component.spec.ts b/public/docs/_examples/testing/ts/src/app/shared/twain.component.spec.ts
new file mode 100644
index 0000000000..b177c0bfc3
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/twain.component.spec.ts
@@ -0,0 +1,92 @@
+// #docplaster
+import { async, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { TwainService } from './twain.service';
+import { TwainComponent } from './twain.component';
+
+describe('TwainComponent', () => {
+
+ let comp: TwainComponent;
+ let fixture: ComponentFixture;
+
+ let spy: jasmine.Spy;
+ let de: DebugElement;
+ let el: HTMLElement;
+ let twainService: TwainService; // the actually injected service
+
+ const testQuote = 'Test Quote';
+
+ // #docregion setup
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [ TwainComponent ],
+ providers: [ TwainService ],
+ });
+
+ fixture = TestBed.createComponent(TwainComponent);
+ comp = fixture.componentInstance;
+
+ // TwainService actually injected into the component
+ twainService = fixture.debugElement.injector.get(TwainService);
+
+ // Setup spy on the `getQuote` method
+ // #docregion spy
+ spy = spyOn(twainService, 'getQuote')
+ .and.returnValue(Promise.resolve(testQuote));
+ // #enddocregion spy
+
+ // Get the Twain quote element by CSS selector (e.g., by class name)
+ de = fixture.debugElement.query(By.css('.twain'));
+ el = de.nativeElement;
+ });
+ // #enddocregion setup
+
+ // #docregion tests
+ it('should not show quote before OnInit', () => {
+ expect(el.textContent).toBe('', 'nothing displayed');
+ expect(spy.calls.any()).toBe(false, 'getQuote not yet called');
+ });
+
+ it('should still not show quote after component initialized', () => {
+ fixture.detectChanges();
+ // getQuote service is async => still has not returned with quote
+ expect(el.textContent).toBe('...', 'no quote yet');
+ expect(spy.calls.any()).toBe(true, 'getQuote called');
+ });
+
+ // #docregion async-test
+ it('should show quote after getQuote promise (async)', async(() => {
+ fixture.detectChanges();
+
+ fixture.whenStable().then(() => { // wait for async getQuote
+ fixture.detectChanges(); // update view with quote
+ expect(el.textContent).toBe(testQuote);
+ });
+ }));
+ // #enddocregion async-test
+
+ // #docregion fake-async-test
+ it('should show quote after getQuote promise (fakeAsync)', fakeAsync(() => {
+ fixture.detectChanges();
+ tick(); // wait for async getQuote
+ fixture.detectChanges(); // update view with quote
+ expect(el.textContent).toBe(testQuote);
+ }));
+ // #enddocregion fake-async-test
+ // #enddocregion tests
+
+ // #docregion done-test
+ it('should show quote after getQuote promise (done)', (done: any) => {
+ fixture.detectChanges();
+
+ // get the spy promise and wait for it to resolve
+ spy.calls.mostRecent().returnValue.then(() => {
+ fixture.detectChanges(); // update view with quote
+ expect(el.textContent).toBe(testQuote);
+ done();
+ });
+ });
+ // #enddocregion done-test
+});
diff --git a/public/docs/_examples/testing/ts/src/app/shared/twain.component.timer.spec.ts.no-work b/public/docs/_examples/testing/ts/src/app/shared/twain.component.timer.spec.ts.no-work
new file mode 100644
index 0000000000..74dec3e766
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/twain.component.timer.spec.ts.no-work
@@ -0,0 +1,116 @@
+// #docplaster
+// When AppComponent learns to present quote with intervalTimer
+import { async, discardPeriodicTasks, fakeAsync, ComponentFixture, TestBed, tick } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { TwainService } from './model';
+import { TwainComponent } from './twain.component';
+
+xdescribe('TwainComponent', () => {
+
+ let comp: TwainComponent;
+ let fixture: ComponentFixture;
+
+ const quotes = [
+ 'Test Quote 1',
+ 'Test Quote 2',
+ 'Test Quote 3'
+ ];
+
+ let spy: jasmine.Spy;
+ let twainEl: DebugElement; // the element with the Twain quote
+ let twainService: TwainService; // the actually injected service
+
+ function getQuote() { return twainEl.nativeElement.textContent; }
+
+ // #docregion setup
+ beforeEach(() => {
+ TestBed.configureTestingModule({
+ declarations: [ TwainComponent ],
+ providers: [ TwainService ],
+ });
+
+ fixture = TestBed.createComponent(TwainComponent);
+ comp = fixture.componentInstance;
+
+ // TwainService actually injected into the component
+ twainService = fixture.debugElement.injector.get(TwainService);
+
+ // Setup spy on the `getQuote` method
+ spy = spyOn(twainService, 'getQuote')
+ .and.returnValues(...quotes.map(q => Promise.resolve(q)));
+
+ // Get the Twain quote element by CSS selector (e.g., by class name)
+ twainEl = fixture.debugElement.query(By.css('.twain'));
+ });
+
+ afterEach(() => {
+ // destroy component to stop the component timer
+ fixture.destroy();
+ });
+ // #enddocregion setup
+
+ // #docregion tests
+ it('should not show quote before OnInit', () => {
+ expect(getQuote()).toBe('');
+ });
+
+ it('should still not show quote after component initialized', () => {
+ // because the getQuote service is async
+ fixture.detectChanges(); // trigger data binding
+ expect(getQuote()).toContain('not initialized');
+ });
+
+ // WIP
+ // If go this way, add jasmine.clock().uninstall(); to afterEach
+ // it('should show quote after Angular "settles"', async(() => {
+ // //jasmine.clock().install();
+ // fixture.detectChanges(); // trigger data binding
+ // fixture.whenStable().then(() => {
+ // fixture.detectChanges(); // update view with the quote
+ // expect(getQuote()).toBe(quotes[0]);
+ // });
+ // // jasmine.clock().tick(5000);
+ // // fixture.whenStable().then(() => {
+ // // fixture.detectChanges(); // update view with the quote
+ // // expect(getQuote()).toBe(quotes[1]);
+ // // });
+ // }));
+
+ it('should show quote after getQuote promise returns', fakeAsync(() => {
+ fixture.detectChanges(); // trigger data binding
+ tick(); // wait for first async getQuote to return
+ fixture.detectChanges(); // update view with the quote
+ expect(getQuote()).toBe(quotes[0]);
+
+ // destroy component to stop the component timer before test ends
+ // else test errors because still have timer in the queue
+ fixture.destroy();
+ }));
+
+ it('should show 2nd quote after 5 seconds pass', fakeAsync(() => {
+ fixture.detectChanges(); // trigger data binding
+ tick(5000); // wait for second async getQuote to return
+ fixture.detectChanges(); // update view with the quote
+ expect(getQuote()).toBe(quotes[1]);
+
+ // still have intervalTimer queuing requres
+ // discardPeriodicTasks() else test errors
+ discardPeriodicTasks();
+ }));
+
+ fit('should show 3rd quote after 10 seconds pass', fakeAsync(() => {
+ fixture.detectChanges(); // trigger data binding
+ tick(5000); // wait for second async getQuote to return
+ fixture.detectChanges(); // update view with the 2nd quote
+ tick(5000); // wait for third async getQuote to return
+ fixture.detectChanges(); // update view with the 3rd quote
+ expect(getQuote()).toBe(quotes[2]);
+
+ // still have intervalTimer queuing requres
+ // discardPeriodicTasks() else test errors
+ discardPeriodicTasks();
+ }));
+ // #enddocregion tests
+});
diff --git a/public/docs/_examples/testing/ts/src/app/shared/twain.component.timer.ts.no-work b/public/docs/_examples/testing/ts/src/app/shared/twain.component.timer.ts.no-work
new file mode 100644
index 0000000000..d3dc1f205d
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/twain.component.timer.ts.no-work
@@ -0,0 +1,27 @@
+// #docregion
+import { Component, OnInit, OnDestroy } from '@angular/core';
+
+import { TwainService } from './twain.service';
+
+@Component({
+ selector: 'twain-quote',
+ template: '{{quote}}
'
+})
+export class TwainComponent implements OnInit, OnDestroy {
+ intervalId: number;
+ quote = '-- not initialized yet --';
+ constructor(private twainService: TwainService) { }
+
+ getQuote() {
+ this.twainService.getQuote().then(quote => this.quote = quote);
+ }
+
+ ngOnInit(): void {
+ this.getQuote();
+ this.intervalId = window.setInterval(() => this.getQuote(), 5000);
+ }
+
+ ngOnDestroy(): void {
+ clearInterval(this.intervalId);
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/shared/twain.component.ts b/public/docs/_examples/testing/ts/src/app/shared/twain.component.ts
new file mode 100644
index 0000000000..29f24459ab
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/twain.component.ts
@@ -0,0 +1,20 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { TwainService } from './twain.service';
+
+// #docregion component
+@Component({
+ selector: 'twain-quote',
+ template: '{{quote}}
'
+})
+export class TwainComponent implements OnInit {
+ intervalId: number;
+ quote = '...';
+ constructor(private twainService: TwainService) { }
+
+ ngOnInit(): void {
+ this.twainService.getQuote().then(quote => this.quote = quote);
+ }
+}
+// #enddocregion component
diff --git a/public/docs/_examples/testing/ts/src/app/shared/twain.service.ts b/public/docs/_examples/testing/ts/src/app/shared/twain.service.ts
new file mode 100644
index 0000000000..9e394df1ee
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/shared/twain.service.ts
@@ -0,0 +1,32 @@
+import { Injectable } from '@angular/core';
+
+const quotes = [
+'Always do right. This will gratify some people and astonish the rest.',
+'I have never let my schooling interfere with my education.',
+'Don\'t go around saying the world owes you a living. The world owes you nothing. It was here first.',
+'Whenever you find yourself on the side of the majority, it is time to pause and reflect.',
+'If you tell the truth, you don\'t have to remember anything.',
+'Clothes make the man. Naked people have little or no influence on society.',
+'It\'s not the size of the dog in the fight, it\'s the size of the fight in the dog.',
+'Truth is stranger than fiction, but it is because Fiction is obliged to stick to possibilities; Truth isn\'t.',
+'The man who does not read good books has no advantage over the man who cannot read them.',
+'Get your facts first, and then you can distort them as much as you please.',
+];
+
+@Injectable()
+export class TwainService {
+ private next = 0;
+
+ // Imaginary todo: get quotes from a remote quote service
+ // returns quote after delay simulating server latency
+ getQuote(): Promise {
+ return new Promise(resolve => {
+ setTimeout( () => resolve(this.nextQuote()), 500 );
+ });
+ }
+
+ private nextQuote() {
+ if (this.next === quotes.length) { this.next = 0; }
+ return quotes[ this.next++ ];
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/app/welcome.component.spec.ts b/public/docs/_examples/testing/ts/src/app/welcome.component.spec.ts
new file mode 100644
index 0000000000..e506dda396
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/welcome.component.spec.ts
@@ -0,0 +1,108 @@
+// #docplaster
+import { ComponentFixture, inject, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { UserService } from './model';
+import { WelcomeComponent } from './welcome.component';
+
+describe('WelcomeComponent', () => {
+
+ let comp: WelcomeComponent;
+ let fixture: ComponentFixture;
+ let componentUserService: UserService; // the actually injected service
+ let userService: UserService; // the TestBed injected service
+ let de: DebugElement; // the DebugElement with the welcome message
+ let el: HTMLElement; // the DOM element with the welcome message
+
+ let userServiceStub: {
+ isLoggedIn: boolean;
+ user: { name: string}
+ };
+
+ // #docregion setup
+ beforeEach(() => {
+ // stub UserService for test purposes
+ // #docregion user-service-stub
+ userServiceStub = {
+ isLoggedIn: true,
+ user: { name: 'Test User'}
+ };
+ // #enddocregion user-service-stub
+
+ // #docregion config-test-module
+ TestBed.configureTestingModule({
+ declarations: [ WelcomeComponent ],
+ // #enddocregion setup
+ // providers: [ UserService ] // NO! Don't provide the real service!
+ // Provide a test-double instead
+ // #docregion setup
+ providers: [ {provide: UserService, useValue: userServiceStub } ]
+ });
+ // #enddocregion config-test-module
+
+ fixture = TestBed.createComponent(WelcomeComponent);
+ comp = fixture.componentInstance;
+
+ // #enddocregion setup
+ // #docregion injected-service
+ // UserService actually injected into the component
+ userService = fixture.debugElement.injector.get(UserService);
+ // #enddocregion injected-service
+ componentUserService = userService;
+ // #docregion setup
+ // #docregion inject-from-testbed
+ // UserService from the root injector
+ userService = TestBed.get(UserService);
+ // #enddocregion inject-from-testbed
+
+ // get the "welcome" element by CSS selector (e.g., by class name)
+ de = fixture.debugElement.query(By.css('.welcome'));
+ el = de.nativeElement;
+ });
+ // #enddocregion setup
+
+ // #docregion tests
+ it('should welcome the user', () => {
+ fixture.detectChanges();
+ const content = el.textContent;
+ expect(content).toContain('Welcome', '"Welcome ..."');
+ expect(content).toContain('Test User', 'expected name');
+ });
+
+ it('should welcome "Bubba"', () => {
+ userService.user.name = 'Bubba'; // welcome message hasn't been shown yet
+ fixture.detectChanges();
+ expect(el.textContent).toContain('Bubba');
+ });
+
+ it('should request login if not logged in', () => {
+ userService.isLoggedIn = false; // welcome message hasn't been shown yet
+ fixture.detectChanges();
+ const content = el.textContent;
+ expect(content).not.toContain('Welcome', 'not welcomed');
+ expect(content).toMatch(/log in/i, '"log in"');
+ });
+ // #enddocregion tests
+
+ // #docregion inject-it
+ it('should inject the component\'s UserService instance',
+ inject([UserService], (service: UserService) => {
+ expect(service).toBe(componentUserService);
+ }));
+ // #enddocregion inject-it
+
+ it('TestBed and Component UserService should be the same', () => {
+ expect(userService === componentUserService).toBe(true);
+ });
+
+ // #docregion stub-not-injected
+ it('stub object and injected UserService should not be the same', () => {
+ expect(userServiceStub === userService).toBe(false);
+
+ // Changing the stub object has no effect on the injected service
+ userServiceStub.isLoggedIn = false;
+ expect(userService.isLoggedIn).toBe(true);
+ });
+ // #enddocregion stub-not-injected
+});
diff --git a/public/docs/_examples/testing/ts/src/app/welcome.component.ts b/public/docs/_examples/testing/ts/src/app/welcome.component.ts
new file mode 100644
index 0000000000..35958cc5c9
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/app/welcome.component.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { UserService } from './model';
+
+@Component({
+ selector: 'app-welcome',
+ template: '{{welcome}} '
+})
+export class WelcomeComponent implements OnInit {
+ welcome = '-- not initialized yet --';
+ constructor(private userService: UserService) { }
+
+ ngOnInit(): void {
+ this.welcome = this.userService.isLoggedIn ?
+ 'Welcome, ' + this.userService.user.name :
+ 'Please log in.';
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/bag-specs.html b/public/docs/_examples/testing/ts/src/bag-specs.html
new file mode 100644
index 0000000000..89b46f7056
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/bag-specs.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ Specs Bag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/bag.html b/public/docs/_examples/testing/ts/src/bag.html
new file mode 100644
index 0000000000..3e0fcb9025
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/bag.html
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Specs Bag
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading ...
+
+
diff --git a/public/docs/_examples/testing/ts/src/banner-inline-specs.html b/public/docs/_examples/testing/ts/src/banner-inline-specs.html
new file mode 100644
index 0000000000..2a512a5647
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/banner-inline-specs.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ Banner Component (inline template) Specs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/banner-specs.html b/public/docs/_examples/testing/ts/src/banner-specs.html
new file mode 100644
index 0000000000..d16dd977a4
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/banner-specs.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ Banner Component Specs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/docs/_examples/testing/ts/src/browser-test-shim.js b/public/docs/_examples/testing/ts/src/browser-test-shim.js
new file mode 100644
index 0000000000..ee21831e22
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/browser-test-shim.js
@@ -0,0 +1,87 @@
+// BROWSER TESTING SHIM
+// Keep it in-sync with what karma-test-shim does
+// #docregion
+/*global jasmine, __karma__, window*/
+(function () {
+
+Error.stackTraceLimit = 0; // "No stacktrace"" is usually best for app testing.
+
+// Uncomment to get full stacktrace output. Sometimes helpful, usually not.
+// Error.stackTraceLimit = Infinity; //
+
+jasmine.DEFAULT_TIMEOUT_INTERVAL = 3000;
+
+var baseURL = document.baseURI;
+baseURL = baseURL + baseURL[baseURL.length-1] ? '' : '/';
+
+System.config({
+ baseURL: baseURL,
+ // Extend usual application package list with test folder
+ packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } },
+
+ // Assume npm: is set in `paths` in systemjs.config
+ // Map the angular testing umd bundles
+ map: {
+ '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
+ '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
+ '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
+ '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
+ '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
+ '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
+ '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
+ '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',
+ },
+});
+
+System.import('systemjs.config.js')
+ .then(importSystemJsExtras)
+ .then(initTestBed)
+ .then(initTesting);
+
+/** Optional SystemJS configuration extras. Keep going w/o it */
+function importSystemJsExtras(){
+ return System.import('systemjs.config.extras.js')
+ .catch(function(reason) {
+ console.log(
+ 'Note: System.import could not load "systemjs.config.extras.js" where you might have added more configuration. It is an optional file so we will continue without it.'
+ );
+ console.log(reason);
+ });
+}
+
+function initTestBed(){
+ return Promise.all([
+ System.import('@angular/core/testing'),
+ System.import('@angular/platform-browser-dynamic/testing')
+ ])
+
+ .then(function (providers) {
+ var coreTesting = providers[0];
+ var browserTesting = providers[1];
+
+ coreTesting.TestBed.initTestEnvironment(
+ browserTesting.BrowserDynamicTestingModule,
+ browserTesting.platformBrowserDynamicTesting());
+ })
+}
+
+// Import all spec files defined in the html (__spec_files__)
+// and start Jasmine testrunner
+function initTesting () {
+ console.log('loading spec files: '+__spec_files__.join(', '));
+ return Promise.all(
+ __spec_files__.map(function(spec) {
+ return System.import(spec);
+ })
+ )
+ // After all imports load, re-execute `window.onload` which
+ // triggers the Jasmine test-runner start or explain what went wrong
+ .then(success, console.error.bind(console));
+
+ function success () {
+ console.log('Spec files loaded; starting Jasmine testrunner');
+ window.onload();
+ }
+}
+
+})();
diff --git a/public/docs/_examples/testing/ts/src/index.html b/public/docs/_examples/testing/ts/src/index.html
new file mode 100644
index 0000000000..fff2464efc
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/index.html
@@ -0,0 +1,27 @@
+
+
+
+
+
+ App Under Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/testing/ts/src/main.ts b/public/docs/_examples/testing/ts/src/main.ts
new file mode 100644
index 0000000000..fadce2f3c1
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/main.ts
@@ -0,0 +1,5 @@
+// main app entry point
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/testing/ts/src/systemjs.config.extras.js b/public/docs/_examples/testing/ts/src/systemjs.config.extras.js
new file mode 100644
index 0000000000..218e65715c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/systemjs.config.extras.js
@@ -0,0 +1,9 @@
+// #docregion
+/** App specific SystemJS configuration */
+System.config({
+ packages: {
+ // barrels
+ 'app/model': {main:'index.js', defaultExtension:'js'},
+ 'app/model/testing': {main:'index.js', defaultExtension:'js'}
+ }
+});
diff --git a/public/docs/_examples/testing/ts/src/testing/index.ts b/public/docs/_examples/testing/ts/src/testing/index.ts
new file mode 100644
index 0000000000..e3de5164ca
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/testing/index.ts
@@ -0,0 +1,43 @@
+import { DebugElement } from '@angular/core';
+import { tick, ComponentFixture } from '@angular/core/testing';
+
+export * from './jasmine-matchers';
+export * from './router-stubs';
+
+///// Short utilities /////
+
+/** Wait a tick, then detect changes */
+export function advance(f: ComponentFixture): void {
+ tick();
+ f.detectChanges();
+}
+
+/**
+ * Create custom DOM event the old fashioned way
+ *
+ * https://developer.mozilla.org/en-US/docs/Web/API/Event/initEvent
+ * Although officially deprecated, some browsers (phantom) don't accept the preferred "new Event(eventName)"
+ */
+export function newEvent(eventName: string, bubbles = false, cancelable = false) {
+ let evt = document.createEvent('CustomEvent'); // MUST be 'CustomEvent'
+ evt.initCustomEvent(eventName, bubbles, cancelable, null);
+ return evt;
+}
+
+// See https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
+// #docregion click-event
+/** Button events to pass to `DebugElement.triggerEventHandler` for RouterLink event handler */
+export const ButtonClickEvents = {
+ left: { button: 0 },
+ right: { button: 2 }
+};
+
+/** Simulate element click. Defaults to mouse left-button click event. */
+export function click(el: DebugElement | HTMLElement, eventObj: any = ButtonClickEvents.left): void {
+ if (el instanceof HTMLElement) {
+ el.click();
+ } else {
+ el.triggerEventHandler('click', eventObj);
+ }
+}
+// #enddocregion click-event
diff --git a/public/docs/_examples/testing/ts/src/testing/jasmine-matchers.d.ts b/public/docs/_examples/testing/ts/src/testing/jasmine-matchers.d.ts
new file mode 100644
index 0000000000..f1c5acf77c
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/testing/jasmine-matchers.d.ts
@@ -0,0 +1,5 @@
+declare namespace jasmine {
+ interface Matchers {
+ toHaveText(actual: any, expectationFailOutput?: any): jasmine.CustomMatcher;
+ }
+}
diff --git a/public/docs/_examples/testing/ts/src/testing/jasmine-matchers.ts b/public/docs/_examples/testing/ts/src/testing/jasmine-matchers.ts
new file mode 100644
index 0000000000..4cab02e148
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/testing/jasmine-matchers.ts
@@ -0,0 +1,45 @@
+///
+
+//// Jasmine Custom Matchers ////
+// Be sure to extend jasmine-matchers.d.ts when adding matchers
+
+export function addMatchers(): void {
+ jasmine.addMatchers({
+ toHaveText: toHaveText
+ });
+}
+
+function toHaveText(): jasmine.CustomMatcher {
+ return {
+ compare: function (actual: any, expectedText: string, expectationFailOutput?: any): jasmine.CustomMatcherResult {
+ const actualText = elementText(actual);
+ const pass = actualText.indexOf(expectedText) > -1;
+ const message = pass ? '' : composeMessage();
+ return { pass, message };
+
+ function composeMessage () {
+ const a = (actualText.length < 100 ? actualText : actualText.substr(0, 100) + '...');
+ const efo = expectationFailOutput ? ` '${expectationFailOutput}'` : '';
+ return `Expected element to have text content '${expectedText}' instead of '${a}'${efo}`;
+ }
+ }
+ };
+}
+
+function elementText(n: any): string {
+ if (n instanceof Array) {
+ return n.map(elementText).join('');
+ }
+
+ if (n.nodeType === Node.COMMENT_NODE) {
+ return '';
+ }
+
+ if (n.nodeType === Node.ELEMENT_NODE && n.hasChildNodes()) {
+ return elementText(Array.prototype.slice.call(n.childNodes));
+ }
+
+ if (n.nativeElement) { n = n.nativeElement; }
+
+ return n.textContent;
+}
diff --git a/public/docs/_examples/testing/ts/src/testing/router-stubs.ts b/public/docs/_examples/testing/ts/src/testing/router-stubs.ts
new file mode 100644
index 0000000000..75a2858f65
--- /dev/null
+++ b/public/docs/_examples/testing/ts/src/testing/router-stubs.ts
@@ -0,0 +1,57 @@
+ // export for convenience.
+export { ActivatedRoute, Router, RouterLink, RouterOutlet} from '@angular/router';
+
+import { Component, Directive, Injectable, Input } from '@angular/core';
+import { NavigationExtras } from '@angular/router';
+
+// #docregion router-link
+@Directive({
+ selector: '[routerLink]',
+ host: {
+ '(click)': 'onClick()'
+ }
+})
+export class RouterLinkStubDirective {
+ @Input('routerLink') linkParams: any;
+ navigatedTo: any = null;
+
+ onClick() {
+ this.navigatedTo = this.linkParams;
+ }
+}
+// #enddocregion router-link
+
+@Component({selector: 'router-outlet', template: ''})
+export class RouterOutletStubComponent { }
+
+@Injectable()
+export class RouterStub {
+ navigate(commands: any[], extras?: NavigationExtras) { }
+}
+
+
+// Only implements params and part of snapshot.params
+// #docregion activated-route-stub
+import { BehaviorSubject } from 'rxjs/BehaviorSubject';
+
+@Injectable()
+export class ActivatedRouteStub {
+
+ // ActivatedRoute.params is Observable
+ private subject = new BehaviorSubject(this.testParams);
+ params = this.subject.asObservable();
+
+ // Test parameters
+ private _testParams: {};
+ get testParams() { return this._testParams; }
+ set testParams(params: {}) {
+ this._testParams = params;
+ this.subject.next(params);
+ }
+
+ // ActivatedRoute.snapshot.params
+ get snapshot() {
+ return { params: this.testParams };
+ }
+}
+// #enddocregion activated-route-stub
diff --git a/public/docs/_examples/testing/ts/wallaby.js b/public/docs/_examples/testing/ts/wallaby.js
new file mode 100644
index 0000000000..cd31e158fe
--- /dev/null
+++ b/public/docs/_examples/testing/ts/wallaby.js
@@ -0,0 +1,118 @@
+// Configuration for the Wallaby Visual Studio Code testing extension
+// https://marketplace.visualstudio.com/items?itemName=WallabyJs.wallaby-vscode
+// Note: Wallaby is not open source and costs money
+
+module.exports = function () {
+ return {
+ files: [
+ // System.js for module loading
+ {pattern: 'node_modules/systemjs/dist/system.js', instrument: false},
+ {pattern: 'systemjs.config.js', instrument: false},
+ {pattern: 'systemjs.config.extras.js', instrument: false},
+
+ // Polyfills
+ {pattern: 'node_modules/core-js/client/shim.min.js', instrument: false},
+
+ // zone.js
+ {pattern: 'node_modules/zone.js/dist/zone.js', instrument: false},
+ {pattern: 'node_modules/zone.js/dist/long-stack-trace-zone.js', instrument: false},
+ {pattern: 'node_modules/zone.js/dist/proxy.js', instrument: false},
+ {pattern: 'node_modules/zone.js/dist/sync-test.js', instrument: false},
+ {pattern: 'node_modules/zone.js/dist/jasmine-patch.js', instrument: false},
+ {pattern: 'node_modules/zone.js/dist/async-test.js', instrument: false},
+ {pattern: 'node_modules/zone.js/dist/fake-async-test.js', instrument: false},
+
+ // application (but not specs) loaded via module imports
+ {pattern: 'app/**/*+(ts|html|css)', load: false},
+ {pattern: 'app/**/*.spec.ts', ignore: true},
+
+ {pattern: 'testing/**/*+(ts|html|css)', load: false},
+ ],
+
+ tests: [
+ {pattern: 'app/**/*.spec.ts', load: false}
+ ],
+
+ middleware: function (app, express) {
+ app.use('/node_modules', express.static(require('path').join(__dirname, 'node_modules')));
+ },
+
+ testFramework: 'jasmine',
+
+ debug: true,
+
+ bootstrap: bootstrap
+ };
+};
+
+// Like karma-test-shim.js
+function bootstrap (wallaby) {
+ wallaby.delayStart();
+
+ System.config({
+ // Extend usual application package list with test folder
+ packages: { 'testing': { main: 'index.js', defaultExtension: 'js' } },
+
+ // Assume npm: is set in `paths` in systemjs.config
+ // Map the angular testing umd bundles
+ map: {
+ '@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
+ '@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
+ '@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
+ '@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
+ '@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
+ '@angular/http/testing': 'npm:@angular/http/bundles/http-testing.umd.js',
+ '@angular/router/testing': 'npm:@angular/router/bundles/router-testing.umd.js',
+ '@angular/forms/testing': 'npm:@angular/forms/bundles/forms-testing.umd.js',
+ },
+ });
+
+ System.import('systemjs.config.js')
+ .then(importSystemJsExtras)
+ .then(initTestBed)
+ .then(initTesting);
+
+ /** Optional SystemJS configuration extras. Keep going w/o it */
+ function importSystemJsExtras(){
+ return System.import('systemjs.config.extras.js')
+ .catch(function(reason) {
+ console.log(
+ 'Warning: System.import could not load the optional "systemjs.config.extras.js". Did you omit it by accident? Continuing without it.'
+ );
+ console.log(reason);
+ });
+ }
+
+ function initTestBed(){
+ return Promise.all([
+ System.import('@angular/core/testing'),
+ System.import('@angular/platform-browser-dynamic/testing')
+ ])
+
+ .then(function (providers) {
+ var coreTesting = providers[0];
+ var browserTesting = providers[1];
+
+ coreTesting.TestBed.initTestEnvironment(
+ browserTesting.BrowserDynamicTestingModule,
+ browserTesting.platformBrowserDynamicTesting());
+ })
+ }
+
+ // Load all spec files and start wallaby
+ function initTesting () {
+ return Promise.all(
+ wallaby.tests.map(function (specFile) {
+ return System.import(specFile);
+ })
+ )
+ .then(function () {
+ wallaby.start();
+ })
+ .catch(function (e) {
+ setTimeout(function () {
+ throw e;
+ }, 0);
+ });
+ }
+}
diff --git a/public/docs/_examples/toh-1/dart-snippets/app_component_snippets_pt1.dart b/public/docs/_examples/toh-1/dart-snippets/app_component_snippets_pt1.dart
new file mode 100644
index 0000000000..3bfa89762d
--- /dev/null
+++ b/public/docs/_examples/toh-1/dart-snippets/app_component_snippets_pt1.dart
@@ -0,0 +1,37 @@
+// #docregion show-hero
+template: '{{title}} {{hero}} details! '
+// #enddocregion show-hero
+
+// #docregion show-hero-2
+template: '{{title}} {{hero.name}} details! '
+// #enddocregion show-hero-2
+
+// #docregion show-hero-properties
+template: '{{title}} {{hero.name}} details! id: {{hero.id}}
name: {{hero.name}}
'
+// #enddocregion show-hero-properties
+
+// #docregion multi-line-strings
+template: '''
+ {{title}}
+ {{hero.name}} details!
+ id: {{hero.id}}
+ name: {{hero.name}}
'''
+// #enddocregion multi-line-strings
+
+// #docregion editing-Hero
+template: '''
+ {{title}}
+ {{hero.name}} details!
+ id: {{hero.id}}
+
+ name:
+
+
'''
+// #enddocregion editing-Hero
+
+// #docregion app-component-1
+class AppComponent {
+ String title = 'Tour of Heroes';
+ var hero = 'Windstorm';
+}
+// #enddocregion app-component-1
diff --git a/public/docs/_examples/toh-1/e2e-spec.ts b/public/docs/_examples/toh-1/e2e-spec.ts
new file mode 100644
index 0000000000..75f99788a6
--- /dev/null
+++ b/public/docs/_examples/toh-1/e2e-spec.ts
@@ -0,0 +1,70 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+const expectedH1 = 'Tour of Heroes';
+const expectedTitle = `Angular ${expectedH1}`;
+
+class Hero {
+ id: number;
+ name: string;
+
+ // Factory method
+ // Get hero id and name from the given detail element.
+ static async fromDetail(detail: ElementFinder): Promise {
+ // Get hero id from the first
+ let _id = await detail.all(by.css('div')).first().getText();
+ // Get name from the h2
+ let _name = await detail.element(by.css('h2')).getText();
+ return {
+ id: +_id.substr(_id.indexOf(' ') + 1),
+ name: _name.substr(0, _name.lastIndexOf(' '))
+ };
+ }
+}
+
+const nameSuffix = 'X';
+function addToHeroName(text: string): promise.Promise
{
+ let input = element(by.css('input'));
+ return input.sendKeys(text);
+}
+
+describe('Tutorial part 1', () => {
+
+ const expectedHero = { id: 1, name: 'Windstorm' };
+
+ beforeAll(() => browser.get(''));
+
+ it(`has title '${expectedTitle}'`, () => {
+ expect(browser.getTitle()).toEqual(expectedTitle);
+ });
+
+ it(`has h1 '${expectedH1}'`, () => {
+ let hText = element(by.css('h1')).getText();
+ expect(hText).toEqual(expectedH1, 'h1');
+ });
+
+ it(`shows initial hero details`, async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(expectedHero.id);
+ expect(hero.name).toEqual(expectedHero.name);
+ });
+
+ it(`shows updated hero name`, async () => {
+ addToHeroName(nameSuffix);
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ let newName = expectedHero.name + nameSuffix;
+ expect(hero.id).toEqual(expectedHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+});
+
+function getPageElts() {
+ return {
+ heroDetail: element(by.css('my-app'))
+ };
+}
diff --git a/public/docs/_examples/toh-1/ts/app/app.component.1.ts b/public/docs/_examples/toh-1/ts/app/app.component.1.ts
new file mode 100644
index 0000000000..371fe39f90
--- /dev/null
+++ b/public/docs/_examples/toh-1/ts/app/app.component.1.ts
@@ -0,0 +1,44 @@
+import { Component } from '@angular/core';
+
+let t = {
+// #docregion show-hero
+template: `{{title}} {{hero}} details! `
+// #enddocregion show-hero
+};
+
+t = {
+// #docregion show-hero-2
+template: `{{title}} {{hero.name}} details! `
+// #enddocregion show-hero-2
+};
+
+t = {
+// #docregion multi-line-strings
+template: `
+ {{title}}
+ {{hero.name}} details!
+ id: {{hero.id}}
+ name: {{hero.name}}
+ `
+// #enddocregion multi-line-strings
+};
+
+
+/*
+// #docregion name-input
+
+ name:
+
+
+// #enddocregion name-input
+*/
+
+/////////////////
+
+@Component(t)
+// #docregion app-component-1
+export class AppComponent {
+ title = 'Tour of Heroes';
+ hero = 'Windstorm';
+}
+// #enddocregion app-component-1
diff --git a/public/docs/_examples/toh-1/ts/example-config.json b/public/docs/_examples/toh-1/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/toh-1/ts/plnkr.json b/public/docs/_examples/toh-1/ts/plnkr.json
new file mode 100644
index 0000000000..ca75131d96
--- /dev/null
+++ b/public/docs/_examples/toh-1/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Tour of Heroes: Part 1",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ],
+ "tags": ["tutorial", "tour", "heroes"]
+}
diff --git a/public/docs/_examples/toh-1/ts/src/app/app.component.ts b/public/docs/_examples/toh-1/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..602781b9ba
--- /dev/null
+++ b/public/docs/_examples/toh-1/ts/src/app/app.component.ts
@@ -0,0 +1,33 @@
+// #docregion
+import { Component } from '@angular/core';
+
+// #docregion hero-class-1
+export class Hero {
+ id: number;
+ name: string;
+}
+// #enddocregion hero-class-1
+
+@Component({
+ selector: 'my-app',
+ // #docregion editing-Hero
+ template: `
+ {{title}}
+ {{hero.name}} details!
+ id: {{hero.id}}
+
+ name:
+
+
+ `
+ // #enddocregion editing-Hero
+})
+export class AppComponent {
+ title = 'Tour of Heroes';
+ // #docregion hero-property-1
+ hero: Hero = {
+ id: 1,
+ name: 'Windstorm'
+ };
+ // #enddocregion hero-property-1
+}
diff --git a/public/docs/_examples/toh-1/ts/src/app/app.module.ts b/public/docs/_examples/toh-1/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..8e87678efc
--- /dev/null
+++ b/public/docs/_examples/toh-1/ts/src/app/app.module.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule // <-- import the FormsModule before binding with [(ngModel)]
+ ],
+ declarations: [
+ AppComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/toh-1/ts/src/index.html b/public/docs/_examples/toh-1/ts/src/index.html
new file mode 100644
index 0000000000..a217238c6c
--- /dev/null
+++ b/public/docs/_examples/toh-1/ts/src/index.html
@@ -0,0 +1,25 @@
+
+
+
+ Angular Tour of Heroes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/toh-1/ts/src/main.ts b/public/docs/_examples/toh-1/ts/src/main.ts
new file mode 100644
index 0000000000..80ece654a5
--- /dev/null
+++ b/public/docs/_examples/toh-1/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+// #enddocregion
diff --git a/public/docs/_examples/toh-2/dart-snippets/app_component_snippets_pt2.dart b/public/docs/_examples/toh-2/dart-snippets/app_component_snippets_pt2.dart
new file mode 100644
index 0000000000..9f805c5bc5
--- /dev/null
+++ b/public/docs/_examples/toh-2/dart-snippets/app_component_snippets_pt2.dart
@@ -0,0 +1,69 @@
+// #docregion ng-for
+
+ {{hero.id}} {{hero.name}}
+
+// #enddocregion ng-for
+
+// #docregion heroes-styled
+My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+// #enddocregion heroes-styled
+
+// #docregion selectedHero-click
+
+ {{hero.id}} {{hero.name}}
+
+// #enddocregion selectedHero-click
+
+// #docregion selectedHero-details
+{{selectedHero.name}} details!
+id: {{selectedHero.id}}
+
+ name:
+
+
+// #enddocregion selectedHero-details
+
+// #docregion ng-if
+
+
{{selectedHero.name}} details!
+
id: {{selectedHero.id}}
+
+ name:
+
+
+
+// #enddocregion ng-if
+
+// #docregion hero-array-1
+final List heroes = mockHeroes;
+// #enddocregion hero-array-1
+
+// #docregion heroes-template-1
+My Heroes
+
+// #enddocregion heroes-template-1
+
+// #docregion heroes-ngfor-1
+
+// #enddocregion heroes-ngfor-1
+
+// #docregion class-selected-1
+[class.selected]="hero == selectedHero"
+// #enddocregion class-selected-1
+
+// #docregion class-selected-2
+
+ {{hero.id}} {{hero.name}}
+
+// #enddocregion class-selected-2
diff --git a/public/docs/_examples/toh-2/e2e-spec.ts b/public/docs/_examples/toh-2/e2e-spec.ts
new file mode 100644
index 0000000000..34f9fba573
--- /dev/null
+++ b/public/docs/_examples/toh-2/e2e-spec.ts
@@ -0,0 +1,133 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+const expectedH1 = 'Tour of Heroes';
+const expectedTitle = `Angular ${expectedH1}`;
+const expectedH2 = 'My Heroes';
+const targetHero = { id: 16, name: 'RubberMan' };
+const nameSuffix = 'X';
+
+class Hero {
+ id: number;
+ name: string;
+
+ // Factory methods
+
+ // Get hero from s formatted as ' '.
+ static fromString(s: string): Hero {
+ return {
+ id: +s.substr(0, s.indexOf(' ')),
+ name: s.substr(s.indexOf(' ') + 1),
+ };
+ }
+
+ // Get hero id and name from the given detail element.
+ static async fromDetail(detail: ElementFinder): Promise {
+ // Get hero id from the first
+ let _id = await detail.all(by.css('div')).first().getText();
+ // Get name from the h2
+ let _name = await detail.element(by.css('h2')).getText();
+ return {
+ id: +_id.substr(_id.indexOf(' ') + 1),
+ name: _name.substr(0, _name.lastIndexOf(' '))
+ };
+ }
+}
+
+describe('Tutorial part 2', () => {
+ beforeAll(() => browser.get(''));
+ describe('Initial page', initialPageTests);
+ describe('Select hero', selectHeroTests);
+ describe('Update hero', updateHeroTests);
+});
+
+function initialPageTests() {
+ it(`has title '${expectedTitle}'`, () => {
+ expect(browser.getTitle()).toEqual(expectedTitle);
+ });
+
+ it(`has h1 '${expectedH1}'`, () => {
+ expectHeading(1, expectedH1);
+ });
+
+ it(`has h2 '${expectedH2}'`, () => {
+ expectHeading(2, expectedH2);
+ });
+
+ it('has the right number of heroes', () => {
+ let page = getPageElts();
+ expect(page.heroes.count()).toEqual(10);
+ });
+
+ it('has no selected hero and no hero details', function () {
+ let page = getPageElts();
+ expect(page.selected.isPresent()).toBeFalsy('selected hero');
+ expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
+ });
+}
+
+function selectHeroTests() {
+ it(`selects ${targetHero.name} from hero list`, function () {
+ let hero = element(by.cssContainingText('li span.badge', targetHero.id.toString()));
+ hero.click();
+ // Nothing specific to expect other than lack of exceptions.
+ });
+
+ it(`has selected ${targetHero.name}`, function () {
+ let page = getPageElts();
+ let expectedText = `${targetHero.id} ${targetHero.name}`;
+ expect(page.selected.getText()).toBe(expectedText);
+ });
+
+ it('shows selected hero details', async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(targetHero.name);
+ });
+}
+
+function updateHeroTests() {
+ it(`can update hero name`, () => {
+ addToHeroName(nameSuffix);
+ // Nothing specific to expect other than lack of exceptions.
+ });
+
+ it(`shows updated hero name in details`, async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ let newName = targetHero.name + nameSuffix;
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+ it(`shows updated hero name in list`, async () => {
+ let page = getPageElts();
+ let hero = Hero.fromString(await page.selected.getText());
+ let newName = targetHero.name + nameSuffix;
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+}
+
+function addToHeroName(text: string): promise.Promise
{
+ let input = element(by.css('input'));
+ return input.sendKeys(text);
+}
+
+function expectHeading(hLevel: number, expectedText: string): void {
+ let hTag = `h${hLevel}`;
+ let hText = element(by.css(hTag)).getText();
+ expect(hText).toEqual(expectedText, hTag);
+};
+
+function getPageElts() {
+ return {
+ heroes: element.all(by.css('my-app li')),
+ selected: element(by.css('my-app li.selected')),
+ heroDetail: element(by.css('my-app > div, my-app > hero-detail > div'))
+ };
+}
diff --git a/public/docs/_examples/toh-2/ts/example-config.json b/public/docs/_examples/toh-2/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/toh-2/ts/plnkr.json b/public/docs/_examples/toh-2/ts/plnkr.json
new file mode 100644
index 0000000000..08c7b9581a
--- /dev/null
+++ b/public/docs/_examples/toh-2/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Tour of Heroes: Part 2",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ],
+ "tags": ["tutorial", "tour", "heroes"]
+}
diff --git a/public/docs/_examples/toh-2/ts/src/app/app.component.1.html b/public/docs/_examples/toh-2/ts/src/app/app.component.1.html
new file mode 100644
index 0000000000..86f8228723
--- /dev/null
+++ b/public/docs/_examples/toh-2/ts/src/app/app.component.1.html
@@ -0,0 +1,69 @@
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
+My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
+
+
+ ...
+
+
+
+
+{{selectedHero.name}} details!
+id: {{selectedHero.id}}
+
+ name:
+
+
+
+
+
+
+
{{selectedHero.name}} details!
+
id: {{selectedHero.id}}
+
+ name:
+
+
+
+
+
+
+heroes = HEROES;
+
+
+
+My Heroes
+
+
+
+
+
+
+
+
+[class.selected]="hero === selectedHero"
+
+
+
+
+ {{hero.id}} {{hero.name}}
+
+
diff --git a/public/docs/_examples/toh-2/ts/src/app/app.component.ts b/public/docs/_examples/toh-2/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..3e7c86f150
--- /dev/null
+++ b/public/docs/_examples/toh-2/ts/src/app/app.component.ts
@@ -0,0 +1,109 @@
+// #docregion
+import { Component } from '@angular/core';
+
+export class Hero {
+ id: number;
+ name: string;
+}
+
+// #docregion hero-array
+const HEROES: Hero[] = [
+ { 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' }
+];
+// #enddocregion hero-array
+
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+ My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
{{selectedHero.name}} details!
+
id: {{selectedHero.id}}
+
+ name:
+
+
+
+ `,
+ // #docregion styles
+ styles: [`
+ .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.selected:hover {
+ background-color: #BBD8DC !important;
+ color: white;
+ }
+ .heroes li:hover {
+ color: #607D8B;
+ background-color: #DDD;
+ left: .1em;
+ }
+ .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;
+ }
+ `]
+ // #enddocregion styles
+})
+export class AppComponent {
+ title = 'Tour of Heroes';
+ heroes = HEROES;
+ // #docregion selected-hero
+ selectedHero: Hero;
+ // #enddocregion selected-hero
+
+ // #docregion on-select
+ onSelect(hero: Hero): void {
+ this.selectedHero = hero;
+ }
+ // #enddocregion on-select
+}
diff --git a/public/docs/_examples/toh-2/ts/src/app/app.module.ts b/public/docs/_examples/toh-2/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..4c0b77ea48
--- /dev/null
+++ b/public/docs/_examples/toh-2/ts/src/app/app.module.ts
@@ -0,0 +1,18 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ declarations: [
+ AppComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/toh-2/ts/src/index.html b/public/docs/_examples/toh-2/ts/src/index.html
new file mode 100644
index 0000000000..a217238c6c
--- /dev/null
+++ b/public/docs/_examples/toh-2/ts/src/index.html
@@ -0,0 +1,25 @@
+
+
+
+ Angular Tour of Heroes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/toh-2/ts/src/main.ts b/public/docs/_examples/toh-2/ts/src/main.ts
new file mode 100644
index 0000000000..80ece654a5
--- /dev/null
+++ b/public/docs/_examples/toh-2/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+// #enddocregion
diff --git a/public/docs/_examples/toh-3/e2e-spec.ts b/public/docs/_examples/toh-3/e2e-spec.ts
new file mode 100644
index 0000000000..842716c5fc
--- /dev/null
+++ b/public/docs/_examples/toh-3/e2e-spec.ts
@@ -0,0 +1,133 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+const expectedH1 = 'Tour of Heroes';
+const expectedTitle = `Angular ${expectedH1}`;
+const expectedH2 = 'My Heroes';
+const targetHero = { id: 16, name: 'RubberMan' };
+const nameSuffix = 'X';
+
+class Hero {
+ id: number;
+ name: string;
+
+ // Factory methods
+
+ // Get hero from s formatted as ' '.
+ static fromString(s: string): Hero {
+ return {
+ id: +s.substr(0, s.indexOf(' ')),
+ name: s.substr(s.indexOf(' ') + 1),
+ };
+ }
+
+ // Get hero id and name from the given detail element.
+ static async fromDetail(detail: ElementFinder): Promise {
+ // Get hero id from the first
+ let _id = await detail.all(by.css('div')).first().getText();
+ // Get name from the h2
+ let _name = await detail.element(by.css('h2')).getText();
+ return {
+ id: +_id.substr(_id.indexOf(' ') + 1),
+ name: _name.substr(0, _name.lastIndexOf(' '))
+ };
+ }
+}
+
+describe('Tutorial part 3', () => {
+ beforeAll(() => browser.get(''));
+ describe('Initial page', initialPageTests);
+ describe('Select hero', selectHeroTests);
+ describe('Update hero', updateHeroTests);
+});
+
+function initialPageTests() {
+ it(`has title '${expectedTitle}'`, () => {
+ expect(browser.getTitle()).toEqual(expectedTitle);
+ });
+
+ it(`has h1 '${expectedH1}'`, () => {
+ expectHeading(1, expectedH1);
+ });
+
+ it(`has h2 '${expectedH2}'`, () => {
+ expectHeading(2, expectedH2);
+ });
+
+ it('has the right number of heroes', () => {
+ let page = getPageElts();
+ expect(page.heroes.count()).toEqual(10);
+ });
+
+ it('has no selected hero and no hero details', function () {
+ let page = getPageElts();
+ expect(page.selected.isPresent()).toBeFalsy('selected hero');
+ expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
+ });
+}
+
+function selectHeroTests() {
+ it(`selects ${targetHero.name} from hero list`, function () {
+ let hero = element(by.cssContainingText('li span.badge', targetHero.id.toString()));
+ hero.click();
+ // Nothing specific to expect other than lack of exceptions.
+ });
+
+ it(`has selected ${targetHero.name}`, function () {
+ let page = getPageElts();
+ let expectedText = `${targetHero.id} ${targetHero.name}`;
+ expect(page.selected.getText()).toBe(expectedText);
+ });
+
+ it('shows selected hero details', async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(targetHero.name);
+ });
+}
+
+function updateHeroTests() {
+ it(`can update hero name`, () => {
+ addToHeroName(nameSuffix);
+ // Nothing specific to expect other than lack of exceptions.
+ });
+
+ it(`shows updated hero name in details`, async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ let newName = targetHero.name + nameSuffix;
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+ it(`shows updated hero name in list`, async () => {
+ let page = getPageElts();
+ let hero = Hero.fromString(await page.selected.getText());
+ let newName = targetHero.name + nameSuffix;
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+}
+
+function addToHeroName(text: string): promise.Promise
{
+ let input = element(by.css('input'));
+ return input.sendKeys(text);
+}
+
+function expectHeading(hLevel: number, expectedText: string): void {
+ let hTag = `h${hLevel}`;
+ let hText = element(by.css(hTag)).getText();
+ expect(hText).toEqual(expectedText, hTag);
+};
+
+function getPageElts() {
+ return {
+ heroes: element.all(by.css('my-app li')),
+ selected: element(by.css('my-app li.selected')),
+ heroDetail: element(by.css('my-app > div, my-app > hero-detail > div'))
+ };
+}
diff --git a/public/docs/_examples/toh-3/ts/app/app.component.1.html b/public/docs/_examples/toh-3/ts/app/app.component.1.html
new file mode 100644
index 0000000000..bab9dda877
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/app/app.component.1.html
@@ -0,0 +1,12 @@
+{{title}}
+My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
+
diff --git a/public/docs/_examples/toh-3/ts/app/hero-detail.component.1.ts b/public/docs/_examples/toh-3/ts/app/hero-detail.component.1.ts
new file mode 100644
index 0000000000..fec477fbd7
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/app/hero-detail.component.1.ts
@@ -0,0 +1,35 @@
+// #docplaster
+// #docregion v1
+import { Component } from '@angular/core';
+
+// #enddocregion v1
+// #docregion hero-import
+import { Hero } from './hero';
+// #enddocregion hero-import
+
+// #docregion v1
+@Component({
+ selector: 'hero-detail',
+// #enddocregion v1
+ // #docregion template
+ template: `
+
+
{{hero.name}} details!
+
id: {{hero.id}}
+
+ name:
+
+
+
+ `
+ // #enddocregion template
+// #docregion v1
+})
+export class HeroDetailComponent {
+// #enddocregion v1
+// #docregion hero
+ hero: Hero;
+// #enddocregion hero
+// #docregion v1
+}
+// #enddocregion v1
diff --git a/public/docs/_examples/toh-3/ts/example-config.json b/public/docs/_examples/toh-3/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/toh-3/ts/plnkr.json b/public/docs/_examples/toh-3/ts/plnkr.json
new file mode 100644
index 0000000000..829715877e
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Tour of Heroes: Part 3",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1].*"
+ ],
+ "tags": ["tutorial", "tour", "heroes"]
+}
diff --git a/public/docs/_examples/toh-3/ts/src/app/app.component.ts b/public/docs/_examples/toh-3/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..3e242914aa
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/src/app/app.component.ts
@@ -0,0 +1,95 @@
+// #docregion
+import { Component } from '@angular/core';
+
+// #docregion hero-import
+import { Hero } from './hero';
+// #enddocregion hero-import
+
+const HEROES: Hero[] = [
+ { 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' }
+];
+
+@Component({
+ selector: 'my-app',
+// #docregion hero-detail-template
+ template: `
+ {{title}}
+ My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+ `,
+// #enddocregion hero-detail-template
+ styles: [`
+ .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.selected:hover {
+ background-color: #BBD8DC !important;
+ color: white;
+ }
+ .heroes li:hover {
+ color: #607D8B;
+ background-color: #DDD;
+ left: .1em;
+ }
+ .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;
+ }
+ `]
+})
+export class AppComponent {
+ title = 'Tour of Heroes';
+ heroes = HEROES;
+ selectedHero: Hero;
+
+ onSelect(hero: Hero): void {
+ this.selectedHero = hero;
+ }
+}
diff --git a/public/docs/_examples/toh-3/ts/src/app/app.module.ts b/public/docs/_examples/toh-3/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..e04b8d304b
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/src/app/app.module.ts
@@ -0,0 +1,24 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+// #docregion hero-detail-import
+import { HeroDetailComponent } from './hero-detail.component';
+// #enddocregion hero-detail-import
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+// #docregion declarations
+ declarations: [
+ AppComponent,
+ HeroDetailComponent
+ ],
+// #enddocregion declarations
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/toh-3/ts/src/app/hero-detail.component.ts b/public/docs/_examples/toh-3/ts/src/app/hero-detail.component.ts
new file mode 100644
index 0000000000..45a1a1e7e1
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/src/app/hero-detail.component.ts
@@ -0,0 +1,29 @@
+// #docregion
+// #docregion import-input
+import { Component, Input } from '@angular/core';
+// #enddocregion import-input
+
+import { Hero } from './hero';
+// #docregion template
+@Component({
+ selector: 'hero-detail',
+ template: `
+
+
{{hero.name}} details!
+
id: {{hero.id}}
+
+ name:
+
+
+
+ `
+})
+// #enddocregion template
+// #docregion class
+export class HeroDetailComponent {
+// #docregion hero
+ @Input() hero: Hero;
+// #enddocregion hero
+}
+// #enddocregion class
+
diff --git a/public/docs/_examples/toh-3/ts/src/app/hero.ts b/public/docs/_examples/toh-3/ts/src/app/hero.ts
new file mode 100644
index 0000000000..f4b0cd6b35
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/src/app/hero.ts
@@ -0,0 +1,6 @@
+// #docregion
+export class Hero {
+ id: number;
+ name: string;
+}
+// #enddocregion
diff --git a/public/docs/_examples/toh-3/ts/src/index.html b/public/docs/_examples/toh-3/ts/src/index.html
new file mode 100644
index 0000000000..a217238c6c
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/src/index.html
@@ -0,0 +1,25 @@
+
+
+
+ Angular Tour of Heroes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/toh-3/ts/src/main.ts b/public/docs/_examples/toh-3/ts/src/main.ts
new file mode 100644
index 0000000000..aa939b3241
--- /dev/null
+++ b/public/docs/_examples/toh-3/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion pt1
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
+// #enddocregion pt1
diff --git a/public/docs/_examples/toh-4/e2e-spec.ts b/public/docs/_examples/toh-4/e2e-spec.ts
new file mode 100644
index 0000000000..d6a223dd12
--- /dev/null
+++ b/public/docs/_examples/toh-4/e2e-spec.ts
@@ -0,0 +1,133 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+const expectedH1 = 'Tour of Heroes';
+const expectedTitle = `Angular ${expectedH1}`;
+const expectedH2 = 'My Heroes';
+const targetHero = { id: 16, name: 'RubberMan' };
+const nameSuffix = 'X';
+
+class Hero {
+ id: number;
+ name: string;
+
+ // Factory methods
+
+ // Get hero from s formatted as ' '.
+ static fromString(s: string): Hero {
+ return {
+ id: +s.substr(0, s.indexOf(' ')),
+ name: s.substr(s.indexOf(' ') + 1),
+ };
+ }
+
+ // Get hero id and name from the given detail element.
+ static async fromDetail(detail: ElementFinder): Promise {
+ // Get hero id from the first
+ let _id = await detail.all(by.css('div')).first().getText();
+ // Get name from the h2
+ let _name = await detail.element(by.css('h2')).getText();
+ return {
+ id: +_id.substr(_id.indexOf(' ') + 1),
+ name: _name.substr(0, _name.lastIndexOf(' '))
+ };
+ }
+}
+
+describe('Tutorial part 4', () => {
+ beforeAll(() => browser.get(''));
+ describe('Initial page', initialPageTests);
+ describe('Select hero', selectHeroTests);
+ describe('Update hero', updateHeroTests);
+});
+
+function initialPageTests() {
+ it(`has title '${expectedTitle}'`, () => {
+ expect(browser.getTitle()).toEqual(expectedTitle);
+ });
+
+ it(`has h1 '${expectedH1}'`, () => {
+ expectHeading(1, expectedH1);
+ });
+
+ it(`has h2 '${expectedH2}'`, () => {
+ expectHeading(2, expectedH2);
+ });
+
+ it('has the right number of heroes', () => {
+ let page = getPageElts();
+ expect(page.heroes.count()).toEqual(10);
+ });
+
+ it('has no selected hero and no hero details', function () {
+ let page = getPageElts();
+ expect(page.selected.isPresent()).toBeFalsy('selected hero');
+ expect(page.heroDetail.isPresent()).toBeFalsy('no hero detail');
+ });
+}
+
+function selectHeroTests() {
+ it(`selects ${targetHero.name} from hero list`, function () {
+ let hero = element(by.cssContainingText('li span.badge', targetHero.id.toString()));
+ hero.click();
+ // Nothing specific to expect other than lack of exceptions.
+ });
+
+ it(`has selected ${targetHero.name}`, function () {
+ let page = getPageElts();
+ let expectedText = `${targetHero.id} ${targetHero.name}`;
+ expect(page.selected.getText()).toBe(expectedText);
+ });
+
+ it('shows selected hero details', async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(targetHero.name);
+ });
+}
+
+function updateHeroTests() {
+ it(`can update hero name`, () => {
+ addToHeroName(nameSuffix);
+ // Nothing specific to expect other than lack of exceptions.
+ });
+
+ it(`shows updated hero name in details`, async () => {
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ let newName = targetHero.name + nameSuffix;
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+ it(`shows updated hero name in list`, async () => {
+ let page = getPageElts();
+ let hero = Hero.fromString(await page.selected.getText());
+ let newName = targetHero.name + nameSuffix;
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newName);
+ });
+
+}
+
+function addToHeroName(text: string): promise.Promise
{
+ let input = element(by.css('input'));
+ return input.sendKeys(text);
+}
+
+function expectHeading(hLevel: number, expectedText: string): void {
+ let hTag = `h${hLevel}`;
+ let hText = element(by.css(hTag)).getText();
+ expect(hText).toEqual(expectedText, hTag);
+};
+
+function getPageElts() {
+ return {
+ heroes: element.all(by.css('my-app li')),
+ selected: element(by.css('my-app li.selected')),
+ heroDetail: element(by.css('my-app > div, my-app > hero-detail > div'))
+ };
+}
diff --git a/public/docs/_examples/toh-4/ts/example-config.json b/public/docs/_examples/toh-4/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/toh-4/ts/plnkr.json b/public/docs/_examples/toh-4/ts/plnkr.json
new file mode 100644
index 0000000000..95987d95ce
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Tour of Heroes: Part 4",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1,2].*"
+ ],
+ "tags": ["tutorial", "tour", "heroes"]
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/app.component.1.ts b/public/docs/_examples/toh-4/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..cfddbab537
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/app.component.1.ts
@@ -0,0 +1,65 @@
+// #docplaster
+// #docregion on-init
+import { OnInit } from '@angular/core';
+
+// #enddocregion on-init
+import { Component } from '@angular/core';
+
+import { Hero } from './hero';
+// #docregion hero-service-import
+import { HeroService } from './hero.service.2';
+// #enddocregion hero-service-import
+
+// Testable but never shown
+@Component({
+ selector: 'my-app',
+ template: `
+
+ {{hero.name}}
+
+
+ `,
+ // #docregion providers
+ providers: [HeroService]
+ // #enddocregion providers
+})
+// #docregion on-init
+export class AppComponent implements OnInit {
+ // #enddocregion on-init
+ title = 'Tour of Heroes';
+ // #docregion heroes-prop
+ heroes: Hero[];
+ // #enddocregion heroes-prop
+ selectedHero: Hero;
+
+ /*
+ // #docregion new-service
+ heroService = new HeroService(); // don't do this
+ // #enddocregion new-service
+ */
+ // #docregion ctor
+ constructor(private heroService: HeroService) { }
+ // #enddocregion ctor
+ // #docregion getHeroes
+ getHeroes(): void {
+ // #docregion get-heroes
+ this.heroes = this.heroService.getHeroes();
+ // #enddocregion get-heroes
+ }
+ // #enddocregion getHeroes
+
+ // #docregion ng-on-init
+ // #docregion on-init
+ ngOnInit(): void {
+ // #enddocregion on-init
+ this.getHeroes();
+ // #docregion on-init
+ }
+ // #enddocregion on-init
+ // #enddocregion ng-on-init
+
+ onSelect(hero: Hero): void {
+ this.selectedHero = hero;
+ }
+ // #docregion on-init
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/app.component.ts b/public/docs/_examples/toh-4/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..0d57acb5b0
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/app.component.ts
@@ -0,0 +1,97 @@
+// #docplaster
+// #docregion
+import { Component, OnInit } from '@angular/core';
+
+import { Hero } from './hero';
+// #docregion hero-service-import
+import { HeroService } from './hero.service';
+// #enddocregion hero-service-import
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ {{title}}
+ My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+ `,
+ // #enddocregion template
+ styles: [`
+ .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.selected:hover {
+ background-color: #BBD8DC !important;
+ color: white;
+ }
+ .heroes li:hover {
+ color: #607D8B;
+ background-color: #DDD;
+ left: .1em;
+ }
+ .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;
+ }
+ `],
+ providers: [HeroService]
+})
+export class AppComponent implements OnInit {
+ title = 'Tour of Heroes';
+ heroes: Hero[];
+ selectedHero: Hero;
+
+ constructor(private heroService: HeroService) { }
+
+// #docregion get-heroes
+ getHeroes(): void {
+ this.heroService.getHeroes().then(heroes => this.heroes = heroes);
+ }
+// #enddocregion get-heroes
+
+ ngOnInit(): void {
+ this.getHeroes();
+ }
+
+ onSelect(hero: Hero): void {
+ this.selectedHero = hero;
+ }
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/app.module.ts b/public/docs/_examples/toh-4/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..3df186c62a
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/app.module.ts
@@ -0,0 +1,19 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { HeroDetailComponent } from './hero-detail.component';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ declarations: [
+ AppComponent,
+ HeroDetailComponent
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule { }
diff --git a/public/docs/_examples/toh-4/ts/src/app/hero-detail.component.ts b/public/docs/_examples/toh-4/ts/src/app/hero-detail.component.ts
new file mode 100644
index 0000000000..865fb98da7
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/hero-detail.component.ts
@@ -0,0 +1,22 @@
+// #docregion
+import { Component, Input } from '@angular/core';
+import { Hero } from './hero';
+
+@Component({
+ selector: 'hero-detail',
+ template: `
+
+
{{hero.name}} details!
+
+ id: {{hero.id}}
+
+
+ name:
+
+
+
+ `
+})
+export class HeroDetailComponent {
+ @Input() hero: Hero;
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/hero.service.1.ts b/public/docs/_examples/toh-4/ts/src/app/hero.service.1.ts
new file mode 100644
index 0000000000..2366215259
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/hero.service.1.ts
@@ -0,0 +1,24 @@
+// #docplaster
+// #docregion
+// #docregion empty-class, full
+import { Injectable } from '@angular/core';
+
+// #enddocregion empty-class
+import { Hero } from './hero';
+import { HEROES } from './mock-heroes';
+
+// #docregion empty-class, getHeroes-stub
+@Injectable()
+export class HeroService {
+ // #enddocregion empty-class, getHeroes-stub, full
+ /*
+ // #docregion getHeroes-stub
+ getHeroes(): void {} // stub
+ // #enddocregion getHeroes-stub
+ */
+ // #docregion full
+ getHeroes(): Hero[] {
+ return HEROES;
+ }
+ // #docregion empty-class, getHeroes-stub
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/hero.service.2.ts b/public/docs/_examples/toh-4/ts/src/app/hero.service.2.ts
new file mode 100644
index 0000000000..d14fe02410
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/hero.service.2.ts
@@ -0,0 +1,13 @@
+// #docregion
+import { Injectable } from '@angular/core';
+
+import { Hero } from './hero';
+import { HEROES } from './mock-heroes';
+
+@Injectable()
+export class HeroService {
+
+ getHeroes(): Hero[] {
+ return HEROES;
+ }
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/hero.service.ts b/public/docs/_examples/toh-4/ts/src/app/hero.service.ts
new file mode 100644
index 0000000000..03a1c10a4a
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/hero.service.ts
@@ -0,0 +1,29 @@
+// #docplaster
+// #docregion
+// #docregion just-get-heroes
+import { Injectable } from '@angular/core';
+
+import { Hero } from './hero';
+import { HEROES } from './mock-heroes';
+
+@Injectable()
+export class HeroService {
+ // #docregion get-heroes
+ getHeroes(): Promise {
+ return Promise.resolve(HEROES);
+ }
+ // #enddocregion get-heroes, just-get-heroes
+ // #enddocregion
+
+ // See the "Take it slow" appendix
+ // #docregion get-heroes-slowly
+ getHeroesSlowly(): Promise {
+ return new Promise(resolve => {
+ // Simulate server latency with 2 second delay
+ setTimeout(() => resolve(this.getHeroes()), 2000);
+ });
+ }
+ // #enddocregion get-heroes-slowly
+ // #docregion
+ // #docregion just-get-heroes
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/hero.ts b/public/docs/_examples/toh-4/ts/src/app/hero.ts
new file mode 100644
index 0000000000..e3eac516da
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/hero.ts
@@ -0,0 +1,4 @@
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/toh-4/ts/src/app/mock-heroes.ts b/public/docs/_examples/toh-4/ts/src/app/mock-heroes.ts
new file mode 100644
index 0000000000..6f7c5d83a0
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/app/mock-heroes.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Hero } from './hero';
+
+export const HEROES: Hero[] = [
+ {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'}
+];
diff --git a/public/docs/_examples/toh-4/ts/src/index.html b/public/docs/_examples/toh-4/ts/src/index.html
new file mode 100644
index 0000000000..a217238c6c
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/index.html
@@ -0,0 +1,25 @@
+
+
+
+ Angular Tour of Heroes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/toh-4/ts/src/main.1.ts b/public/docs/_examples/toh-4/ts/src/main.1.ts
new file mode 100644
index 0000000000..f22933ba8e
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/main.1.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/toh-4/ts/src/main.ts b/public/docs/_examples/toh-4/ts/src/main.ts
new file mode 100644
index 0000000000..f22933ba8e
--- /dev/null
+++ b/public/docs/_examples/toh-4/ts/src/main.ts
@@ -0,0 +1,4 @@
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/toh-5/e2e-spec.ts b/public/docs/_examples/toh-5/e2e-spec.ts
new file mode 100644
index 0000000000..a49f2ddb62
--- /dev/null
+++ b/public/docs/_examples/toh-5/e2e-spec.ts
@@ -0,0 +1,190 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+const expectedH1 = 'Tour of Heroes';
+const expectedTitle = `Angular ${expectedH1}`;
+const targetHero = { id: 15, name: 'Magneta' };
+const targetHeroDashboardIndex = 3;
+const nameSuffix = 'X';
+const newHeroName = targetHero.name + nameSuffix;
+
+class Hero {
+ id: number;
+ name: string;
+
+ // Factory methods
+
+ // Get hero from s formatted as ' '.
+ static fromString(s: string): Hero {
+ return {
+ id: +s.substr(0, s.indexOf(' ')),
+ name: s.substr(s.indexOf(' ') + 1),
+ };
+ }
+
+ // Get hero id and name from the given detail element.
+ static async fromDetail(detail: ElementFinder): Promise {
+ // Get hero id from the first
+ let _id = await detail.all(by.css('div')).first().getText();
+ // Get name from the h2
+ let _name = await detail.element(by.css('h2')).getText();
+ return {
+ id: +_id.substr(_id.indexOf(' ') + 1),
+ name: _name.substr(0, _name.lastIndexOf(' '))
+ };
+ }
+}
+
+describe('Tutorial part 5', () => {
+
+ beforeAll(() => browser.get(''));
+
+ function getPageElts() {
+ let navElts = element.all(by.css('my-app nav a'));
+
+ return {
+ navElts: navElts,
+
+ myDashboardHref: navElts.get(0),
+ myDashboard: element(by.css('my-app my-dashboard')),
+ topHeroes: element.all(by.css('my-app my-dashboard > div h4')),
+
+ myHeroesHref: navElts.get(1),
+ myHeroes: element(by.css('my-app my-heroes')),
+ allHeroes: element.all(by.css('my-app my-heroes li')),
+ selectedHero: element(by.css('my-app li.selected')),
+ selectedHeroSubview: element(by.css('my-app my-heroes > div')),
+
+ heroDetail: element(by.css('my-app hero-detail > div'))
+ };
+ }
+
+ describe('Initial page', () => {
+
+ it(`has title '${expectedTitle}'`, () => {
+ expect(browser.getTitle()).toEqual(expectedTitle);
+ });
+
+ it(`has h1 '${expectedH1}'`, () => {
+ expectHeading(1, expectedH1);
+ });
+
+ const expectedViewNames = ['Dashboard', 'Heroes'];
+ it(`has views ${expectedViewNames}`, () => {
+ let viewNames = getPageElts().navElts.map((el: ElementFinder) => el.getText());
+ expect(viewNames).toEqual(expectedViewNames);
+ });
+
+ it('has dashboard as the active view', () => {
+ let page = getPageElts();
+ expect(page.myDashboard.isPresent()).toBeTruthy();
+ });
+
+ });
+
+ describe('Dashboard tests', () => {
+
+ beforeAll(() => browser.get(''));
+
+ it('has top heroes', () => {
+ let page = getPageElts();
+ expect(page.topHeroes.count()).toEqual(4);
+ });
+
+ it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
+
+ it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
+
+ it(`saves and shows ${newHeroName} in Dashboard`, () => {
+ element(by.buttonText('Back')).click();
+ let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
+ expect(targetHeroElt.getText()).toEqual(newHeroName);
+ });
+
+ });
+
+ describe('Heroes tests', () => {
+
+ beforeAll(() => browser.get(''));
+
+ it('can switch to Heroes view', () => {
+ getPageElts().myHeroesHref.click();
+ let page = getPageElts();
+ expect(page.myHeroes.isPresent()).toBeTruthy();
+ expect(page.allHeroes.count()).toEqual(10, 'number of heroes');
+ });
+
+ it(`selects and shows ${targetHero.name} as selected in list`, () => {
+ getHeroLiEltById(targetHero.id).click();
+ let expectedText = `${targetHero.id} ${targetHero.name}`;
+ expect(getPageElts().selectedHero.getText()).toBe(expectedText);
+ });
+
+ it('shows selected hero subview', async () => {
+ let page = getPageElts();
+ let title = page.selectedHeroSubview.element(by.css('h2')).getText();
+ let expectedTitle = `${targetHero.name.toUpperCase()} is my hero`;
+ expect(title).toEqual(expectedTitle);
+ });
+
+ it('can route to hero details', async () => {
+ element(by.buttonText('View Details')).click();
+
+ let page = getPageElts();
+ expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(targetHero.name);
+ });
+
+ it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
+
+ it(`shows ${newHeroName} in Heroes list`, () => {
+ element(by.buttonText('Back')).click();
+ let expectedText = `${targetHero.id} ${newHeroName}`;
+ expect(getHeroLiEltById(targetHero.id).getText()).toEqual(expectedText);
+ });
+
+ });
+
+ async function dashboardSelectTargetHero() {
+ let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
+ expect(targetHeroElt.getText()).toEqual(targetHero.name);
+ targetHeroElt.click();
+
+ let page = getPageElts();
+ expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(targetHero.name);
+ }
+
+ async function updateHeroNameInDetailView() {
+ // Assumes that the current view is the hero details view.
+ addToHeroName(nameSuffix);
+
+ let page = getPageElts();
+ let hero = await Hero.fromDetail(page.heroDetail);
+ expect(hero.id).toEqual(targetHero.id);
+ expect(hero.name).toEqual(newHeroName);
+ }
+
+});
+
+function addToHeroName(text: string): promise.Promise
{
+ let input = element(by.css('input'));
+ return input.sendKeys(text);
+}
+
+function expectHeading(hLevel: number, expectedText: string): void {
+ let hTag = `h${hLevel}`;
+ let hText = element(by.css(hTag)).getText();
+ expect(hText).toEqual(expectedText, hTag);
+};
+
+function getHeroLiEltById(id: number) {
+ let spanForId = element(by.cssContainingText('li span.badge', id.toString()));
+ return spanForId.element(by.xpath('..'));
+}
diff --git a/public/docs/_examples/toh-5/ts/example-config.json b/public/docs/_examples/toh-5/ts/example-config.json
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/public/docs/_examples/toh-5/ts/plnkr.json b/public/docs/_examples/toh-5/ts/plnkr.json
new file mode 100644
index 0000000000..db4b15d160
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/plnkr.json
@@ -0,0 +1,10 @@
+{
+ "description": "Tour of Heroes: Part 5",
+ "basePath": "src/",
+ "files":[
+ "!**/*.d.ts",
+ "!**/*.js",
+ "!**/*.[1,2,3].*"
+ ],
+ "tags": ["tutorial", "tour", "heroes", "router"]
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/app-routing.module.ts b/public/docs/_examples/toh-5/ts/src/app/app-routing.module.ts
new file mode 100644
index 0000000000..dfd957782b
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app-routing.module.ts
@@ -0,0 +1,20 @@
+// #docregion
+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 {}
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.component.1.ts b/public/docs/_examples/toh-5/ts/src/app/app.component.1.ts
new file mode 100644
index 0000000000..c9f5db9712
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.component.1.ts
@@ -0,0 +1,42 @@
+// #docplaster
+// #docregion , v2
+import { Component } from '@angular/core';
+
+// #enddocregion v2
+@Component({
+ selector: 'my-app',
+ template: `
+ {{title}}
+
+ `
+})
+// #enddocregion
+// #docregion v2
+@Component({
+ selector: 'my-app',
+ // #docregion template-v2
+ template: `
+ {{title}}
+ Heroes
+
+ `
+ // #enddocregion template-v2
+})
+// #enddocregion
+@Component({
+ selector: 'my-app',
+ // #docregion template-v3
+ template: `
+ {{title}}
+
+ Dashboard
+ Heroes
+
+
+ `
+ // #enddocregion template-v3
+})
+// #docregion , v2
+export class AppComponent {
+ title = 'Tour of Heroes';
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.component.css b/public/docs/_examples/toh-5/ts/src/app/app.component.css
new file mode 100644
index 0000000000..071e665767
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.component.css
@@ -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;
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.component.ts b/public/docs/_examples/toh-5/ts/src/app/app.component.ts
new file mode 100644
index 0000000000..96bc3fe694
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.component.ts
@@ -0,0 +1,22 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-app',
+ // #docregion template
+ template: `
+ {{title}}
+
+ Dashboard
+ Heroes
+
+
+ `,
+ // #enddocregion template
+ // #docregion styleUrls
+ styleUrls: ['./app.component.css'],
+ // #enddocregion styleUrls
+})
+export class AppComponent {
+ title = 'Tour of Heroes';
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.module.1.ts b/public/docs/_examples/toh-5/ts/src/app/app.module.1.ts
new file mode 100644
index 0000000000..e1cda9b620
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.module.1.ts
@@ -0,0 +1,28 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroesComponent } from './heroes.component';
+import { HeroService } from './hero.service';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule
+ ],
+ declarations: [
+ AppComponent,
+ HeroDetailComponent,
+ HeroesComponent
+ ],
+ providers: [
+ HeroService
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {
+}
+// #enddocregion
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.module.2.ts b/public/docs/_examples/toh-5/ts/src/app/app.module.2.ts
new file mode 100644
index 0000000000..00876570f3
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.module.2.ts
@@ -0,0 +1,48 @@
+// #docplaster
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroesComponent } from './heroes.component';
+import { HeroService } from './hero.service';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ RouterModule.forRoot([
+ {
+ path: 'heroes',
+ component: HeroesComponent
+ }
+ ])
+ ],
+ declarations: [
+ AppComponent,
+ HeroDetailComponent,
+ HeroesComponent
+ ],
+ providers: [
+ HeroService
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {
+}
+// #enddocregion
+/*
+// #docregion heroes, routing
+import { RouterModule } from '@angular/router';
+
+RouterModule.forRoot([
+ {
+ path: 'heroes',
+ component: HeroesComponent
+ }
+])
+// #enddocregion heroes, routing
+*/
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.module.3.ts b/public/docs/_examples/toh-5/ts/src/app/app.module.3.ts
new file mode 100644
index 0000000000..306d9958f0
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.module.3.ts
@@ -0,0 +1,58 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+import { RouterModule } from '@angular/router';
+
+import { AppComponent } from './app.component';
+import { HeroDetailComponent } from './hero-detail.component';
+import { DashboardComponent } from './dashboard.component';
+import { HeroesComponent } from './heroes.component';
+import { HeroService } from './hero.service';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ RouterModule.forRoot([
+ // #docregion redirect
+ {
+ path: '',
+ redirectTo: '/dashboard',
+ pathMatch: 'full'
+ },
+ // #enddocregion redirect
+ // #docregion dashboard
+ {
+ path: 'dashboard',
+ component: DashboardComponent
+ },
+ // #enddocregion dashboard
+ // #docregion hero-detail
+ {
+ path: 'detail/:id',
+ component: HeroDetailComponent
+ },
+ // #enddocregion hero-detail
+ // #docregion heroes, routing
+ {
+ path: 'heroes',
+ component: HeroesComponent
+ }
+ // #enddocregion heroes, routing
+ ])
+ ],
+ declarations: [
+ AppComponent,
+ DashboardComponent,
+ HeroDetailComponent,
+ HeroesComponent
+ ],
+ providers: [
+ HeroService
+ ],
+ bootstrap: [ AppComponent ]
+})
+export class AppModule {
+}
+// #enddocregion
diff --git a/public/docs/_examples/toh-5/ts/src/app/app.module.ts b/public/docs/_examples/toh-5/ts/src/app/app.module.ts
new file mode 100644
index 0000000000..b376d69aba
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/app.module.ts
@@ -0,0 +1,35 @@
+// #docregion
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { FormsModule } from '@angular/forms';
+
+import { AppComponent } from './app.component';
+import { DashboardComponent } from './dashboard.component';
+import { HeroDetailComponent } from './hero-detail.component';
+import { HeroesComponent } from './heroes.component';
+import { HeroService } from './hero.service';
+
+// #docregion routing-module
+import { AppRoutingModule } from './app-routing.module';
+
+@NgModule({
+ imports: [
+ BrowserModule,
+ FormsModule,
+ AppRoutingModule
+ ],
+// #enddocregion routing-module
+ // #docregion dashboard
+ declarations: [
+ AppComponent,
+ DashboardComponent,
+ HeroDetailComponent,
+ HeroesComponent
+ ],
+ // #enddocregion dashboard
+ providers: [ HeroService ],
+ bootstrap: [ AppComponent ]
+// #docregion routing-module
+})
+export class AppModule { }
+// #enddocregion routing-module
diff --git a/public/docs/_examples/toh-5/ts/src/app/dashboard.component.1.html b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.1.html
new file mode 100644
index 0000000000..0c556b8de0
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.1.html
@@ -0,0 +1,9 @@
+
+Top Heroes
+
diff --git a/public/docs/_examples/toh-5/ts/src/app/dashboard.component.1.ts b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.1.ts
new file mode 100644
index 0000000000..3c92b205c8
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.1.ts
@@ -0,0 +1,8 @@
+// #docregion
+import { Component } from '@angular/core';
+
+@Component({
+ selector: 'my-dashboard',
+ template: 'My Dashboard '
+})
+export class DashboardComponent { }
diff --git a/public/docs/_examples/toh-5/ts/src/app/dashboard.component.css b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.css
new file mode 100644
index 0000000000..dc7fb7ce06
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.css
@@ -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;
+ }
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/dashboard.component.html b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.html
new file mode 100644
index 0000000000..49e77c460c
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.html
@@ -0,0 +1,11 @@
+
+Top Heroes
+
diff --git a/public/docs/_examples/toh-5/ts/src/app/dashboard.component.ts b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.ts
new file mode 100644
index 0000000000..416cf8868e
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/dashboard.component.ts
@@ -0,0 +1,33 @@
+// #docplaster
+// #docregion , imports
+import { Component, OnInit } from '@angular/core';
+
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+// #enddocregion imports
+
+// #docregion metadata
+@Component({
+ selector: 'my-dashboard',
+ templateUrl: './dashboard.component.html',
+ // #enddocregion metadata
+ // #docregion css
+ styleUrls: [ './dashboard.component.css' ]
+ // #enddocregion css
+ // #docregion metadata
+})
+// #enddocregion metadata
+// #docregion class
+export class DashboardComponent implements OnInit {
+
+ heroes: Hero[] = [];
+
+ // #docregion ctor
+ constructor(private heroService: HeroService) { }
+ // #enddocregion ctor
+
+ ngOnInit(): void {
+ this.heroService.getHeroes()
+ .then(heroes => this.heroes = heroes.slice(1, 5));
+ }
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.1.ts b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.1.ts
new file mode 100644
index 0000000000..2f5081bdce
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.1.ts
@@ -0,0 +1,29 @@
+// Imports in comments cause problems when the app is executed
+// (some error about 'traceur' missing). Hence this separate file
+// is solely for containing the transitory state of the imports.
+
+// #docregion added-imports
+// Keep the Input import for now, you'll remove it later:
+import { Component, Input, OnInit } from '@angular/core';
+import { ActivatedRoute, Params } from '@angular/router';
+import { Location } from '@angular/common';
+
+import { HeroService } from './hero.service';
+// #enddocregion added-imports
+
+// Bogus code below this point. It is only here to make lint happy.
+import { Hero } from './hero';
+
+@Component({})
+export class HeroDetailComponent implements OnInit {
+ @Input() hero: Hero;
+ bogus: Params;
+
+ constructor(
+ private heroService: HeroService,
+ private route: ActivatedRoute,
+ private location: Location
+ ) {}
+
+ ngOnInit() {}
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.css b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.css
new file mode 100644
index 0000000000..ab2437efd8
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.css
@@ -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;
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.html b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.html
new file mode 100644
index 0000000000..8f2ff9d90c
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.html
@@ -0,0 +1,14 @@
+
+
+
+
{{hero.name}} details!
+
+ id: {{hero.id}}
+
+ name:
+
+
+
+
Back
+
+
diff --git a/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.ts b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.ts
new file mode 100644
index 0000000000..222b6705d7
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/hero-detail.component.ts
@@ -0,0 +1,46 @@
+// #docplaster
+// #docregion , v2, rxjs-import
+import 'rxjs/add/operator/switchMap';
+// #enddocregion rxjs-import
+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';
+// #docregion metadata
+@Component({
+ selector: 'hero-detail',
+ templateUrl: './hero-detail.component.html',
+ // #enddocregion metadata, v2
+ styleUrls: [ './hero-detail.component.css' ]
+ // #docregion metadata, v2
+})
+// #enddocregion metadata
+// #docregion implement
+export class HeroDetailComponent implements OnInit {
+// #enddocregion implement
+ hero: Hero;
+
+ // #docregion ctor
+ constructor(
+ private heroService: HeroService,
+ private route: ActivatedRoute,
+ private location: Location
+ ) {}
+ // #enddocregion ctor
+
+ // #docregion ngOnInit
+ ngOnInit(): void {
+ this.route.params
+ .switchMap((params: Params) => this.heroService.getHero(+params['id']))
+ .subscribe(hero => this.hero = hero);
+ }
+ // #enddocregion ngOnInit
+
+ // #docregion goBack
+ goBack(): void {
+ this.location.back();
+ }
+// #enddocregion goBack
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/hero.service.ts b/public/docs/_examples/toh-5/ts/src/app/hero.service.ts
new file mode 100644
index 0000000000..ee5a684762
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/hero.service.ts
@@ -0,0 +1,26 @@
+// #docplaster
+// #docregion
+import { Hero } from './hero';
+import { HEROES } from './mock-heroes';
+import { Injectable } from '@angular/core';
+
+@Injectable()
+export class HeroService {
+ getHeroes(): Promise {
+ return Promise.resolve(HEROES);
+ }
+
+ getHeroesSlowly(): Promise {
+ return new Promise(resolve => {
+ // Simulate server latency with 2 second delay
+ setTimeout(() => resolve(this.getHeroes()), 2000);
+ });
+ }
+
+ // #docregion getHero
+ getHero(id: number): Promise {
+ return this.getHeroes()
+ .then(heroes => heroes.find(hero => hero.id === id));
+ }
+ // #enddocregion getHero
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/hero.ts b/public/docs/_examples/toh-5/ts/src/app/hero.ts
new file mode 100644
index 0000000000..e3eac516da
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/hero.ts
@@ -0,0 +1,4 @@
+export class Hero {
+ id: number;
+ name: string;
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/heroes.component.css b/public/docs/_examples/toh-5/ts/src/app/heroes.component.css
new file mode 100644
index 0000000000..b49fa0a419
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/heroes.component.css
@@ -0,0 +1,60 @@
+/* #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;
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/heroes.component.html b/public/docs/_examples/toh-5/ts/src/app/heroes.component.html
new file mode 100644
index 0000000000..db41c4692e
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/heroes.component.html
@@ -0,0 +1,19 @@
+
+
+My Heroes
+
+
+ {{hero.id}} {{hero.name}}
+
+
+
+
+
+
+ {{selectedHero.name | uppercase}} is my hero
+
+
+ View Details
+
diff --git a/public/docs/_examples/toh-5/ts/src/app/heroes.component.ts b/public/docs/_examples/toh-5/ts/src/app/heroes.component.ts
new file mode 100644
index 0000000000..def615e6b9
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/heroes.component.ts
@@ -0,0 +1,46 @@
+// #docplaster
+// #docregion
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+import { Hero } from './hero';
+import { HeroService } from './hero.service';
+
+// #docregion renaming, metadata
+@Component({
+ selector: 'my-heroes',
+ // #enddocregion renaming
+ templateUrl: './heroes.component.html',
+ styleUrls: [ './heroes.component.css' ]
+ // #docregion renaming
+})
+// #enddocregion metadata
+// #docregion class
+export class HeroesComponent implements OnInit {
+ // #enddocregion renaming
+ heroes: Hero[];
+ selectedHero: Hero;
+
+ constructor(
+ private router: Router,
+ private heroService: HeroService) { }
+
+ getHeroes(): void {
+ this.heroService.getHeroes().then(heroes => this.heroes = heroes);
+ }
+
+ ngOnInit(): void {
+ this.getHeroes();
+ }
+
+ onSelect(hero: Hero): void {
+ this.selectedHero = hero;
+ }
+
+ // #docregion gotoDetail
+ gotoDetail(): void {
+ this.router.navigate(['/detail', this.selectedHero.id]);
+ }
+ // #enddocregion gotoDetail
+ // #docregion renaming
+}
diff --git a/public/docs/_examples/toh-5/ts/src/app/mock-heroes.ts b/public/docs/_examples/toh-5/ts/src/app/mock-heroes.ts
new file mode 100644
index 0000000000..69afde3d34
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/app/mock-heroes.ts
@@ -0,0 +1,15 @@
+// #docregion
+import { Hero } from './hero';
+
+export var HEROES: Hero[] = [
+ {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'}
+];
diff --git a/public/docs/_examples/toh-5/ts/src/index.html b/public/docs/_examples/toh-5/ts/src/index.html
new file mode 100644
index 0000000000..cbacc9b83b
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/index.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+ Angular Tour of Heroes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Loading...
+
+
diff --git a/public/docs/_examples/toh-5/ts/src/main.ts b/public/docs/_examples/toh-5/ts/src/main.ts
new file mode 100644
index 0000000000..505f60b35b
--- /dev/null
+++ b/public/docs/_examples/toh-5/ts/src/main.ts
@@ -0,0 +1,6 @@
+// #docregion
+// main entry point
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+import { AppModule } from './app/app.module';
+
+platformBrowserDynamic().bootstrapModule(AppModule);
diff --git a/public/docs/_examples/toh-6/e2e-spec.ts b/public/docs/_examples/toh-6/e2e-spec.ts
new file mode 100644
index 0000000000..80d9660263
--- /dev/null
+++ b/public/docs/_examples/toh-6/e2e-spec.ts
@@ -0,0 +1,283 @@
+'use strict'; // necessary for es6 output in node
+
+import { browser, element, by, ElementFinder, ElementArrayFinder } from 'protractor';
+import { promise } from 'selenium-webdriver';
+
+const expectedH1 = 'Tour of Heroes';
+const expectedTitle = `Angular ${expectedH1}`;
+const targetHero = { id: 15, name: 'Magneta' };
+const targetHeroDashboardIndex = 3;
+const nameSuffix = 'X';
+const newHeroName = targetHero.name + nameSuffix;
+
+class Hero {
+ id: number;
+ name: string;
+
+ // Factory methods
+
+ // Hero from string formatted as ' '.
+ static fromString(s: string): Hero {
+ return {
+ id: +s.substr(0, s.indexOf(' ')),
+ name: s.substr(s.indexOf(' ') + 1),
+ };
+ }
+
+ // Hero from hero list element.
+ static async fromLi(li: ElementFinder): Promise {
+ let strings = await li.all(by.xpath('span')).getText();
+ return { id: +strings[0], name: strings[1] };
+ }
+
+ // Hero id and name from the given detail element.
+ static async fromDetail(detail: ElementFinder): Promise {
+ // Get hero id from the first
+ let _id = await detail.all(by.css('div')).first().getText();
+ // Get name from the h2
+ let _name = await detail.element(by.css('h2')).getText();
+ return {
+ id: +_id.substr(_id.indexOf(' ') + 1),
+ name: _name.substr(0, _name.lastIndexOf(' '))
+ };
+ }
+}
+
+describe('Tutorial part 6', () => {
+
+ beforeAll(() => browser.get(''));
+
+ function getPageElts() {
+ let navElts = element.all(by.css('my-app nav a'));
+
+ return {
+ navElts: navElts,
+
+ myDashboardHref: navElts.get(0),
+ myDashboard: element(by.css('my-app my-dashboard')),
+ topHeroes: element.all(by.css('my-app my-dashboard > div h4')),
+
+ myHeroesHref: navElts.get(1),
+ myHeroes: element(by.css('my-app my-heroes')),
+ allHeroes: element.all(by.css('my-app my-heroes li')),
+ selectedHero: element(by.css('my-app li.selected')),
+ selectedHeroSubview: element(by.css('my-app my-heroes > div:last-child')),
+
+ heroDetail: element(by.css('my-app hero-detail > div')),
+
+ searchBox: element(by.css('#search-box')),
+ searchResults: element.all(by.css('.search-result'))
+ };
+ }
+
+ describe('Initial page', () => {
+
+ it(`has title '${expectedTitle}'`, () => {
+ expect(browser.getTitle()).toEqual(expectedTitle);
+ });
+
+ it(`has h1 '${expectedH1}'`, () => {
+ expectHeading(1, expectedH1);
+ });
+
+ const expectedViewNames = ['Dashboard', 'Heroes'];
+ it(`has views ${expectedViewNames}`, () => {
+ let viewNames = getPageElts().navElts.map((el: ElementFinder) => el.getText());
+ expect(viewNames).toEqual(expectedViewNames);
+ });
+
+ it('has dashboard as the active view', () => {
+ let page = getPageElts();
+ expect(page.myDashboard.isPresent()).toBeTruthy();
+ });
+
+ });
+
+ describe('Dashboard tests', () => {
+
+ beforeAll(() => browser.get(''));
+
+ it('has top heroes', () => {
+ let page = getPageElts();
+ expect(page.topHeroes.count()).toEqual(4);
+ });
+
+ it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
+
+ it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
+
+ it(`cancels and shows ${targetHero.name} in Dashboard`, () => {
+ element(by.buttonText('Back')).click();
+ browser.waitForAngular(); // seems necessary to gets tests to past for toh-6
+
+ let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
+ expect(targetHeroElt.getText()).toEqual(targetHero.name);
+ });
+
+ it(`selects and routes to ${targetHero.name} details`, dashboardSelectTargetHero);
+
+ it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
+
+ it(`saves and shows ${newHeroName} in Dashboard`, () => {
+ element(by.buttonText('Save')).click();
+ browser.waitForAngular(); // seems necessary to gets tests to past for toh-6
+
+ let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
+ expect(targetHeroElt.getText()).toEqual(newHeroName);
+ });
+
+ });
+
+ describe('Heroes tests', () => {
+
+ beforeAll(() => browser.get(''));
+
+ it('can switch to Heroes view', () => {
+ getPageElts().myHeroesHref.click();
+ let page = getPageElts();
+ expect(page.myHeroes.isPresent()).toBeTruthy();
+ expect(page.allHeroes.count()).toEqual(10, 'number of heroes');
+ });
+
+ it(`selects and shows ${targetHero.name} as selected in list`, () => {
+ getHeroLiEltById(targetHero.id).click();
+ expect(Hero.fromLi(getPageElts().selectedHero)).toEqual(targetHero);
+ });
+
+ it('shows selected hero subview', () => {
+ let page = getPageElts();
+ let title = page.selectedHeroSubview.element(by.css('h2')).getText();
+ let expectedTitle = `${targetHero.name.toUpperCase()} is my hero`;
+ expect(title).toEqual(expectedTitle);
+ });
+
+ it('can route to hero details', () => {
+ element(by.buttonText('View Details')).click();
+
+ let page = getPageElts();
+ expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
+ let hero = Hero.fromDetail(page.heroDetail);
+ expect(hero).toEqual(targetHero);
+ });
+
+ it(`updates hero name (${newHeroName}) in details view`, updateHeroNameInDetailView);
+
+ it(`shows ${newHeroName} in Heroes list`, () => {
+ element(by.buttonText('Save')).click();
+ browser.waitForAngular(); // seems necessary to gets tests to past for toh-6
+ let expectedHero = {id: targetHero.id, name: newHeroName};
+ expect(Hero.fromLi(getHeroLiEltById(targetHero.id))).toEqual(expectedHero);
+ });
+
+ it(`deletes ${newHeroName} from Heroes list`, async () => {
+ const heroesBefore = await toHeroArray(getPageElts().allHeroes);
+ const li = getHeroLiEltById(targetHero.id);
+ li.element(by.buttonText('x')).click();
+
+ const page = getPageElts();
+ expect(page.myHeroes.isPresent()).toBeTruthy();
+ expect(page.allHeroes.count()).toEqual(9, 'number of heroes');
+ const heroesAfter = await toHeroArray(page.allHeroes);
+ const expectedHeroes = heroesBefore.filter(h => h.name !== newHeroName);
+ expect(heroesAfter).toEqual(expectedHeroes);
+ expect(page.selectedHeroSubview.isPresent()).toBeFalsy();
+ });
+
+ it(`adds back ${targetHero.name}`, async () => {
+ const newHeroName = 'Alice';
+ const heroesBefore = await toHeroArray(getPageElts().allHeroes);
+ const numHeroes = heroesBefore.length;
+
+ element(by.css('input')).sendKeys(newHeroName);
+ element(by.buttonText('Add')).click();
+
+ let page = getPageElts();
+ let heroesAfter = await toHeroArray(page.allHeroes);
+ expect(heroesAfter.length).toEqual(numHeroes + 1, 'number of heroes');
+
+ expect(heroesAfter.slice(0, numHeroes)).toEqual(heroesBefore, 'Old heroes are still there');
+
+ const maxId = heroesBefore[heroesBefore.length - 1].id;
+ expect(heroesAfter[numHeroes]).toEqual({id: maxId + 1, name: newHeroName});
+ });
+ });
+
+ describe('Progressive hero search', () => {
+
+ beforeAll(() => browser.get(''));
+
+ it(`searches for 'Ma'`, async () => {
+ getPageElts().searchBox.sendKeys('Ma');
+ browser.sleep(1000);
+ expect(getPageElts().searchResults.count()).toBe(4);
+ });
+
+ it(`continues search with 'g'`, async () => {
+ getPageElts().searchBox.sendKeys('g');
+ browser.sleep(1000);
+ expect(getPageElts().searchResults.count()).toBe(2);
+ });
+
+ it(`continues search with 'n' and gets ${targetHero.name}`, async () => {
+ getPageElts().searchBox.sendKeys('n');
+ browser.sleep(1000);
+ let page = getPageElts();
+ expect(page.searchResults.count()).toBe(1);
+ let hero = page.searchResults.get(0);
+ expect(hero.getText()).toEqual(targetHero.name);
+ });
+
+ it(`navigates to ${targetHero.name} details view`, async () => {
+ let hero = getPageElts().searchResults.get(0);
+ expect(hero.getText()).toEqual(targetHero.name);
+ hero.click();
+
+ let page = getPageElts();
+ expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
+ expect(Hero.fromDetail(page.heroDetail)).toEqual(targetHero);
+ });
+ });
+
+ function dashboardSelectTargetHero() {
+ let targetHeroElt = getPageElts().topHeroes.get(targetHeroDashboardIndex);
+ expect(targetHeroElt.getText()).toEqual(targetHero.name);
+ targetHeroElt.click();
+ browser.waitForAngular(); // seems necessary to gets tests to past for toh-6
+
+ let page = getPageElts();
+ expect(page.heroDetail.isPresent()).toBeTruthy('shows hero detail');
+ let hero = Hero.fromDetail(page.heroDetail);
+ expect(hero).toEqual(targetHero);
+ }
+
+ async function updateHeroNameInDetailView() {
+ // Assumes that the current view is the hero details view.
+ addToHeroName(nameSuffix);
+
+ let hero = await Hero.fromDetail(getPageElts().heroDetail);
+ expect(hero).toEqual({id: targetHero.id, name: newHeroName});
+ }
+
+});
+
+function addToHeroName(text: string): promise.Promise {
+ let input = element(by.css('input'));
+ return input.sendKeys(text);
+}
+
+function expectHeading(hLevel: number, expectedText: string): void {
+ let hTag = `h${hLevel}`;
+ let hText = element(by.css(hTag)).getText();
+ expect(hText).toEqual(expectedText, hTag);
+};
+
+function getHeroLiEltById(id: number): ElementFinder {
+ let spanForId = element(by.cssContainingText('li span.badge', id.toString()));
+ return spanForId.element(by.xpath('..'));
+}
+
+async function toHeroArray(allHeroes: ElementArrayFinder): Promise