From 63e0e6eaf6e67cd7674fd0fd774669af16a1bf04 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Sat, 6 Aug 2016 07:18:23 +0100 Subject: [PATCH 01/21] doc-gen: ignore new private module --- tools/api-builder/angular.io-package/index.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/api-builder/angular.io-package/index.js b/tools/api-builder/angular.io-package/index.js index 4f4121ad7c..054d7837f7 100644 --- a/tools/api-builder/angular.io-package/index.js +++ b/tools/api-builder/angular.io-package/index.js @@ -54,6 +54,7 @@ module.exports = new Package('angular.io', [basePackage, targetPackage, cheatshe '___platform_browser_private_types__', '___platform_browser_dynamic_private__', '___platform_browser_dynamic_private_types__', + '___core_private_testing_types__', '___compiler_private__', '__core_private__', '___core_private__' @@ -135,7 +136,7 @@ module.exports = new Package('angular.io', [basePackage, targetPackage, cheatshe pathTemplate: 'api-list.json', outputPathTemplate: '${path}' }); - + computePathsProcessor.pathTemplates.push({ docTypes: ['api-list-data'], pathTemplate: 'api-list-audit.json', From 9f03fca0390fabc8b36997b4a1cdd4ad358fda33 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin Date: Sat, 6 Aug 2016 07:45:32 +0100 Subject: [PATCH 02/21] docs(cookbook): text following a node requires a dot --- .../latest/cookbook/set-document-title.jade | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/public/docs/ts/latest/cookbook/set-document-title.jade b/public/docs/ts/latest/cookbook/set-document-title.jade index 9c545f5a0d..f25fe798f8 100644 --- a/public/docs/ts/latest/cookbook/set-document-title.jade +++ b/public/docs/ts/latest/cookbook/set-document-title.jade @@ -9,21 +9,21 @@ a(id='top') .l-sub-section img(src='/resources/images/devguide/plunker-separate-window-button.png' alt="pop out the window" align="right" style="margin-right:-20px") :marked - To see the browser Title bar changes, + To see the browser Title bar changes, pop out the preview window by clicking the blue 'X' button in the upper right corner. :marked ## The problem with *<title>* - + The obvious approach is to bind a property of the component to the HTML `` like this: -code-example(format='') +code-example(format=''). <title>{{This_Does_Not_Work}}</title> :marked - Sorry but that won't work. + Sorry but that won't work. The root component of our application is an element contained within the `<body>` tag. The HTML `<title>` is in the document `<head>`, outside the body, making it inaccessible to Angular data binding. - We could grab the browser `document` object and set the title manually. - That's dirty and undermines our chances of running the app outside of a browser someday. + We could grab the browser `document` object and set the title manually. + That's dirty and undermines our chances of running the app outside of a browser someday. .l-sub-section :marked Running your app outside a browser means that you can take advantage of server-side @@ -40,16 +40,16 @@ code-example(format='') * `getTitle() : string` — Gets the title of the current HTML document. * `setTitle( newTitle : string )` — Sets the title of the current HTML document. - While this class is part of the Browser platform package, it is *not part of the default Browser - platform providers* that Angular loads automatically. + While this class is part of the Browser platform package, it is *not part of the default Browser + platform providers* that Angular loads automatically. This means as we bootstrap our application using the Browser platform `boostrap()` function, we'll also have to include `Title` service explicitly as one of the bootstrap providers: +makeExample( "cb-set-document-title/ts/app/main.ts", "bootstrap-title", "app/main.ts (provide Title service)" )(format='.') :marked Once we've explicitly provided the `Title` service we can then inject the `Title` service into any of our - custom application components and services. - + custom application components and services. + Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it: +makeExample( "cb-set-document-title/ts/app/app.component.ts", "class", "app/app.component.ts (class)" )(format='.') @@ -61,31 +61,31 @@ figure.image-display :marked Here's the complete solution -+makeTabs( ++makeTabs( `cb-set-document-title/ts/app/main.ts, - cb-set-document-title/ts/app/app.component.ts`, - '', + cb-set-document-title/ts/app/app.component.ts`, + '', 'app/main.ts, app/app.component.ts' ) // - Todo: tie this back to the router so we can see how to use this Title service to (re)set the title + Todo: tie this back to the router so we can see how to use this Title service to (re)set the title that appears in the window navigation history and shows up in the back/forward buttons during routing. - + See https://github.com/angular/angular/issues/7630#issuecomment-198328802 - + .l-main-section :marked ## Why we provide the *Title* service in *bootstrap* - + We generally recommended providing application-wide services in the root application component, `AppComponent`. - - Here we recommend registering the title service during bootstrapping, + + Here we recommend registering the title service during bootstrapping, a location we reserve for configuring the runtime Angular environment. - + That's exactly what we're doing. - The `Title` service is part of the Angular *browser platform*. - If we bootstrap our application into a different platform, + The `Title` service is part of the Angular *browser platform*. + If we bootstrap our application into a different platform, we'll have to provide a different `Title` service that understands the concept of a "document title" for that specific platform. Ideally the application itself neither knows nor cares about the runtime environment. :marked From 35b2dbd867e832db1c3af6015a3b953eef7537d2 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin <pete@bacondarwin.com> Date: Sat, 6 Aug 2016 07:58:28 +0100 Subject: [PATCH 03/21] docs(tutorial): text following a node requires a dot --- public/docs/dart/latest/tutorial/toh-pt3.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/docs/dart/latest/tutorial/toh-pt3.jade b/public/docs/dart/latest/tutorial/toh-pt3.jade index 87c061630d..7e7bd9911a 100644 --- a/public/docs/dart/latest/tutorial/toh-pt3.jade +++ b/public/docs/dart/latest/tutorial/toh-pt3.jade @@ -62,7 +62,7 @@ code-example(language="bash"). All of our component names end in "Component". All of our component file names end in "_component". - We spell our filenames in lower **underscore case** + We spell our filenames in lower **underscore case** (AKA **[snake_case](../guide/glossary.html#!#snake_case)**) so we don't worry about case sensitivity on the server or in source control. @@ -164,7 +164,7 @@ code-example(format="."). :marked The two components won't coordinate until we bind the `selectedHero` property of the `AppComponent` to the `HeroDetailComponent` element's `hero` property like this: -code-example(format=".") +code-example(format="."). <my-hero-detail [hero]="selectedHero"></my-hero-detail> :marked The `AppComponent`’s template should now look like this From 177161a708cc9e714eb6a4e095c358481924b474 Mon Sep 17 00:00:00 2001 From: AlmeroSteyn <almero.steyn@gmail.com> Date: Fri, 15 Jul 2016 11:59:23 +0200 Subject: [PATCH 04/21] docs-styles(build): new a11y-compliant styles for doc sample closes #1897 Generates a2docs.css from a11y-compliant less file See PR #1897 for more info and next steps. --- .gitignore | 1 + gulpfile.js | 32 +++++--- package.json | 2 + tools/styles-builder/less/a2docs.less | 29 +++++++ .../colors/docs-material-palette.colors.less | 10 +++ .../less/mixins/docs.mixins.less | 44 +++++++++++ .../less/overrides/bootstrap.overrides.less | 21 ++++++ .../less/overrides/docs.a11y.overrides.less | 55 ++++++++++++++ .../overrides/docs.opacity.overrides.less | 35 +++++++++ .../less/overrides/docs.overrides.less | 75 +++++++++++++++++++ 10 files changed, 295 insertions(+), 9 deletions(-) create mode 100644 tools/styles-builder/less/a2docs.less create mode 100644 tools/styles-builder/less/colors/docs-material-palette.colors.less create mode 100644 tools/styles-builder/less/mixins/docs.mixins.less create mode 100644 tools/styles-builder/less/overrides/bootstrap.overrides.less create mode 100644 tools/styles-builder/less/overrides/docs.a11y.overrides.less create mode 100644 tools/styles-builder/less/overrides/docs.opacity.overrides.less create mode 100644 tools/styles-builder/less/overrides/docs.overrides.less diff --git a/.gitignore b/.gitignore index 9e91bc6a33..b6d52bd9b9 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,4 @@ plnkr.html public/docs/*/latest/guide/cheatsheet.json protractor-results.txt link-checker-results.txt +*a2docs.css diff --git a/gulpfile.js b/gulpfile.js index b2e93be14e..6c5a3a4490 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -22,7 +22,7 @@ var globby = require("globby"); // - because childProcess.kill does not work properly on windows var treeKill = require("tree-kill"); var blc = require("broken-link-checker"); - +var less = require('gulp-less'); var tslint = require('gulp-tslint'); // TODO: @@ -41,6 +41,7 @@ var EXAMPLES_PROTRACTOR_PATH = path.join(EXAMPLES_PATH, '_protractor'); var NOT_API_DOCS_GLOB = path.join(PUBLIC_PATH, './{docs/*/latest/!(api),!(docs)}/**/*.*'); var RESOURCES_PATH = path.join(PUBLIC_PATH, 'resources'); var LIVE_EXAMPLES_PATH = path.join(RESOURCES_PATH, 'live-examples'); +var STYLES_SOURCE_PATH = path.join(TOOLS_PATH, 'styles-builder/less'); var docShredder = require(path.resolve(TOOLS_PATH, 'doc-shredder/doc-shredder')); var exampleZipper = require(path.resolve(TOOLS_PATH, '_example-zipper/exampleZipper')); @@ -87,6 +88,7 @@ var _excludeMatchers = _excludePatterns.map(function(excludePattern){ var _exampleBoilerplateFiles = [ '.editorconfig', + 'a2docs.css', 'karma.conf.js', 'karma-test-shim.js', 'package.json', @@ -98,7 +100,7 @@ var _exampleBoilerplateFiles = [ 'wallaby.js' ]; -var _exampleDartWebBoilerPlateFiles = ['styles.css']; +var _exampleDartWebBoilerPlateFiles = ['a2docs.css', 'styles.css']; var _exampleProtractorBoilerplateFiles = [ 'tsconfig.json' @@ -106,6 +108,8 @@ var _exampleProtractorBoilerplateFiles = [ var _exampleConfigFilename = 'example-config.json'; +var _styleLessName = 'a2docs.less'; + // Gulp flags: // // --lang=[all | ts | js | dart | 'ts|js' | 'ts|js|dart' | ...] @@ -118,7 +122,7 @@ var _exampleConfigFilename = 'example-config.json'; var lang, langs, buildDartApiDocs = false; function configLangs(langOption) { const fullSiteBuildTasks = ['build-compile', 'check-serve', 'check-deploy']; - const buildAllDocs = argv['_'] && + const buildAllDocs = argv['_'] && fullSiteBuildTasks.some((task) => argv['_'].indexOf(task) >= 0); const langDefault = buildAllDocs ? 'all' : 'ts|js'; lang = (langOption || langDefault).toLowerCase(); @@ -190,7 +194,7 @@ function runE2e() { return spawnInfo.promise; }) .then(function() { - copyExampleBoilerplate(); + buildStyles(copyExampleBoilerplate, _.noop); gutil.log('runE2e: update webdriver'); spawnInfo = spawnExt('npm', ['run', 'webdriver:update'], {cwd: EXAMPLES_PROTRACTOR_PATH}); return spawnInfo.promise; @@ -414,7 +418,7 @@ gulp.task('help', taskListing.withFilters(function(taskName) { })); // requires admin access because it adds symlinks -gulp.task('add-example-boilerplate', function() { +gulp.task('add-example-boilerplate', function(done) { var realPath = path.join(EXAMPLES_PATH, '/node_modules'); var nodeModulesPaths = excludeDartPaths(getNodeModulesPaths(EXAMPLES_PATH)); @@ -430,16 +434,26 @@ gulp.task('add-example-boilerplate', function() { fsUtils.addSymlink(realPath, linkPath); }); - return copyExampleBoilerplate(); + return buildStyles(copyExampleBoilerplate, done); }); // copies boilerplate files to locations // where an example app is found -gulp.task('_copy-example-boilerplate', function () { - if (!argv.fast) copyExampleBoilerplate(); +gulp.task('_copy-example-boilerplate', function (done) { + if (!argv.fast) buildStyles(copyExampleBoilerplate, done); }); +//Builds Angular 2 Docs CSS file from Bootstrap npm LESS source +//and copies the result to the _examples folder to be included as +//part of the example boilerplate. +function buildStyles(cb, done){ + gulp.src(path.join(STYLES_SOURCE_PATH, _styleLessName)) + .pipe(less()) + .pipe(gulp.dest(EXAMPLES_PATH)).on('end', function(){ + cb().then(function() { done(); }); + }); +} // copies boilerplate files to locations // where an example app is found @@ -1251,7 +1265,7 @@ function buildApiDocsForDart() { dab.createApiDataAndJadeFiles(apiEntries); }).catch((err) => { - console.log(err); + console.error(err); }); } catch(err) { diff --git a/package.json b/package.json index e6d82ff58e..d5283e3f43 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,7 @@ "devDependencies": { "archiver": "^1.0.0", "assert-plus": "^1.0.0", + "bootstrap": "3.3.6", "broken-link-checker": "0.7.1", "browser-sync": "^2.9.3", "canonical-path": "0.0.2", @@ -43,6 +44,7 @@ "gulp": "^3.5.6", "gulp-env": "0.4.0", "gulp-sass": "^2.3.2", + "gulp-less": "^3.1.0", "gulp-task-listing": "^1.0.1", "gulp-tslint": "^5.0.0", "gulp-util": "^3.0.6", diff --git a/tools/styles-builder/less/a2docs.less b/tools/styles-builder/less/a2docs.less new file mode 100644 index 0000000000..9462e2d54f --- /dev/null +++ b/tools/styles-builder/less/a2docs.less @@ -0,0 +1,29 @@ +//Import angular 2 docs colors and mixins +@import './colors/docs-material-palette.colors.less'; +@import './mixins/docs.mixins.less'; +//Import Bootstrap scaffolding and variables +@import '../../../node_modules/bootstrap/less/scaffolding.less'; +@import '../../../node_modules/bootstrap/less/variables.less'; +//Override Bootstrap variables with custom values for angular 2 docs +@import './overrides/bootstrap.overrides.less'; +//Import Bootstrap layout systems +@import '../../../node_modules/bootstrap/less/mixins.less'; +@import '../../../node_modules/bootstrap/less/grid.less'; +@import '../../../node_modules/bootstrap/less/utilities.less'; +@import '../../../node_modules/bootstrap/less/responsive-utilities.less'; +//Import only Bootstrap elements to support angular 2 docs styles +@import '../../../node_modules/bootstrap/less/type.less'; +@import '../../../node_modules/bootstrap/less/forms.less'; +@import '../../../node_modules/bootstrap/less/buttons.less'; +@import '../../../node_modules/bootstrap/less/button-groups.less'; +@import '../../../node_modules/bootstrap/less/input-groups.less'; +@import '../../../node_modules/bootstrap/less/tables.less'; +@import '../../../node_modules/bootstrap/less/glyphicons.less'; +@import '../../../node_modules/bootstrap/less/alerts.less'; +@import '../../../node_modules/bootstrap/less/close.less'; +//Import opacity overrides +@import './overrides/docs.opacity.overrides.less'; +//Import styles overrides and angular 2 doc styles layouts +@import './overrides/docs.overrides.less'; +//Import accessibility tweaks +@import './overrides/docs.a11y.overrides.less'; diff --git a/tools/styles-builder/less/colors/docs-material-palette.colors.less b/tools/styles-builder/less/colors/docs-material-palette.colors.less new file mode 100644 index 0000000000..a9ee4cd821 --- /dev/null +++ b/tools/styles-builder/less/colors/docs-material-palette.colors.less @@ -0,0 +1,10 @@ +//Style guide colors +@material-header: #455A64; +@material-header-darker: #37474F; +@material-header-dark: #263238; +@material-200: #EEEEEE; +@material-700: #616161; +@material-800: #424242; +@material-900: #212121; +@black: #000000; +@white: #FFFFFF; diff --git a/tools/styles-builder/less/mixins/docs.mixins.less b/tools/styles-builder/less/mixins/docs.mixins.less new file mode 100644 index 0000000000..142c36fe67 --- /dev/null +++ b/tools/styles-builder/less/mixins/docs.mixins.less @@ -0,0 +1,44 @@ +.opacity(){ + opacity: 0.87; +} + +.top-header-text(){ + font-size: 50px; + font-weight: bold; +} + +.header-text(){ + font-size: 30px; + font-weight: bold; +} + +.sub-header-text(){ + font-size: 20px; + font-weight: 400; + line-height: 40px; +} + +.paragraph-text(){ + font-size: 16px; + font-weight: 400; + line-height: 28px; +} + +.label-text(){ + font-size: 16px; + font-weight: 400; + line-height: 28px; +} + +.button-text(){ + font-size: 16px; + font-weight: bold; + line-height: 28px; +} + +.input-text(){ + font-size: 16px; + font-weight: 400; + line-height: 28px; + margin-top: 15px; +} diff --git a/tools/styles-builder/less/overrides/bootstrap.overrides.less b/tools/styles-builder/less/overrides/bootstrap.overrides.less new file mode 100644 index 0000000000..24149caf11 --- /dev/null +++ b/tools/styles-builder/less/overrides/bootstrap.overrides.less @@ -0,0 +1,21 @@ +//Main Bootstrap color overrides +@gray-base: @black; +@gray-darker: @black; +@gray-dark: @material-900; +@gray: @material-800; +@gray-light: @material-700; +@gray-lighter: @material-200; + +//Font override +@font-family-sans-serif: "Roboto", Helvetica, Sans-Serif; + +//Glyphicons font path +@icon-font-path: "./node_modules/bootstrap/fonts/"; + +//Class overrides +@input-color: @gray-dark; +@text-color: @gray-dark; +@btn-primary-color: @gray-lighter; +@btn-primary-bg: @material-header-dark; +@btn-default-color: @white; +@btn-default-bg: @material-header-darker; diff --git a/tools/styles-builder/less/overrides/docs.a11y.overrides.less b/tools/styles-builder/less/overrides/docs.a11y.overrides.less new file mode 100644 index 0000000000..4ee1a14360 --- /dev/null +++ b/tools/styles-builder/less/overrides/docs.a11y.overrides.less @@ -0,0 +1,55 @@ +//Accessibility tweaks +@danger-text: #632827; +@state-danger-text: @danger-text; + +input { + &::-webkit-input-placeholder { + color: @gray-light !important; + } + + &::-moz-placeholder { + color: @gray-light !important; + } +} + +label { + input, textarea, select { + font-weight: normal; + } +} + +button.btn.disabled, +button.btn[disabled] { + color: @white; + background-color: @black; +} + +.btn.btn-primary:not(.disabled):not([disabled]) { + &:hover, + &:focus{ + color: @black; + background-color: @gray-lighter; + } +} + +.btn.btn-default:not(.disabled):not([disabled]) { + &:hover, + &:focus{ + color: @black; + background-color: @gray-lighter; + } +} + +button.btn.disabled, button.btn[disabled] { + &:hover, + &:focus{ + color: @white; + background-color: @black; + } +} + +.close, +.close:hover, +.close:focus{ + opacity: 1; +} diff --git a/tools/styles-builder/less/overrides/docs.opacity.overrides.less b/tools/styles-builder/less/overrides/docs.opacity.overrides.less new file mode 100644 index 0000000000..215ec29378 --- /dev/null +++ b/tools/styles-builder/less/overrides/docs.opacity.overrides.less @@ -0,0 +1,35 @@ +//Opacity tweaks + +h1, h2, h3, h4, h5, h6, .h1, .h2, .h3, .h4, .h5, .h6 { + .opacity(); +} + +label { + + input { + opacity: 1; + } + + .opacity(); +} + + +input { + .opacity(); +} + +.label-default { + .opacity(); +} + +legend { + .opacity(); +} + +button { + .opacity(); +} + +span, p, dl { + .opacity(); +} diff --git a/tools/styles-builder/less/overrides/docs.overrides.less b/tools/styles-builder/less/overrides/docs.overrides.less new file mode 100644 index 0000000000..f2803b3f86 --- /dev/null +++ b/tools/styles-builder/less/overrides/docs.overrides.less @@ -0,0 +1,75 @@ +//Design tweaks +h1 { + color: @material-header; + .top-header-text(); +} + +h2 { + color: @material-header; + .header-text(); +} + +h3, legend { + .sub-header-text(); +} + +label, .label-default { + .label-text(); +} + +input, textarea, select { + color: @gray-dark; + .input-text(); +} + +span, p, dl, .doc-text { + .paragraph-text(); +} + +button.btn { + .button-text(); +} + +.dl-horizontal dt { + text-align: left; +} + +.input-group-btn { + padding-top: 15px; + + button.btn { + padding-top: 2px; + padding-bottom: 3px; + } + +} + +.shadow-1 { + box-shadow: 0 1px 4px 0 rgba(0, 0, 0, 0.37); +} + +.showcase { + margin-bottom: 20px; +} + +.showcase .showcase-header { + background: #E0E0E0; + padding: 32px; +} + +.showcase .showcase-content { + padding: 32px; +} + +.main-header { + margin-bottom: 40px; +} + +a *{ + color:inherit; + + &:hover { + color: inherit; + } + +} From b796d4b97d3eff507023dce0c6c4d4d7fe9621f7 Mon Sep 17 00:00:00 2001 From: Patrice Chalin <chalin@users.noreply.github.com> Date: Mon, 8 Aug 2016 12:48:44 -0700 Subject: [PATCH 05/21] chore(dart-doc-sync): data files for component-styles and lifecycle-hooks (#2052) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Note that component-styles and lifecycle-hooks examples have been sync’d for a while, though they were missing `.docsync.json` files. --- public/docs/_examples/component-styles/dart/.docsync.json | 4 ++++ public/docs/_examples/lifecycle-hooks/dart/.docsync.json | 4 ++++ 2 files changed, 8 insertions(+) create mode 100644 public/docs/_examples/component-styles/dart/.docsync.json create mode 100644 public/docs/_examples/lifecycle-hooks/dart/.docsync.json diff --git a/public/docs/_examples/component-styles/dart/.docsync.json b/public/docs/_examples/component-styles/dart/.docsync.json new file mode 100644 index 0000000000..9dcb83d783 --- /dev/null +++ b/public/docs/_examples/component-styles/dart/.docsync.json @@ -0,0 +1,4 @@ +{ + "title": "Component Styles", + "docPart": "guide" +} diff --git a/public/docs/_examples/lifecycle-hooks/dart/.docsync.json b/public/docs/_examples/lifecycle-hooks/dart/.docsync.json new file mode 100644 index 0000000000..8e0aa032a4 --- /dev/null +++ b/public/docs/_examples/lifecycle-hooks/dart/.docsync.json @@ -0,0 +1,4 @@ +{ + "title": "Lifecycle Hooks", + "docPart": "guide" +} From 5ce8304227879970e26a894300d677e86dbcdee7 Mon Sep 17 00:00:00 2001 From: Patrice Chalin <chalin@users.noreply.github.com> Date: Mon, 8 Aug 2016 15:48:45 -0700 Subject: [PATCH 06/21] docs(dart): decouple from TS .jade files temporarily (#2044) Addresses #2043. --- .../docs/dart/latest/guide/architecture.jade | 2 +- .../latest/guide/attribute-directives.jade | 2 +- .../dart/latest/guide/component-styles.jade | 2 +- .../latest/guide/dependency-injection.jade | 2 +- .../dart/latest/guide/displaying-data.jade | 2 +- .../hierarchical-dependency-injection.jade | 2 +- .../dart/latest/guide/lifecycle-hooks.jade | 2 +- public/docs/dart/latest/guide/pipes.jade | 2 +- .../latest/guide/server-communication.jade | 2 +- .../latest/guide/structural-directives.jade | 2 +- .../dart/latest/guide/template-syntax.jade | 2 +- public/docs/dart/latest/quickstart.jade | 2 +- public/docs/dart/latest/tutorial/toh-pt6.jade | 2 +- public/docs/ts/_cache/_quickstart_repo.jade | 1 + public/docs/ts/_cache/_util-fns.jade | 1 + public/docs/ts/_cache/guide/architecture.jade | 597 +++++++ .../ts/_cache/guide/attribute-directives.jade | 372 ++++ .../ts/_cache/guide/component-styles.jade | 319 ++++ .../ts/_cache/guide/dependency-injection.jade | 897 ++++++++++ .../docs/ts/_cache/guide/displaying-data.jade | 283 ++++ .../hierarchical-dependency-injection.jade | 178 ++ .../docs/ts/_cache/guide/lifecycle-hooks.jade | 574 +++++++ public/docs/ts/_cache/guide/pipes.jade | 487 ++++++ public/docs/ts/_cache/guide/security.jade | 263 +++ .../ts/_cache/guide/server-communication.jade | 702 ++++++++ .../_cache/guide/structural-directives.jade | 336 ++++ .../docs/ts/_cache/guide/template-syntax.jade | 1502 +++++++++++++++++ public/docs/ts/_cache/quickstart.jade | 582 +++++++ public/docs/ts/_cache/tutorial/toh-pt6.jade | 632 +++++++ scripts/refresh-cache.sh | 44 + 30 files changed, 7783 insertions(+), 13 deletions(-) create mode 100644 public/docs/ts/_cache/_quickstart_repo.jade create mode 100644 public/docs/ts/_cache/_util-fns.jade create mode 100644 public/docs/ts/_cache/guide/architecture.jade create mode 100644 public/docs/ts/_cache/guide/attribute-directives.jade create mode 100644 public/docs/ts/_cache/guide/component-styles.jade create mode 100644 public/docs/ts/_cache/guide/dependency-injection.jade create mode 100644 public/docs/ts/_cache/guide/displaying-data.jade create mode 100644 public/docs/ts/_cache/guide/hierarchical-dependency-injection.jade create mode 100644 public/docs/ts/_cache/guide/lifecycle-hooks.jade create mode 100644 public/docs/ts/_cache/guide/pipes.jade create mode 100644 public/docs/ts/_cache/guide/security.jade create mode 100644 public/docs/ts/_cache/guide/server-communication.jade create mode 100644 public/docs/ts/_cache/guide/structural-directives.jade create mode 100644 public/docs/ts/_cache/guide/template-syntax.jade create mode 100644 public/docs/ts/_cache/quickstart.jade create mode 100644 public/docs/ts/_cache/tutorial/toh-pt6.jade create mode 100755 scripts/refresh-cache.sh diff --git a/public/docs/dart/latest/guide/architecture.jade b/public/docs/dart/latest/guide/architecture.jade index f5f63c07a8..9bf1256210 100644 --- a/public/docs/dart/latest/guide/architecture.jade +++ b/public/docs/dart/latest/guide/architecture.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/architecture.jade +extends ../../../ts/_cache/guide/architecture.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/attribute-directives.jade b/public/docs/dart/latest/guide/attribute-directives.jade index 350cd60b88..eda47d20f5 100644 --- a/public/docs/dart/latest/guide/attribute-directives.jade +++ b/public/docs/dart/latest/guide/attribute-directives.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/attribute-directives.jade +extends ../../../ts/_cache/guide/attribute-directives.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/component-styles.jade b/public/docs/dart/latest/guide/component-styles.jade index 967dc2d8b0..a101402e74 100644 --- a/public/docs/dart/latest/guide/component-styles.jade +++ b/public/docs/dart/latest/guide/component-styles.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/component-styles.jade +extends ../../../ts/_cache/guide/component-styles.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/dependency-injection.jade b/public/docs/dart/latest/guide/dependency-injection.jade index 7236d24c7d..c840601347 100644 --- a/public/docs/dart/latest/guide/dependency-injection.jade +++ b/public/docs/dart/latest/guide/dependency-injection.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/dependency-injection.jade +extends ../../../ts/_cache/guide/dependency-injection.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/displaying-data.jade b/public/docs/dart/latest/guide/displaying-data.jade index 9207073069..5a9a1cdffb 100644 --- a/public/docs/dart/latest/guide/displaying-data.jade +++ b/public/docs/dart/latest/guide/displaying-data.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/displaying-data.jade +extends ../../../ts/_cache/guide/displaying-data.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/hierarchical-dependency-injection.jade b/public/docs/dart/latest/guide/hierarchical-dependency-injection.jade index 153e3c8108..4214572991 100644 --- a/public/docs/dart/latest/guide/hierarchical-dependency-injection.jade +++ b/public/docs/dart/latest/guide/hierarchical-dependency-injection.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/hierarchical-dependency-injection.jade +extends ../../../ts/_cache/guide/hierarchical-dependency-injection.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/lifecycle-hooks.jade b/public/docs/dart/latest/guide/lifecycle-hooks.jade index 5a57169bbb..c67c1fa3f2 100644 --- a/public/docs/dart/latest/guide/lifecycle-hooks.jade +++ b/public/docs/dart/latest/guide/lifecycle-hooks.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/lifecycle-hooks.jade +extends ../../../ts/_cache/guide/lifecycle-hooks.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/pipes.jade b/public/docs/dart/latest/guide/pipes.jade index ae4e3aeead..e444a67771 100644 --- a/public/docs/dart/latest/guide/pipes.jade +++ b/public/docs/dart/latest/guide/pipes.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/pipes.jade +extends ../../../ts/_cache/guide/pipes.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/server-communication.jade b/public/docs/dart/latest/guide/server-communication.jade index 4064b7ec97..ef79633e6d 100644 --- a/public/docs/dart/latest/guide/server-communication.jade +++ b/public/docs/dart/latest/guide/server-communication.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/server-communication.jade +extends ../../../ts/_cache/guide/server-communication.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/structural-directives.jade b/public/docs/dart/latest/guide/structural-directives.jade index ecae8ac2ee..2637084cbd 100644 --- a/public/docs/dart/latest/guide/structural-directives.jade +++ b/public/docs/dart/latest/guide/structural-directives.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/structural-directives.jade +extends ../../../ts/_cache/guide/structural-directives.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/guide/template-syntax.jade b/public/docs/dart/latest/guide/template-syntax.jade index 71878e7b94..b754481889 100644 --- a/public/docs/dart/latest/guide/template-syntax.jade +++ b/public/docs/dart/latest/guide/template-syntax.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/guide/template-syntax.jade +extends ../../../ts/_cache/guide/template-syntax.jade block includes include ../_util-fns diff --git a/public/docs/dart/latest/quickstart.jade b/public/docs/dart/latest/quickstart.jade index f3d81db102..8126efc8dc 100644 --- a/public/docs/dart/latest/quickstart.jade +++ b/public/docs/dart/latest/quickstart.jade @@ -1,4 +1,4 @@ -extends ../../ts/latest/quickstart.jade +extends ../../ts/_cache/quickstart.jade block includes include _util-fns diff --git a/public/docs/dart/latest/tutorial/toh-pt6.jade b/public/docs/dart/latest/tutorial/toh-pt6.jade index bee08c95de..888bdb78bf 100644 --- a/public/docs/dart/latest/tutorial/toh-pt6.jade +++ b/public/docs/dart/latest/tutorial/toh-pt6.jade @@ -1,4 +1,4 @@ -extends ../../../ts/latest/tutorial/toh-pt6.jade +extends ../../../ts/_cache/tutorial/toh-pt6.jade block includes include ../_util-fns diff --git a/public/docs/ts/_cache/_quickstart_repo.jade b/public/docs/ts/_cache/_quickstart_repo.jade new file mode 100644 index 0000000000..c11222793b --- /dev/null +++ b/public/docs/ts/_cache/_quickstart_repo.jade @@ -0,0 +1 @@ +include ../latest/_quickstart_repo diff --git a/public/docs/ts/_cache/_util-fns.jade b/public/docs/ts/_cache/_util-fns.jade new file mode 100644 index 0000000000..b44b0e2959 --- /dev/null +++ b/public/docs/ts/_cache/_util-fns.jade @@ -0,0 +1 @@ +include ../latest/_util-fns diff --git a/public/docs/ts/_cache/guide/architecture.jade b/public/docs/ts/_cache/guide/architecture.jade new file mode 100644 index 0000000000..3510c864bb --- /dev/null +++ b/public/docs/ts/_cache/guide/architecture.jade @@ -0,0 +1,597 @@ +block includes + include ../_util-fns + - var _library_module = 'library module' + - var _at_angular = '@angular' + +:marked + Angular 2 is a framework to help us build client applications in HTML and + either JavaScript or a language (like Dart or TypeScript) that compiles to JavaScript. + +block angular-parts + :marked + The framework consists of several cooperating libraries, some of them core and some optional. + +:marked + With Angular, we write applications by composing HTML *templates* with Angularized-markup, + writing *component* classes to manage those templates, adding application logic in *services*, + and handing the top root component to Angular's *bootstrapper*. + + Angular takes over, presenting our application content in a browser and + responding to user interactions according to the instructions we provided. + + Of course there is more to it than this. + We'll learn the details when we dive into the guide chapters. + Let's get the big picture first. + +figure + img(src="/resources/images/devguide/architecture/overview2.png" alt="overview" style="margin-left:-40px;" width="700") + +:marked + The architecture diagram identifies the eight main building blocks of an Angular 2 application: + + 1. [Modules](#modules) + 1. [Components](#components) + 1. [Templates](#templates) + 1. [Metadata](#metadata) + 1. [Data binding](#data-binding) + 1. [Directives](#directives) + 1. [Services](#services) + 1. [Dependency injection](#dependency-injection) + + Learn these, and we're on our way. + +.l-sub-section + p The code referenced in this chapter is available as a #[+liveExampleLink2()]. + +.l-main-section +:marked + ## Modules +figure + img(src="/resources/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" ) +:marked + Angular apps are modular. + + In general we assemble our application from many **modules**. + + A typical module is a cohesive block of code dedicated to a single purpose. + A module **exports** something of value in that code, typically one thing such as a class. + <br clear="all"><br> + +block modules-in-dart + //- N/A + +block modules-are-optional + .l-sub-section + :marked + ### Modules are optional + We highly recommend modular design. TypeScript has great support for ES2015 module syntax and our chapters assume we're taking a modular + approach using that syntax. That's why we list *Module* among the basic building blocks. + + Angular itself doesn't require a modular approach nor this particular syntax. Don't use it if you don't want it. + Each chapter has plenty to offer after you steer clear of the `import` and `export` statements. + + Find setup and organization clues in the JavaScript track (select it from the combo-box at the top of this page) + which demonstrates Angular 2 development with plain old JavaScript and no module system. + +- var _app_comp_filename = _docsFor == 'dart' ? 'app_component.dart' : 'app.component.ts'; +:marked + Perhaps the first module we meet is a module that exports a *component* class. + The component is one of the basic Angular blocks, we write a lot of them, + and we'll talk about components in the next segment. For the moment it is enough to know that a + component class is the kind of thing we'd export from a module. + + Most applications have an `AppComponent`. By convention, we'll find it in a file named `!{_app_comp_filename}`. + Look inside such a file and we'll see a declaration such as this one. + ++makeExcerpt('app/app.component.ts ()', 'export') + +block export-qualifier + :marked + The `export` statement tells TypeScript that this is a module whose + `AppComponent` class is public and accessible to other modules of the application. + +:marked + When we need a reference to the `AppComponent`, we **import** it like this: + ++makeExcerpt('app/main.ts', 'import') + +block ts-import + :marked + The `import` statement tells the system it can get an `AppComponent` from a module named `app.component` + located in a neighboring file. + The **module name** (AKA module id) is often the same as the filename without its extension. + +:marked + ### Libraries + +figure + img(src="/resources/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" ) + +block angular-library-modules + :marked + Some modules are _libraries_ of other modules. + Angular itself ships as a collection of library modules within several npm packages. + Their names begin with the `!{_at_angular}` prefix. + + Each Angular library contains a [barrel](../glossary.html#barrel) module + that is actually a public façade over several logically-related private modules. + +:marked + `!{_at_angular}/core` is the primary Angular library from which we get most of what we need. + <br clear="all"> + + There are other important Angular libraries too, such as `!{_at_angular}/common`<span if-docs="ts">, + `!{_at_angular}/http`</span> and `!{_at_angular}/router`. + We import what we need from an Angular !{_library_module}. + +block angular-imports + :marked + For example, we import the Angular **`Component` *function*** from `@angular/core` like this: + +makeExcerpt('app/app.component.ts', 'import') + :marked + Compare that syntax to our previous import of `AppComponent`. + +makeExcerpt('app/main.ts', 'import') + + :marked + Notice the difference? + In the first case, when importing from an Angular library module, + the import statement refers to the bare module name, `@angular/core`, *without a path prefix*. + + When we import from one of *our* own files, we prefix the module name with the file path. + In this example we specify a relative file path (`./`). That means the + source module is in the same folder (`./`) as the module importing it. + We could path up and around the application folder structure if the source module were somewhere else. + .l-sub-section + :marked + We import and export in the ECMAScript 2015 (ES2015) module syntax. + Learn more about that syntax [here](http://www.2ality.com/2014/09/es6-modules-final.html) + and many other places on the web. + + The infrastructure *behind* module loading and importing is an important subject. + But it's a subject outside the scope of this introduction to Angular. + While we're focused on our application, *import* and *export* + is about all we need to know. + +- var _export = _docsFor == 'dart' ? 'publicly declare' : 'export'; +- var _declare = _docsFor == 'dart' ? 'declare' : 'export'; +:marked + The key take-aways are: + * Angular apps are composed of modules. + * Modules !{_export} things — classes, function, values — that other modules import. + * We prefer to write our application as a collection of modules, each module exporting one thing. + + The first module we write will most likely !{_declare} a component. + +.l-main-section +:marked + ## Components +figure + img(src="/resources/images/devguide/architecture/hero-component.png" alt="Component" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) + +:marked + A **component** controls a patch of screen real estate that we could call a *view*. + The shell at the application root with navigation links, a list of heroes, a hero editor ... + they're all views controlled by components. + + We define a component's application logic — what it does to support the view — inside a class. + The class interacts with the view through an API of properties and methods. + + <a id="component-code"></a> + A `HeroListComponent`, for example, might have a `heroes` property that returns !{_an} !{_array} of heroes + that it acquired from a service. + It might have a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. + The component might be a class like this: + ++makeExcerpt('app/hero-list.component.ts', 'class') +:marked + Angular creates, updates, and destroys components as the user moves through the application. + The developer can take action at each moment in this lifecycle through optional [lifecycle hooks](lifecycle-hooks.html), like `ngOnInit()` declared above. +.l-sub-section + :marked + We may wonder who is calling the component's constructor? Who provides the service parameter? + For the moment, have faith that Angular will call the constructor and deliver an + appropriate `HeroService` when we need it. + +.l-main-section +:marked + ## Templates +figure + img(src="/resources/images/devguide/architecture/template.png" alt="Template" align="left" style="width:200px; margin-left:-40px;margin-right:10px" ) +:marked + We define a component's view with its companion **template**. A template is a form of HTML + that tells Angular how to render the component. + + A template looks like regular HTML much of the time ... and then it gets a bit strange. Here is a + template for our `HeroListComponent`: + ++makeExample('app/hero-list.component.html') + +:marked + This template features typical HTML elements like `<h2>` and `<p>`. + But what are `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>`? + + These are examples of Angular's [template syntax](template-syntax.html). + We will grow accustomed to that syntax and may even learn to love it. + We'll begin to explain it in a moment. + + Before we do, focus attention on the last line. + The `<hero-detail>` tag is a custom element representing the `HeroDetailComponent`. + + The `HeroDetailComponent` is a *different* component than the `HeroListComponent` we've been reviewing. + The `HeroDetailComponent` (code not shown) presents facts about a particular hero, the + hero that the user selects from the list presented by the `HeroListComponent`. + The `HeroDetailComponent` is a **child** of the `HeroListComponent`. + +figure + img(src="/resources/images/devguide/architecture/component-tree.png" alt="Metadata" align="left" style="width:300px; margin-left:-40px;margin-right:10px" ) +:marked + Notice how `<hero-detail>` rests comfortably among native HTML elements. + We can and _will_ mix our custom components with native HTML in the same layouts. + + In this manner we'll compose complex component trees to build out our richly featured application. +<br clear="all"> + +.l-main-section +:marked + ## Metadata +figure + img(src="/resources/images/devguide/architecture/metadata.png" alt="Metadata" align="left" style="width:150px; margin-left:-40px;margin-right:10px" ) + +:marked +<p style="padding-top:10px">Metadata tells Angular how to process a class.</p> +<br clear="all"> +:marked + [Looking back at the code](#component-code) for `HeroListComponent`, we see that it's just a class. + There is no evidence of a framework, no "Angular" in it at all. + + In fact, it really is *just a class*. It's not a component until we *tell Angular about it*. + + We tell Angular that `HeroListComponent` is a component by attaching **metadata** to the class. + + In !{_Lang}, we attach metadata by using !{_a} **!{_decorator}**. + Here's some metadata for `HeroListComponent`: + ++makeExcerpt('app/hero-list.component.ts', 'metadata') + +:marked + Here we see the `@Component` !{_decorator} which (no surprise) identifies the class + immediately below it as a component class. + +block ts-decorator + :marked + A decorator is a function. Decorators often have a configuration parameter. + The `@Component` decorator takes a required configuration object with the + information Angular needs to create and present the component and its view. + + Here are a few of the possible `@Component` configuration options: + +:marked + - `selector`: CSS selector that tells Angular to create and insert an instance of this component + where it finds a `<hero-list>` tag in *parent* HTML. + For example, if an app's HTML contains `<hero-list></hero-list>`, then + Angular inserts an instance of the `HeroListComponent` view between those tags. + + - `templateUrl`: address of this component's template, which we showed [above](#templates). + + - `directives`: !{_array} of the components or directives that *this* template requires. + We saw in the last line of our template that we expect Angular to insert a `HeroDetailComponent` + in the space indicated by `<hero-detail>` tags. + Angular will do so only if we mention the `HeroDetailComponent` in this `directives` !{_array}. + + - `providers`: !{_array} of **dependency injection providers** for services that the component requires. + This is one way to tell Angular that our component's constructor requires a `HeroService` + so it can get the list of heroes to display. We'll get to dependency injection later. + +figure + img(src="/resources/images/devguide/architecture/template-metadata-component.png" alt="Metadata" align="left" style="height:200px; margin-left:-40px;margin-right:10px" ) + +:marked + Angular reads the metadata specified by the `@Component` + annotation. That's how Angular learns to do "the right thing". + + The template, metadata, and component together describe a view. + + We apply other metadata !{_decorator}s in a similar fashion to guide Angular behavior. + `@Injectable`, `@Input`, and `@Output` are a few of the more popular !{_decorator}s + we'll master as our Angular knowledge grows. +<br clear="all"> +:marked + The architectural takeaway is that we must add metadata to our code + so that Angular knows what to do. + +.l-main-section +:marked + ## Data binding + Without a framework, we would be responsible for pushing data values into the HTML controls and turning user responses + into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to + read as any experienced jQuery programmer can attest. +figure + img(src="/resources/images/devguide/architecture/databinding.png" alt="Data Binding" style="width:220px; float:left; margin-left:-40px;margin-right:20px" ) +:marked + Angular supports **data binding**, + a mechanism for coordinating parts of a template with parts of a component. + We add binding markup to the template HTML to tell Angular how to connect both sides. + + There are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions — + as indicated by the arrows in the diagram. +<br clear="all"> +:marked + We saw three forms of data binding in our [example](#templates) template: + ++makeExcerpt('app/hero-list.component.1.html', 'binding') + +:marked + * The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation) + displays the component's `hero.name` property value within the `<li>` tags. + + * The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the value of `selectedHero` from + the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`. + + * The `(click)` [*event binding*](user-input.html#click) calls the component's `selectHero` method when the user clicks a hero's name. + + **Two-way data binding** is an important fourth form + that combines property and event binding in a single notation, using the `ngModel` directive. + We didn't have a two-way binding in the `HeroListComponent` template; + here's an example from the `HeroDetailComponent` template: + ++makeExcerpt('app/hero-detail.component.html', 'ngModel') + +:marked + In two-way binding, a data property value flows to the input box from the component as with property binding. + The user's changes also flow back to the component, resetting the property to the latest value, + as with event binding. + + Angular processes *all* data bindings once per JavaScript event cycle, + from the root of the application component tree down to the leaves. + +figure + img(src="/resources/images/devguide/architecture/component-databinding.png" alt="Data Binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" ) +:marked + We don't know all the details yet, + but it's clear from these examples that data binding plays an important role in communication + between a template and its component. +<br clear="all"> +figure + img(src="/resources/images/devguide/architecture/parent-child-binding.png" alt="Parent/Child binding" style="float:left; width:300px; margin-left:-40px;margin-right:10px" ) +:marked + Data binding is also important for communication between parent and child components. +<br clear="all"> + +.l-main-section +:marked + ## Directives +figure + img(src="/resources/images/devguide/architecture/directive.png" alt="Parent child" style="float:left; width:150px; margin-left:-40px;margin-right:10px" ) +:marked + Angular templates are *dynamic*. When Angular renders them, it transforms the DOM + according to the instructions given by **directives**. + + A directive is a class with directive metadata. In !{_Lang} we apply the `@Directive` !{_decorator} + to attach metadata to the class. +<br clear="all"> +:marked + We already met one form of directive: the component. A component is a *directive-with-a-template*; + a `@Component` !{_decorator} is actually a `@Directive` !{_decorator} extended with template-oriented features. + +.l-sub-section + :marked + While **a component is technically a directive**, + components are so distinctive and central to Angular applications that we chose + to separate components from directives in this architectural overview. +:marked + Two *other* kinds of directives exist: _structural_ and _attribute_ directives. + + They tend to appear within an element tag as attributes do, + sometimes by name but more often as the target of an assignment or a binding. + + **Structural** directives alter layout by adding, removing, and replacing elements in DOM. + + Our [example](#templates) template uses two built-in structural directives: + ++makeExcerpt('app/hero-list.component.1.html', 'structural') + +:marked + * [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `<li>` per hero in the `heroes` list. + * [`*ngIf`](displaying-data.html#ngIf) includes the `HeroDetail` component only if a selected hero exists. + +block dart-bool + //- N/A + +:marked + **Attribute** directives alter the appearance or behavior of an existing element. + In templates they look like regular HTML attributes, hence the name. + + The `ngModel` directive, which implements two-way data binding, is + an example of an attribute directive. `ngModel` modifies the behavior of + an existing element (typically an `<input>`) + by setting its display value property and responding to change events. + ++makeExcerpt('app/hero-detail.component.html', 'ngModel') +:marked + Angular ships with a small number of other directives that either alter the layout structure + (for example, [ngSwitch](template-syntax.html#ngSwitch)) + or modify aspects of DOM elements and components + (for example, [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)). + + Of course, we can also write our own directives. Components such as + `HeroListComponent` are one kind of custom directive. + <!-- PENDING: link to where to learn more about other kinds! --> + +.l-main-section +:marked + ## Services +figure + img(src="/resources/images/devguide/architecture/service.png" alt="Service" style="float:left; margin-left:-40px;margin-right:10px" ) +:marked + _Service_ is a broad category encompassing any value, function, or feature that our application needs. + + Almost anything can be a service. + A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well. +<br clear="all"> +:marked + Examples include: + * logging service + * data service + * message bus + * tax calculator + * application configuration + + There is nothing specifically _Angular_ about services. Angular itself has no definition of a service. + There is no service base class, and no place to register a service. + + Yet services are fundamental to any Angular application. Our components are big consumers of services. + + Here's an example of a service class that logs to the browser console + ++makeExcerpt('app/logger.service.ts', 'class') + +:marked + Here's a `HeroService` that fetches heroes and returns them in a resolved !{_PromiseLinked}. + The `HeroService` depends on the `Logger` service and another `BackendService` that handles the server communication grunt work. + ++makeExcerpt('app/hero.service.ts', 'class') + +:marked + Services are everywhere. + + We prefer our component classes lean. Our components don't fetch data from the server, + they don't validate user input, and they don't log directly to the console. + They delegate such tasks to services. + + A component's job is to enable the user experience and nothing more. It mediates between the view (rendered by the template) + and the application logic (which often includes some notion of a _model_). + A good component presents properties and methods for data binding. + It delegates everything nontrivial to services. + + Angular doesn't *enforce* these principles. + It won't complain if we write a "kitchen sink" component with 3000 lines. + + Angular does help us *follow* these principles by making it easy to factor our + application logic into services and make those services available to components through *dependency injection*. + +.l-main-section +:marked + ## Dependency injection +figure + img(src="/resources/images/devguide/architecture/dependency-injection.png" alt="Service" style="float:left; width:200px; margin-left:-40px;margin-right:10px" ) +:marked + _Dependency injection_ is a way to supply a new instance of a class + with the fully-formed dependencies it requires. Most dependencies are services. + Angular uses dependency injection to provide new components with the services they need. +<br clear="all"> +:marked + Angular can tell which services a component needs by looking at the types of its constructor parameters. + For example, the constructor of our `HeroListComponent` needs a `HeroService`: + ++makeExcerpt('app/hero-list.component.ts (constructor)', 'ctor') + +:marked + When Angular creates a component, it first asks an **injector** for + the services that the component requires. + + An injector maintains a container of service instances that it has previously created. + If a requested service instance is not in the container, the injector makes one and adds it to the container + before returning the service to Angular. + When all requested services have been resolved and returned, + Angular can call the component's constructor with those services as arguments. + This is what we mean by *dependency injection*. + + The process of `HeroService` injection looks a bit like this: +figure + img(src="/resources/images/devguide/architecture/injector-injects.png" alt="Service" ) +:marked + If the injector doesn't have a `HeroService`, how does it know how to make one? + + In brief, we must have previously registered a **provider** of the `HeroService` with the injector. + A provider is something that can create or return a service, typically the service class itself. + + We can register providers at any level of the application component tree. + We often do so at the root when we bootstrap the application so that + the same instance of a service is available everywhere. + ++makeExcerpt('app/main.ts', 'bootstrap') + +:marked + Alternatively, we might register at a component level, in the providers property of the `@Component` metadata: + ++makeExcerpt('app/hero-list.component.ts', 'providers') + +:marked + Registering at a component level means we get a new instance of the + service with each new instance of that component. + + <!-- We've vastly oversimplified dependency injection for this overview. + The full story is in the [Dependency Injection](dependency-injection.html) chapter. --> + + Points to remember about dependency injection: + + * Dependency injection is wired into the Angular framework and used everywhere. + + * The *injector* is the main mechanism. + * An injector maintains a *container* of service instances that it created. + * An injector can create a new service instance from a *provider*. + + * A *provider* is a recipe for creating a service. + + * We register *providers* with injectors. + +.l-main-section +:marked + ## Wrap up + + We've learned just a bit about the eight main building blocks of an Angular application: + + 1. [Modules](#modules) + 1. [Components](#components) + 1. [Templates](#templates) + 1. [Metadata](#metadata) + 1. [Data binding](#data-binding) + 1. [Directives](#directives) + 1. [Services](#services) + 1. [Dependency injection](#dependency-injection) + + That's a foundation for everything else in an Angular application, + and it's more than enough to get going. + But it doesn't include everything we'll need or want to know. + + Here is a brief, alphabetical list of other important Angular features and services. + Most of them are covered in this Developers Guide (or soon will be). + + > [**Animations**](animations.html): The animation library makes it easy for developers to animate component behavior + without deep knowledge of animation techniques or CSS. + + > **Bootstrap**: A method to configure and launch the root application component. + + > **Change detection**: Learn how Angular decides that a component property value has changed and + when to update the screen. + Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies. + + > **Component router**: With the component Router service, users can navigate a multi-screen application + in a familiar web browsing style using URLs. + + > **Events**: The DOM raises events. So can components and services. Angular offers mechanisms for + publishing and subscribing to events. + + > [**Forms**](forms.html): Support complex data entry scenarios with HTML-based validation and dirty checking. + + > [**HTTP**](server-communication.html): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client. + + > [**Lifecycle hooks**](lifecycle-hooks.html): We can tap into key moments in the lifetime of a component, from its creation to its destruction, + by implementing the lifecycle hook interfaces. + + > [**Pipes**](pipes.html): Services that transform values for display. + We can put pipes in our templates to improve the user experience. Consider + this `currency` pipe expression: +<div style="margin-left:40px"> +code-example(). + price | currency:'USD':true +</div> +:marked + > It displays a price of "42.33" as `$42.33`. + + > [**Router**](router.html): Navigate from page to page within the client + application and never leave the browser. + + > [**Testing**](testing.html): Angular provides a + [testing library](https://pub.dartlang.org/packages/angular2_testing) + to run unit tests on our application parts as they interact with the Angular framework. diff --git a/public/docs/ts/_cache/guide/attribute-directives.jade b/public/docs/ts/_cache/guide/attribute-directives.jade new file mode 100644 index 0000000000..e6f0db9472 --- /dev/null +++ b/public/docs/ts/_cache/guide/attribute-directives.jade @@ -0,0 +1,372 @@ +block includes + include ../_util-fns + +:marked + An **Attribute** directive changes the appearance or behavior of a DOM element. + +:marked + In this chapter we will + * [write an attribute directive to change the background color](#write-directive) + * [apply the attribute directive to an element in a template](#apply-directive) + * [respond to user-initiated events](#respond-to-user) + * [pass values into the directive using data binding](#bindings) + + Try the <live-example></live-example>. + + ## Directives overview + + There are three kinds of directives in Angular: + 1. Components + 1. Structural directives + 1. Attribute directives + + A *Component* is really a directive with a template. + It's the most common of the three directives and we tend to write lots of them as we build applications. + + [*Structural* directives](structural-directives.html) can change the DOM layout by adding and removing DOM elements. + [NgFor](template-syntax.html#ngFor) and [NgIf](template-syntax.html#ngIf) are two familiar examples. + + An *Attribute* directive can change the appearance or behavior of an element. + The built-in [NgStyle](template-syntax.html#ngStyle) directive, for example, + can change several element styles at the same time. + + We are going to write our own attribute directive to set an element's background color + when the user hovers over that element. +.l-sub-section + :marked + We don't need *any* directive to simply set the background color. + We can set it with the special [Style Binding](template-syntax.html#style-binding) like this: + + +makeExample('attribute-directives/ts/app/app.component.1.html','p-style-background') + + :marked + That wouldn't be nearly as much fun as creating our own directive. + + Besides, we're not just *setting* the color; we'll be *changing* the color + in response to a user action, a mouse hover. + +.l-main-section +a#write-directive +:marked + ## Build a simple attribute directive + An attribute directive minimally requires building a controller class annotated with + `@Directive`, which specifies the selector identifying + the attribute associated with the directive. + The controller class implements the desired directive behavior. + + Let's build a small illustrative example together. + +:marked + ### Our first draft + Create a new project folder (`attribute-directives`) and follow the steps in the [QuickStart](../quickstart.html). + +include ../_quickstart_repo +:marked + Create the following source file in the indicated folder with the given code: ++makeExample('app/highlight.directive.1.ts') + +block highlight-directive-1 + :marked + We begin by importing some symbols from the Angular `core`. + We need the `Directive` symbol for the `@Directive` decorator. + We need the `ElementRef` to [inject](dependency-injection.html) into the directive's constructor + so we can access the DOM element. + We don't need `Input` immediately but we will need it later in the chapter. + + Then we define the directive metadata in a configuration object passed + as an argument to the `@Directive` decorator function. +:marked + `@Directive` requires a CSS selector to identify + the HTML in the template that is associated with our directive. + The [CSS selector for an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors) + is the attribute name in square brackets. + Our directive's selector is `[myHighlight]`. + Angular will locate all elements in the template that have an attribute named `myHighlight`. +.l-sub-section + :marked + ### Why not call it "highlight"? + *highlight* is a nicer name than *myHighlight* and, technically, it would work if we called it that. + + However, we recommend picking a selector name with a prefix to ensure + that it cannot conflict with any standard HTML attribute, now or in the future. + There is also less risk of colliding with a third-party directive name when we give ours a prefix. + + We do **not** prefix our `highlight` directive name with **`ng`**. + That prefix belongs to Angular. + + We need a prefix of our own, preferably short, and `my` will do for now. +p + | After the #[code @Directive] metadata comes the directive's controller class, which contains the logic for the directive. + +ifDocsFor('ts') + | We export `HighlightDirective` to make it accessible to other components. +:marked + Angular creates a new instance of the directive's controller class for + each matching element, injecting an Angular `ElementRef` + into the constructor. + `ElementRef` is a service that grants us direct access to the DOM element + through its `nativeElement` property. + That's all we need to set the element's background color using the browser DOM API. + +.l-main-section +a#apply-directive +:marked + ## Apply the attribute directive + The `AppComponent` in this sample is a test harness for our `HighlightDirective`. + Let's give it a new template that + applies the directive as an attribute to a paragraph (`p`) element. + In Angular terms, the `<p>` element will be the attribute **host**. +p + | We'll put the template in its own + code #[+adjExPath('app.component.html')] + | file that looks like this: ++makeExample('attribute-directives/ts/app/app.component.1.html',null,'app/app.component.html')(format=".") +:marked + A separate template file is clearly overkill for a 2-line template. + Hang in there; we're going to expand it later. + Meanwhile, we'll revise the `AppComponent` to reference this template. ++makeExample('attribute-directives/ts/app/app.component.ts',null,'app/app.component.ts') +:marked + We've added an `import` statement to fetch the 'Highlight' directive and, + added that class to a `directives` component metadata so that Angular + will recognize our directive when it encounters `myHighlight` in the template. + + We run the app and see that our directive highlights the paragraph text. + +figure.image-display + img(src="/resources/images/devguide/attribute-directives/first-highlight.png" alt="First Highlight") +.l-sub-section + :marked + ### Your directive isn't working? + + Did you remember to set the `directives` attribute of `@Component`? It is easy to forget! + + Open the console in the browser tools and look for an error like this: + code-example(format="nocode"). + EXCEPTION: Template parse errors: + Can't bind to 'myHighlight' since it isn't a known native property + :marked + Angular detects that we're trying to bind to *something* but it doesn't know what. + We have to tell it by listing `HighlightDirective` in the `directives` metadata array. +:marked + Let's recap what happened. + + Angular found the `myHighlight` attribute on the `<p>` element. It created + an instance of the `HighlightDirective` class, + injecting a reference to the element into the constructor + where we set the `<p>` element's background style to yellow. + +.l-main-section +a#respond-to-user +:marked + ## Respond to user action + + We are not satisfied to simply set an element color. + Our directive should set the color in response to a user action. + Specifically, we want to set the color when the user hovers over an element. + + We'll need to + 1. detect when the user hovers into and out of the element, + 2. respond to those actions by setting and clearing the highlight color, respectively. + + We apply the `@HostListener` !{_decorator} to methods which are called when an event is raised. + ++makeExample('attribute-directives/ts/app/highlight.directive.2.ts','host')(format=".") + +.l-sub-section + :marked + The `@HostListener` !{_decorator} refers to the DOM element that hosts our attribute directive, the `<p>` in our case. + + We could have attached event listeners by manipulating the host DOM element directly, but + there are at least three problems with such an approach: + + 1. We have to write the listeners correctly. + 1. We must *detach* our listener when the directive is destroyed to avoid memory leaks. + 1. We'd be talking to DOM API directly which, we learned, is something to avoid. + + Let's roll with the `@HostListener` !{_decorator}. +:marked + Now we implement the two mouse event handlers: ++makeExample('attribute-directives/ts/app/highlight.directive.2.ts','mouse-methods')(format=".") +:marked + Notice that they delegate to a helper method that sets the color via a private local variable, `#{_priv}el`. + We revise the constructor to capture the `ElementRef.nativeElement` in this variable. + ++makeExample('attribute-directives/ts/app/highlight.directive.2.ts','ctor')(format=".") +:marked + Here's the updated directive: ++makeExample('app/highlight.directive.2.ts') +:marked + We run the app and confirm that the background color appears as we move the mouse over the `p` and + disappears as we move out. +figure.image-display + img(src="/resources/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight") +.l-main-section +a#bindings +:marked + ## Configure the directive with binding + + Currently the highlight color is hard-coded within the directive. That's inflexible. + We should set the color externally with a binding like this: ++makeExample('attribute-directives/ts/app/app.component.html','pHost') +:marked + We'll extend our directive class with a bindable **input** `highlightColor` property and use it when we highlight text. + + Here is the final version of the class: ++makeExcerpt('app/highlight.directive.ts', 'class') +a#input +:marked + The new `highlightColor` property is called an *input* property because data flows from the binding expression into our directive. + Notice the `@Input()` #{_decorator} applied to the property. ++makeExcerpt('app/highlight.directive.ts', 'color') +:marked + `@Input` adds metadata to the class that makes the `highlightColor` property available for + property binding under the `myHighlight` alias. + We must add this input metadata or Angular will reject the binding. + See the [appendix](#why-input) below to learn why. +.l-sub-section + :marked + ### @Input(_alias_) + The developer who uses this directive expects to bind to the attribute name, `myHighlight`. + The directive property name is `highlightColor`. That's a disconnect. + + We could resolve the discrepancy by renaming the property to `myHighlight` and define it as follows: + + +makeExcerpt('app/highlight.directive.ts', 'highlight', '') + :marked + Maybe we don't want that property name inside the directive perhaps because it + doesn't express our intention well. + We can **alias** the `highlightColor` property with the attribute name by + passing `myHighlight` into the `@Input` #{_decorator}: + +makeExcerpt('app/highlight.directive.ts', 'color', '') +:marked + Now that we're getting the highlight color as an input, we modify the `onMouseEnter()` method to use + it instead of the hard-coded color name. + We also define red as the default color to fallback on in case + the user neglects to bind with a color. ++makeExcerpt('attribute-directives/ts/app/highlight.directive.ts', 'mouse-enter', '') +:marked + Now we'll update our `AppComponent` template to let + users pick the highlight color and bind their choice to our directive. + + Here is the updated template: ++makeExcerpt('attribute-directives/ts/app/app.component.html', 'v2', '') + +.l-sub-section + :marked + ### Where is the templated *color* property? + + The eagle-eyed may notice that the radio button click handlers in the template set a `color` property + and we are binding that `color` to the directive. + We should expect to find a `color` on the host `AppComponent`. + + **We never defined a color property for the host *AppComponent***! + And yet this code works. Where is the template `color` value going? + + Browser debugging reveals that Angular dynamically added a `color` property + to the runtime instance of the `AppComponent`. + + This is *convenient* behavior but it is also *implicit* behavior that could be confusing. + While it's cool that this technique works, we recommend adding the `color` property to the `AppComponent`. + +:marked + Here is our second version of the directive in action. +figure.image-display + img(src="/resources/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2") + +.l-main-section +:marked + ## Bind to a second property + Our directive only has a single, customizable property. What if we had ***two properties***? + + Let's allow the template developer to set the default color, the color that prevails until the user picks a highlight color. + We'll add a second **input** property to `HighlightDirective` called `defaultColor`: ++makeExample('attribute-directives/ts/app/highlight.directive.ts', 'defaultColor')(format=".") +:marked + The `defaultColor` property has a setter that overrides the hard-coded default color, "red". + We don't need a getter. + + How do we bind to it? We already "burned" the `myHighlight` attribute name as a binding target. + + Remember that a *component is a directive too*. + We can add as many component property bindings as we need by stringing them along in the template + as in this example that sets the `a`, `b`, `c` properties to the string literals 'a', 'b', and 'c'. +code-example(format="." ). + <my-component [a]="'a'" [b]="'b'" [c]="'c'"><my-component> +:marked + We do the same thing with an attribute directive. ++makeExample('attribute-directives/ts/app/app.component.html', 'defaultColor')(format=".") +:marked + Here we're binding the user's color choice to the `myHighlight` attribute as we did before. + We're *also* binding the literal string, 'violet', to the `defaultColor`. + + Here is the final version of the directive in action. +figure.image-display + img(src="/resources/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight") + +.l-main-section +:marked + ## Summary + We now know how to + - [build a simple **attribute directive** to attach behavior to an HTML element](#write-directive), + - [use that directive in a template](#apply-directive), + - [respond to **events** to change behavior based on an event](#respond-to-user), + - and [use **binding** to pass values to the attribute directive](#bindings). + + The final source: + ++makeTabs( + `attribute-directives/ts/app/app.component.ts, + attribute-directives/ts/app/app.component.html, + attribute-directives/ts/app/highlight.directive.ts, + attribute-directives/ts/app/main.ts, + attribute-directives/ts/index.html + `, + ',,full', + `app.component.ts, + app.component.html, + highlight.directive.ts, + main.ts, + index.html + `) + + +a#why-input +.l-main-section +:marked + ### Appendix: Input properties + + Earlier we declared the `highlightColor` property to be an ***input*** property of our + `HighlightDirective` + + We've seen properties in bindings before. + We never had to declare them as anything. Why now? + + Angular makes a subtle but important distinction between binding **sources** and **targets**. + + In all previous bindings, the directive or component property was a binding ***source***. + A property is a *source* if it appears in the template expression to the ***right*** of the equals (=). + + A property is a *target* when it appears in **square brackets** ([ ]) to the **left** of the equals (=) ... + as it is does when we bind to the `myHighlight` property of the `HighlightDirective`, ++makeExample('attribute-directives/ts/app/app.component.html','pHost')(format=".") +:marked + The 'color' in `[myHighlight]="color"` is a binding ***source***. + A source property doesn't require a declaration. + + The 'myHighlight' in `[myHighlight]="color"` *is* a binding ***target***. + We must declare it as an *input* property. + Angular rejects the binding with a clear error if we don't. + + Angular treats a *target* property differently for a good reason. + A component or directive in target position needs protection. + + Imagine that our `HighlightDirective` did truly wonderous things. + We graciously made a gift of it to the world. + + To our surprise, some people — perhaps naively — + started binding to *every* property of our directive. + Not just the one or two properties we expected them to target. *Every* property. + That could really mess up our directive in ways we didn't anticipate and have no desire to support. + + The *input* declaration ensures that consumers of our directive can only bind to + the properties of our public API ... nothing else. diff --git a/public/docs/ts/_cache/guide/component-styles.jade b/public/docs/ts/_cache/guide/component-styles.jade new file mode 100644 index 0000000000..c87e746a8e --- /dev/null +++ b/public/docs/ts/_cache/guide/component-styles.jade @@ -0,0 +1,319 @@ +block includes + include ../_util-fns + +:marked + Angular 2 applications are styled with regular CSS. That means we can apply + everything we know about CSS stylesheets, selectors, rules, and media queries + to our Angular applications directly. + + On top of this, Angular has the ability to bundle *component styles* + with our components enabling a more modular design than regular stylesheets. + + In this chapter we learn how to load and apply these *component styles*. + + # Table Of Contents + + * [Using Component Styles](#using-component-styles) + * [Special selectors](#special-selectors) + * [Loading Styles into Components](#loading-styles) + * [Controlling View Encapsulation: Emulated, Native, and None](#view-encapsulation) + * [Appendix 1: Inspecting the generated runtime component styles](#inspect-generated-css) + * [Appendix 2: Loading Styles with Relative URLs](#relative-urls) + + Run the <live-example></live-example> of the code shown in this chapter. + +.l-main-section +:marked + ## Using Component Styles + + For every Angular 2 component we write, we may define not only an HTML template, + but also the CSS styles that go with that template, + specifying any selectors, rules, and media queries that we need. + + One way to do this is to set the `styles` property in the component metadata. + The `styles` property takes #{_an} #{_array} of strings that contain CSS code. + Usually we give it one string as in this example: + ++makeExample('component-styles/ts/app/hero-app.component.ts')(format='.') + +:marked + Component styles differ from traditional, global styles in a couple of ways. + + Firstly, the selectors we put into a component's styles *only apply within the template + of that component*. The `h1` selector in the example above only applies to the `<h1>` tag + in the template of `HeroAppComponent`. Any `<h1>` elements elsewhere in + the application are unaffected. + + This is a big improvement in modularity compared to how CSS traditionally works: + + 1. We can use the CSS class names and selectors that make the most sense in the context of each component. + + 1. Class names and selectors are local to the component and won't collide with + classes and selectors used elsewhere in the application. + + 1. Our component's styles *cannot* be changed by changes to styles elsewhere in the application. + + 1. We can co-locate the CSS code of each component with the TypeScript and HTML code of the component, + which leads to a neat and tidy project structure. + + 1. We can change or remove component CSS code in the future without trawling through the + whole application to see where else it may have been used. We just look at the component we're in. + +a(id="special-selectors") +.l-main-section +:marked + ## Special selectors + + Component styles have a few special *selectors* from the world of + [shadow DOM style scoping](https://www.w3.org/TR/css-scoping-1): + + ### :host + + Use the `:host` pseudo-class selector to target styles in the element that *hosts* the component (as opposed to + targeting elements *inside* the component's template): + ++makeExample('component-styles/ts/app/hero-details.component.css', 'host')(format='.') + +:marked + This is the *only* way we can target the host element. We cannot reach + it from inside the component with other selectors, because it is not part of the + component's own template. It is in a parent component's template. + + Use the *function form* to apply host styles conditionally by + including another selector inside parentheses after `:host`. + + In the next example we target the host element again, but only when it also has the `active` CSS class. + ++makeExample('component-styles/ts/app/hero-details.component.css', 'hostfunction')(format=".") + +:marked + ### :host-context + + Sometimes it is useful to apply styles based on some condition *outside* a component's view. + For example, there may be a CSS theme class applied to the document `<body>` element, and + we want to change how our component looks based on that. + + Use the `:host-context()` pseudo-class selector. It works just like the function + form of `:host()`. It looks for a CSS class in *any ancestor* of the component host element, all the way + up to the document root. It's useful when combined with another selector. + + In the following example, we apply a `background-color` style to all `<h2>` elements *inside* the component, only + if some ancestor element has the CSS class `theme-light`. + ++makeExample('component-styles/ts/app/hero-details.component.css', 'hostcontext')(format='.') + +:marked + ### /deep/ + + Component styles normally apply only to the HTML in the component's own template. + + We can use the `/deep/` selector to force a style down through the child component tree into all the child component views. + The `/deep/` selector works to any depth of nested components, and it applies *both to the view + children and the content children* of the component. + + In this example, we target all `<h3>` elements, from the host element down + through this component to all of its child elements in the DOM: ++makeExample('component-styles/ts/app/hero-details.component.css', 'deep')(format=".") + +:marked + The `/deep/` selector also has the alias `>>>`. We can use either of the two interchangeably. + +.alert.is-important + :marked + The `/deep/` and `>>>` selectors should only be used with **emulated** view encapsulation. + This is the default and it is what we use most of the time. See the + [Controlling View Encapsulation](#view-encapsulation) + section for more details. + +a(id='loading-styles') +.l-main-section +:marked + ## Loading Styles into Components + + We have several ways to add styles to a component: + * inline in the template HTML + * by setting `styles` or `styleUrls` metadata + * with CSS imports + + The scoping rules outlined above apply to each of these loading patterns. + + ### Styles in Metadata + + We can add a `styles` #{_array} property to the `@Component` #{_decorator}. + Each string in the #{_array} (usually just one string) defines the CSS. + ++makeExample('component-styles/ts/app/hero-app.component.ts') + +:marked + ### Template Inline Styles + + We can embed styles directly into the HTML template by putting them + inside `<style>` tags. + ++makeExample('component-styles/ts/app/hero-controls.component.ts', 'inlinestyles') + +:marked + ### Style URLs in Metadata + + We can load styles from external CSS files by adding a `styleUrls` attribute + into a component's `@Component` #{_decorator}: + ++makeExample('component-styles/ts/app/hero-details.component.ts', 'styleurls') + +block style-url + .alert.is-important + :marked + The URL is ***relative to the application root*** which is usually the + location of the `index.html` web page that hosts the application. + The style file URL is *not* relative to the component file. + That's why the example URL begins `app/`. + See [Appendix 2](#relative-urls) to specify a URL relative to the + component file. + +block module-bundlers + .l-sub-section + :marked + Users of module bundlers like Webpack may also use the `styles` attribute + to load styles from external files at build time. They could write: + + `styles: [require('my.component.css')]` + + We set the `styles` property, **not** `styleUrls` property! The module + bundler is loading the CSS strings, not Angular. + Angular only sees the CSS strings *after* the bundler loads them. + To Angular it is as if we wrote the `styles` array by hand. + Refer to the module bundler's documentation for information on + loading CSS in this manner. + +:marked + ### Template Link Tags + + We can also embed `<link>` tags into the component's HTML template. + + As with `styleUrls`, the link tag's `href` URL is relative to the + application root, not relative to the component file. + ++makeExample('component-styles/ts/app/hero-team.component.ts', 'stylelink') + +:marked + ### CSS @imports + + We can also import CSS files into our CSS files by using the standard CSS + [`@import` rule](https://developer.mozilla.org/en/docs/Web/CSS/@import). + +block css-import-url + :marked + In *this* case the URL is relative to the CSS file into which we are importing. + ++makeExample('component-styles/ts/app/hero-details.component.css', 'import', 'app/hero-details.component.css (excerpt)') + +a#view-encapsulation +.l-main-section +:marked + ## Controlling View Encapsulation: Native, Emulated, and None + + As discussed above, component CSS styles are *encapsulated* into the component's own view and do + not affect the rest of the application. + + We can control how this encapsulation happens on a *per + component* basis by setting the *view encapsulation mode* in the component metadata. There + are three modes to choose from: + + * `Native` view encapsulation uses the browser's native [Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM) + implementation to attach a Shadow DOM to the component's host element, and then puts the component + view inside that Shadow DOM. The component's styles are included within the Shadow DOM. + + * `Emulated` view encapsulation (**the default**) emulates the behavior of Shadow DOM by preprocessing + (and renaming) the CSS code to effectively scope the CSS to the component's view. + See [Appendix 1](#inspect-generated-css) for details. + + * `None` means that Angular does no view encapsulation. + Angular adds the CSS to the global styles. + The scoping rules, isolations, and protections discussed earlier do not apply. + This is essentially the same as pasting the component's styles into the HTML. + + Set the components encapsulation mode using the `encapsulation` property in the component metadata: + ++makeExample('component-styles/ts/app/quest-summary.component.ts', 'encapsulation.native')(format='.') + +:marked + `Native` view encapsulation only works on [browsers that have native support + for Shadow DOM](http://caniuse.com/#feat=shadowdom). The support is still limited, + which is why `Emulated` view encapsulation is the default mode and recommended + in most cases. + +a#inspect-generated-css +.l-main-section +:marked + ## Appendix 1: Inspecting The CSS Generated in Emulated View Encapsulation + + When using the default emulated view encapsulation, Angular preprocesses + all component styles so that they approximate the standard Shadow CSS scoping rules. + + When we inspect the DOM of a running Angular application with emulated view + encapsulation enabled, we see that each DOM element has some extra attributes + attached to it: + +code-example(format=""). + <hero-details _nghost-pmm-5> + <h2 _ngcontent-pmm-5>Mister Fantastic</h2> + <hero-team _ngcontent-pmm-5 _nghost-pmm-6> + <h3 _ngcontent-pmm-6>Team</h3> + </hero-team> + </hero-detail> + +:marked + We see two kinds of generated attributes: + * An element that would be a Shadow DOM host in native encapsulation has a + generated `_nghost` attribute. This is typically the case for component host elements. + + * An element within a component's view has a `_ngcontent` attribute + that identifies to which host's emulated Shadow DOM this element belongs. + + The exact values of these attributes are not important. They are automatically + generated and we never refer to them in application code. But they are targeted + by the generated component styles, which we'll find in the `<head>` section of the DOM: + +code-example(format=""). + [_nghost-pmm-5] { + display: block; + border: 1px solid black; + } + + h3[_ngcontent-pmm-6] { + background-color: white; + border: 1px solid #777; + } + +:marked + These are the styles we wrote, post-processed so that each selector is augmented + with `_nghost` or `_ngcontent` attribute selectors. + These extra selectors enable the scoping rules described in this guide. + + We'll likely live with *emulated* mode until shadow DOM gains traction. + +a#relative-urls +.l-main-section +:marked + ## Appendix 2: Loading Styles with Relative URLs + + It's common practice to split a component's code, HTML, and CSS into three separate files in the same directory: + +code-example(format=''). + quest-summary.component.ts + quest-summary.component.html + quest-summary.component.css + +:marked + We include the template and CSS files by setting the `templateUrl` and `styleUrls` metadata properties respectively. + Because these files are co-located with the component, + it would be nice to refer to them by name without also having to specify a path back to the root of the application. + +block module-id + :marked + We can change the way Angular calculates the full URL be setting the component metadata's `moduleId` property to `module.id`. + + +makeExample('component-styles/ts/app/quest-summary.component.ts','', 'app/quest-summary.component.ts') + :marked + Learn more about `moduleId` in the [Component-Relative Paths](../cookbook/component-relative-paths.html) chapter. + diff --git a/public/docs/ts/_cache/guide/dependency-injection.jade b/public/docs/ts/_cache/guide/dependency-injection.jade new file mode 100644 index 0000000000..2ad5b7a7ed --- /dev/null +++ b/public/docs/ts/_cache/guide/dependency-injection.jade @@ -0,0 +1,897 @@ +block includes + include ../_util-fns + - var _thisDot = 'this.'; + +:marked + **Dependency injection** is an important application design pattern. + Angular has its own dependency injection framework, and + we really can't build an Angular application without it. + It's used so widely that almost everyone just calls it _DI_. + + In this chapter we'll learn what DI is and why we want it. + Then we'll learn [how to use it](#angular-di) in an Angular app. + + - [Why dependency injection?](#why-dependency-injection) + - [Angular dependency injection](#angular-dependency-injection) + - [Injector providers](#injector-providers) + - [Dependency injection tokens](#dependency-injection-tokens) + - [Summary](#summary) + + Run the <live-example></live-example>. + +.l-main-section#why-di +:marked + ## Why dependency injection? + + Let's start with the following code. + ++makeExample('dependency-injection/ts/app/car/car-no-di.ts', 'car', 'app/car/car.ts (without DI)') + +:marked + Our `Car` creates everything it needs inside its constructor. + What's the problem? + The problem is that our `Car` class is brittle, inflexible, and hard to test. + + Our `Car` needs an engine and tires. Instead of asking for them, + the `Car` constructor instantiates its own copies from + the very specific classes `Engine` and `Tires`. + + What if the `Engine` class evolves and its constructor requires a parameter? + Our `Car` is broken and stays broken until we rewrite it along the lines of + `#{_thisDot}engine = new Engine(theNewParameter)`. + We didn't care about `Engine` constructor parameters when we first wrote `Car`. + We don't really care about them now. + But we'll *have* to start caring because + when the definition of `Engine` changes, our `Car` class must change. + That makes `Car` brittle. + + What if we want to put a different brand of tires on our `Car`? Too bad. + We're locked into whatever brand the `Tires` class creates. That makes our `Car` inflexible. + + Right now each new car gets its own engine. It can't share an engine with other cars. + While that makes sense for an automobile engine, + we can think of other dependencies that should be shared, such as the onboard + wireless connection to the manufacturer's service center. Our `Car` lacks the flexibility + to share services that have been created previously for other consumers. + + When we write tests for our `Car` we're at the mercy of its hidden dependencies. + Is it even possible to create a new `Engine` in a test environment? + What does `Engine`itself depend upon? What does that dependency depend on? + Will a new instance of `Engine` make an asynchronous call to the server? + We certainly don't want that going on during our tests. + + What if our `Car` should flash a warning signal when tire pressure is low? + How do we confirm that it actually does flash a warning + if we can't swap in low-pressure tires during the test? + + We have no control over the car's hidden dependencies. + When we can't control the dependencies, a class becomes difficult to test. + + How can we make `Car` more robust, flexible, and testable? + + <a id="ctor-injection"></a> + That's super easy. We change our `Car` constructor to a version with DI: + ++makeTabs( + 'dependency-injection/ts/app/car/car.ts, dependency-injection/ts/app/car/car-no-di.ts', + 'car-ctor, car-ctor', + 'app/car/car.ts (excerpt with DI), app/car/car.ts (excerpt without DI)')(format=".") + +:marked + See what happened? We moved the definition of the dependencies to the constructor. + Our `Car` class no longer creates an engine or tires. + It just consumes them. + +block ctor-syntax + .l-sub-section + :marked + We also leveraged TypeScript's constructor syntax for declaring + parameters and properties simultaneously. + +:marked + Now we create a car by passing the engine and tires to the constructor. + ++makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation', '')(format=".") + +:marked + How cool is that? + The definition of the engine and tire dependencies are + decoupled from the `Car` class itself. + We can pass in any kind of engine or tires we like, as long as they + conform to the general API requirements of an engine or tires. + + If someone extends the `Engine` class, that is not `Car`'s problem. + +.l-sub-section + :marked + The _consumer_ of `Car` has the problem. The consumer must update the car creation code to + something like this: + + - var stylePattern = { otl: /(new Car.*$)/gm }; + +makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-param', '', stylePattern)(format=".") + + :marked + The critical point is this: `Car` itself did not have to change. + We'll take care of the consumer's problem soon enough. + +:marked + The `Car` class is much easier to test because we are in complete control + of its dependencies. + We can pass mocks to the constructor that do exactly what we want them to do + during each test: + +- var stylePattern = { otl: /(new Car.*$)/gm }; ++makeExample('dependency-injection/ts/app/car/car-creations.ts', 'car-ctor-instantiation-with-mocks', '', stylePattern)(format=".") + +:marked + **We just learned what dependency injection is**. + + It's a coding pattern in which a class receives its dependencies from external + sources rather than creating them itself. + + Cool! But what about that poor consumer? + Anyone who wants a `Car` must now + create all three parts: the `Car`, `Engine`, and `Tires`. + The `Car` class shed its problems at the consumer's expense. + We need something that takes care of assembling these parts for us. + + We could write a giant class to do that: + ++makeExample('dependency-injection/ts/app/car/car-factory.ts', null, 'app/car/car-factory.ts') + +:marked + It's not so bad now with only three creation methods. + But maintaining it will be hairy as the application grows. + This factory is going to become a huge spiderweb of + interdependent factory methods! + + Wouldn't it be nice if we could simply list the things we want to build without + having to define which dependency gets injected into what? + + This is where the dependency injection framework comes into play. + Imagine the framework had something called an _injector_. + We register some classes with this injector, and it figures out how to create them. + + When we need a `Car`, we simply ask the injector to get it for us and we're good to go. + ++makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-call')(format=".") + +:marked + Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`. + The consumer knows nothing about creating a `Car`. + We don't have a gigantic factory class to maintain. + Both `Car` and consumer simply ask for what they need and the injector delivers. + + This is what a **dependency injection framework** is all about. + + Now that we know what dependency injection is and appreciate its benefits, + let's see how it is implemented in Angular. + +.l-main-section#angular-di +:marked + ## Angular dependency injection + + Angular ships with its own dependency injection framework. This framework can also be used + as a standalone module by other applications and frameworks. + + That sounds nice. What does it do for us when building components in Angular? + Let's see, one step at a time. + + We'll begin with a simplified version of the `HeroesComponent` + that we built in the [The Tour of Heroes](../tutorial/). + ++makeTabs( + `dependency-injection/ts/app/heroes/heroes.component.1.ts, + dependency-injection/ts/app/heroes/hero-list.component.1.ts, + dependency-injection/ts/app/heroes/hero.ts, + dependency-injection/ts/app/heroes/mock-heroes.ts`, + 'v1,,,', + `app/heroes/heroes.component.ts, + app/heroes/hero-list.component.ts, + app/heroes/hero.ts, + app/heroes/mock-heroes.ts`) + +:marked + The `HeroesComponent` is the root component of the *Heroes* feature area. + It governs all the child components of this area. + Our stripped down version has only one child, `HeroListComponent`, + which displays a list of heroes. + +:marked + Right now `HeroListComponent` gets heroes from `HEROES`, an in-memory collection + defined in another file. + That may suffice in the early stages of development, but it's far from ideal. + As soon as we try to test this component or want to get our heroes data from a remote server, + we'll have to change the implementation of `heroes` and + fix every other use of the `HEROES` mock data. + + Let's make a service that hides how we get hero data. + +.l-sub-section + :marked + Given that the service is a + [separate concern](https://en.wikipedia.org/wiki/Separation_of_concerns), + we suggest that you + write the service code in its own file. + +ifDocsFor('ts') + :marked + See [this note](#one-class-per-file) for details. + ++makeExample('dependency-injection/ts/app/heroes/hero.service.1.ts',null, 'app/heroes/hero.service.ts' ) + +:marked + Our `HeroService` exposes a `getHeroes` method that returns + the same mock data as before, but none of its consumers need to know that. +.l-sub-section + :marked + Notice the `@Injectable()` #{_decorator} above the service class. + We'll discuss its purpose [shortly](#injectable). + +- var _perhaps = _docsFor == 'dart' ? '' : 'perhaps'; +.l-sub-section + :marked + We aren't even pretending this is a real service. + If we were actually getting data from a remote server, the API would have to be + asynchronous, #{_perhaps} returning a !{_PromiseLinked}. + We'd also have to rewrite the way components consume our service. + This is important in general, but not to our current story. + +:marked + A service is nothing more than a class in Angular 2. + It remains nothing more than a class until we register it with an Angular injector. + +#bootstrap +:marked + ### Configuring the injector + + We don't have to create an Angular injector. + Angular creates an application-wide injector for us during the bootstrap process. + ++makeExample('dependency-injection/ts/app/main.ts', 'bootstrap', 'app/main.ts (excerpt)')(format='.') + +:marked + We do have to configure the injector by registering the **providers** + that create the services our application requires. + We'll explain what [providers](#providers) are later in this chapter. + Before we do, let's see an example of provider registration during bootstrapping: + ++makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap-discouraged')(format='.') + +:marked + The injector now knows about our `HeroService`. + An instance of our `HeroService` will be available for injection across our entire application. + + Of course we can't help wondering about that comment telling us not to do it this way. + It *will* work. It's just not a best practice. + The bootstrap provider option is intended for configuring and overriding Angular's own + preregistered services, such as its routing support. + + The preferred approach is to register application providers in application components. + Because the `HeroService` is used within the *Heroes* feature area — + and nowhere else — the ideal place to register it is in the top-level `HeroesComponent`. + +:marked + ### Registering providers in a component + Here's a revised `HeroesComponent` that registers the `HeroService`. + +- var stylePattern = { otl: /(providers:.*),/ }; ++makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts', 'full','app/heroes/heroes.component.ts', stylePattern)(format='.') + +:marked + Look closely at the `providers` part of the `@Component` metadata. + An instance of the `HeroService` is now available for injection in this `HeroesComponent` + and all of its child components. + + The `HeroesComponent` itself doesn't happen to need the `HeroService`. + But its child `HeroListComponent` does, so we head there next. + +:marked + ### Preparing the HeroListComponent for injection + + The `HeroListComponent` should get heroes from the injected `HeroService`. + Per the dependency injection pattern, the component must ask for the service in its + constructor, [as we explained earlier](#ctor-injection). + It's a small change: + ++makeTabs( + `dependency-injection/ts/app/heroes/hero-list.component.2.ts, + dependency-injection/ts/app/heroes/hero-list.component.1.ts`, + null, + `app/heroes/hero-list.component (with DI), + app/heroes/hero-list.component (without DI)`) + +.l-sub-section + :marked + #### Focus on the constructor + + Adding a parameter to the constructor isn't all that's happening here. + + +makeExample('dependency-injection/ts/app/heroes/hero-list.component.2.ts', 'ctor')(format=".") + + :marked + Note that the constructor parameter has the type `HeroService`, and that + the `HeroListComponent` class has an `@Component` #{_decorator} + (scroll up to confirm that fact). + Also recall that the parent component (`HeroesComponent`) + has `providers` information for `HeroService`. + + The constructor parameter type, the `@Component` #{_decorator}, + and the parent's `providers` information combine to tell the + Angular injector to inject an instance of + `HeroService` whenever it creates a new `HeroListComponent`. + +#di-metadata +:marked + ### Implicit injector creation + + When we introduced the idea of an injector above, we showed how to + use it to create a new `Car`. Here we also show how such an injector + would be explicitly created: + ++makeExample('dependency-injection/ts/app/car/car-injector.ts','injector-create-and-call')(format=".") + +:marked + We won't find code like that in the Tour of Heroes or any of our other samples. + We *could* write code that [explicitly creates an injector](#explicit-injector) if we *had* to, but we rarely do. + Angular takes care of creating and calling injectors + when it creates components for us — whether through HTML markup, as in `<hero-list></hero-list>`, + or after navigating to a component with the [router](./router.html). + If we let Angular do its job, we'll enjoy the benefits of automated dependency injection. + +:marked + ### Singleton services + + Dependencies are singletons within the scope of an injector. + In our example, a single `HeroService` instance is shared among the + `HeroesComponent` and its `HeroListComponent` children. + + However, Angular DI is an hierarchical injection + system, which means that nested injectors can create their own service instances. + Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter. + +:marked + ### Testing the component + + We emphasized earlier that designing a class for dependency injection makes the class easier to test. + Listing dependencies as constructor parameters may be all we need to test application parts effectively. + + For example, we can create a new `HeroListComponent` with a mock service that we can manipulate + under test: + ++makeExample('dependency-injection/ts/app/test.component.ts', 'spec')(format='.') + +.l-sub-section + :marked + Learn more in [Testing](../testing/index.html). + +:marked + ### When the service needs a service + + Our `HeroService` is very simple. It doesn't have any dependencies of its own. + + + What if it had a dependency? What if it reported its activities through a logging service? + We'd apply the same *constructor injection* pattern, + adding a constructor that takes a `Logger` parameter. + + Here is the revision compared to the original. + ++makeTabs( + `dependency-injection/ts/app/heroes/hero.service.2.ts, + dependency-injection/ts/app/heroes/hero.service.1.ts`, + null, + `app/heroes/hero.service (v2), + app/heroes/hero.service (v1)`) + +:marked + The constructor now asks for an injected instance of a `Logger` and stores it in a private property called `#{_priv}logger`. + We call that property within our `getHeroes` method when anyone asks for heroes. + +//- FIXME refer to Dart API when that page becomes available. +- var injMetaUrl = 'https://angular.io/docs/ts/latest/api/core/index/InjectableMetadata-class.html'; +h3#injectable Why @Injectable()? +:marked + **<a href="#{injMetaUrl}">@Injectable()</a>** marks a class as available to an + injector for instantiation. Generally speaking, an injector will report an + error when trying to instantiate a class that is not marked as + `@Injectable()`. + +block injectable-not-always-needed-in-ts + .l-sub-section + :marked + As it happens, we could have omitted `@Injectable()` from our first + version of `HeroService` because it had no injected parameters. + But we must have it now that our service has an injected dependency. + We need it because Angular requires constructor parameter metadata + in order to inject a `Logger`. + + .callout.is-helpful + header Suggestion: add @Injectable() to every service class + :marked + We recommend adding `@Injectable()` to every service class, even those that don't have dependencies + and, therefore, do not technically require it. Here's why: + + ul(style="font-size:inherit") + li <b>Future proofing:</b> No need to remember <code>@Injectable()</code> when we add a dependency later. + li <b>Consistency:</b> All services follow the same rules, and we don't have to wonder why #{_a} #{_decorator} is missing. + +:marked + Injectors are also responsible for instantiating components + like `HeroesComponent`. Why haven't we marked `HeroesComponent` as + `@Injectable()`? + + We *can* add it if we really want to. It isn't necessary because the + `HeroesComponent` is already marked with `@Component`, and this + !{_decorator} class (like `@Directive` and `@Pipe`, which we'll learn about later) + is a subtype of <a href="#{injMetaUrl}">InjectableMetadata</a>. It is in + fact `InjectableMetadata` #{_decorator}s that + identify a class as a target for instantiation by an injector. + ++ifDocsFor('ts') + .l-sub-section + :marked + At runtime, injectors can read class metadata in the transpiled JavaScript code + and use the constructor parameter type information + to determine what things to inject. + + Not every JavaScript class has metadata. + The TypeScript compiler discards metadata by default. + If the `emitDecoratorMetadata` compiler option is true + (as it should be in the `tsconfig.json`), + the compiler adds the metadata to the generated JavaScript + for _every class with at least one decorator_. + + While any decorator will trigger this effect, mark the service class with the + <a href="#{injMetaUrl}">InjectableMetadata</a> #{_decorator} + to make the intent clear. + +.callout.is-critical + header Always include the parentheses + block always-include-paren + :marked + Always write `@Injectable()`, not just `@Injectable`. + Our application will fail mysteriously if we forget the parentheses. + +.l-main-section#logger-service +:marked + ## Creating and registering a logger service + + We're injecting a logger into our `HeroService` in two steps: + 1. Create the logger service. + 1. Register it with the application. + + Our logger service is quite simple: + ++makeExample('dependency-injection/ts/app/logger.service.ts', null, 'app/logger.service.ts') + +block real-logger + //- N/A + +:marked + We're likely to need the same logger service everywhere in our application, + so we put it in the project's `#{_appDir}` folder, and + we register it in the `providers` #{_array} of the metadata for our application root component, `AppComponent`. + ++makeExcerpt('app/providers.component.ts','providers-logger','app/app.component.ts (excerpt)') + +:marked + If we forget to register the logger, Angular throws an exception when it first looks for the logger: +code-example(format="nocode"). + EXCEPTION: No provider for Logger! (HeroListComponent -> HeroService -> Logger) + +:marked + That's Angular telling us that the dependency injector couldn't find the *provider* for the logger. + It needed that provider to create a `Logger` to inject into a new + `HeroService`, which it needed to + create and inject into a new `HeroListComponent`. + + The chain of creations started with the `Logger` provider. *Providers* are the subject of our next section. + +.l-main-section#providers +:marked + ## Injector providers + + A provider *provides* the concrete, runtime version of a dependency value. + The injector relies on **providers** to create instances of the services + that the injector injects into components and other services. + + We must register a service *provider* with the injector, or it won't know how to create the service. + + Earlier we registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppComponent` like this: + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger') + +- var implements = _docsFor == 'dart' ? 'implements' : 'looks and behaves like a ' +- var objectlike = _docsFor == 'dart' ? '' : 'an object that behaves like ' +- var loggerlike = _docsFor == 'dart' ? '' : 'We could provide a logger-like object. ' +:marked + There are many ways to *provide* something that #{implements} `Logger`. + The `Logger` class itself is an obvious and natural provider. + But it's not the only way. + + We can configure the injector with alternative providers that can deliver #{objectlike} a `Logger`. + We could provide a substitute class. #{loggerlike} + We could give it a provider that calls a logger factory function. + Any of these approaches might be a good choice under the right circumstances. + + What matters is that the injector has a provider to go to when it needs a `Logger`. + +//- Dart limitation: the provide function isn't const so it cannot be used in an annotation. +- var _andProvideFn = _docsFor == 'dart' ? '' : 'and <i>provide</i> object literal'; +#provide +:marked + ### The *Provider* class !{_andProvideFn} + +:marked + We wrote the `providers` #{_array} like this: + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-1') + +:marked + This is actually a short-hand expression for a provider registration + <span if-docs="ts"> + using a _provider_ object literal with two properties: + </span> + <span if-docs="dart"> + that creates a new instance of the + [Provider](../api/core/index/Provider-class.html) class: + </span> + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-3') + +block provider-ctor-args + - var _secondParam = 'provider definition object'; + +:marked + The first is the [token](#token) that serves as the key for both locating a dependency value + and registering the provider. + + The second is a !{_secondParam}, + which we can think of as a *recipe* for creating the dependency value. + There are many ways to create dependency values ... and many ways to write a recipe. + +#class-provider +:marked + ### Alternative class providers + + Occasionally we'll ask a different class to provide the service. + The following code tells the injector + to return a `BetterLogger` when something asks for the `Logger`. + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-4') + +block dart-diff-const-metadata + //- N/A + +:marked + ### Class provider with dependencies + Maybe an `EvenBetterLogger` could display the user name in the log message. + This logger gets the user from the injected `UserService`, + which happens also to be injected at the application level. + ++makeExample('dependency-injection/ts/app/providers.component.ts','EvenBetterLogger')(format='.') + +:marked + Configure it like we did `BetterLogger`. + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-5')(format=".") + +:marked + ### Aliased class providers + + Suppose an old component depends upon an `OldLogger` class. + `OldLogger` has the same interface as the `NewLogger`, but for some reason + we can't update the old component to use it. + + When the *old* component logs a message with `OldLogger`, + we want the singleton instance of `NewLogger` to handle it instead. + + The dependency injector should inject that singleton instance + when a component asks for either the new or the old logger. + The `OldLogger` should be an alias for `NewLogger`. + + We certainly do not want two different `NewLogger` instances in our app. + Unfortunately, that's what we get if we try to alias `OldLogger` to `NewLogger` with `useClass`. + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-6a')(format=".") + +:marked + The solution: alias with the `useExisting` option. + +- var stylePattern = { otl: /(useExisting: \w*)/gm }; ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-6b', '', stylePattern)(format=".") + +#value-provider +:marked + ### Value providers + +:marked + Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class. + +block dart-diff-const-metadata-ctor + //- N/A + ++makeExample('dependency-injection/ts/app/providers.component.ts','silent-logger')(format=".") + +:marked + Then we register a provider with the `useValue` option, + which makes this object play the logger role. + +- var stylePattern = { otl: /(useValue: \w*)/gm }; ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-7', '', stylePattern)(format=".") + +:marked + See more `useValue` examples in the + [Non-class dependencies](#non-class-dependencies) and + [OpaqueToken](#opaquetoken) sections. + +#factory-provider +:marked + ### Factory providers + + Sometimes we need to create the dependent value dynamically, + based on information we won't have until the last possible moment. + Maybe the information changes repeatedly in the course of the browser session. + + Suppose also that the injectable service has no independent access to the source of this information. + + This situation calls for a **factory provider**. + + Let's illustrate by adding a new business requirement: + the HeroService must hide *secret* heroes from normal users. + Only authorized users should see secret heroes. + + Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user. + It needs to know if the user is authorized to see secret heroes. + That authorization can change during the course of a single application session, + as when we log in a different user. + + Unlike `EvenBetterLogger`, we can't inject the `UserService` into the `HeroService`. + The `HeroService` won't have direct access to the user information to decide + who is authorized and who is not. + +.l-sub-section + :marked + Why? We don't know either. Stuff like this happens. + +:marked + Instead the `HeroService` constructor takes a boolean flag to control display of secret heroes. + ++makeExample('dependency-injection/ts/app/heroes/hero.service.ts','internals', 'app/heroes/hero.service.ts (excerpt)')(format='.') + +:marked + We can inject the `Logger`, but we can't inject the boolean `isAuthorized`. + We'll have to take over the creation of new instances of this `HeroService` with a factory provider. + + A factory provider needs a factory function: + ++makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','factory', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.') + +:marked + Although the `HeroService` has no access to the `UserService`, our factory function does. + + We inject both the `Logger` and the `UserService` into the factory provider and let the injector pass them along to the factory function: + ++makeExample('dependency-injection/ts/app/heroes/hero.service.provider.ts','provider', 'app/heroes/hero.service.provider.ts (excerpt)')(format='.') + +.l-sub-section + :marked + The `useFactory` field tells Angular that the provider is a factory function + whose implementation is the `heroServiceFactory`. + + The `deps` property is #{_an} #{_array} of [provider tokens](#token). + The `Logger` and `UserService` classes serve as tokens for their own class providers. + The injector resolves these tokens and injects the corresponding services into the matching factory function parameters. + +- var exportedvar = _docsFor == 'dart' ? 'constant' : 'exported variable' +- var variable = _docsFor == 'dart' ? 'constant' : 'variable' +:marked + Notice that we captured the factory provider in #{_an} #{exportedvar}, `heroServiceProvider`. + This extra step makes the factory provider reusable. + We can register our `HeroService` with this #{variable} wherever we need it. + + In our sample, we need it only in the `HeroesComponent`, + where it replaces the previous `HeroService` registration in the metadata `providers` #{_array}. + Here we see the new and the old implementation side-by-side: + +- var stylePattern = { otl: /(providers.*),$/gm }; ++makeTabs( + `dependency-injection/ts/app/heroes/heroes.component.ts, + dependency-injection/ts/app/heroes/heroes.component.1.ts`, + ',full', + `app/heroes/heroes.component (v3), + app/heroes/heroes.component (v2)`, + stylePattern) + +.l-main-section#token +:marked + ## Dependency injection tokens + + When we register a provider with an injector, we associate that provider with a dependency injection token. + The injector maintains an internal *token-provider* map that it references when + asked for a dependency. The token is the key to the map. + + In all previous examples, the dependency value has been a class *instance*, and + the class *type* served as its own lookup key. + Here we get a `HeroService` directly from the injector by supplying the `HeroService` type as the token: + ++makeExample('dependency-injection/ts/app/injector.component.ts','get-hero-service')(format='.') + +:marked + We have similar good fortune when we write a constructor that requires an injected class-based dependency. + We define a constructor parameter with the `HeroService` class type, + and Angular knows to inject the + service associated with that `HeroService` class token: + ++makeExample('dependency-injection/ts/app/heroes/hero-list.component.ts', 'ctor-signature') + +:marked + This is especially convenient when we consider that most dependency values are provided by classes. + +//- TODO: if function injection is useful explain or illustrate why. +:marked + ### Non-class dependencies +p + | What if the dependency value isn't a class? Sometimes the thing we want to inject is a + block non-class-dep-eg + span string, function, or object. +p + | Applications often define configuration objects with lots of small facts + | (like the title of the application or the address of a web API endpoint) + block config-obj-maps + |  but these configuration objects aren't always instances of a class. + | They can be object literals + |  such as this one: + ++makeExample('dependency-injection/ts/app/app.config.ts','config','app/app-config.ts (excerpt)')(format='.') + +:marked + We'd like to make this configuration object available for injection. + We know we can register an object with a [value provider](#value-provider). + +block what-should-we-use-as-token + :marked + But what should we use as the token? + We don't have a class to serve as a token. + There is no `AppConfig` class. + + .l-sub-section#interface + :marked + ### TypeScript interfaces aren't valid tokens + + The `HERO_DI_CONFIG` constant has an interface, `AppConfig`. Unfortunately, we + cannot use a TypeScript interface as a token: + +makeExample('dependency-injection/ts/app/providers.component.ts','providers-9-interface')(format=".") + +makeExample('dependency-injection/ts/app/providers.component.ts','provider-9-ctor-interface')(format=".") + :marked + That seems strange if we're used to dependency injection in strongly typed languages, where + an interface is the preferred dependency lookup key. + + It's not Angular's fault. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces. + The TypeScript interface disappears from the generated JavaScript. + There is no interface type information left for Angular to find at runtime. + +//- FIXME simplify once APIs are defined for Dart. +- var opaquetoken = _docsFor == 'dart' ? '<b>OpaqueToken</b>' : '<a href="../api/core/index/OpaqueToken-class.html"><b>OpaqueToken</b></a>' +:marked + ### OpaqueToken + + One solution to choosing a provider token for non-class dependencies is + to define and use an !{opaquetoken}. + The definition looks like this: + ++makeExample('dependency-injection/ts/app/app.config.ts','token')(format='.') + +:marked + We register the dependency provider using the `OpaqueToken` object: + ++makeExample('dependency-injection/ts/app/providers.component.ts','providers-9')(format=".") + +:marked + Now we can inject the configuration object into any constructor that needs it, with + the help of an `@Inject` #{_decorator}: + ++makeExample('dependency-injection/ts/app/app.component.2.ts','ctor')(format=".") + +- var configType = _docsFor == 'dart' ? '<code>Map</code>' : '<code>AppConfig</code>' +.l-sub-section + :marked + Although the !{configType} interface plays no role in dependency injection, + it supports typing of the configuration object within the class. + +block dart-map-alternative + :marked + Or we can provide and inject the configuration object in our top-level `AppComponent`. + + +makeExcerpt('app/app.component.ts','providers') + +#optional +:marked + ## Optional dependencies + + Our `HeroService` *requires* a `Logger`, but what if it could get by without + a logger? + We can tell Angular that the dependency is optional by annotating the + constructor argument with `@Optional()`: + ++ifDocsFor('ts') + +makeExample('dependency-injection/ts/app/providers.component.ts','import-optional', '') ++makeExample('dependency-injection/ts/app/providers.component.ts','provider-10-ctor', '')(format='.') + +:marked + When using `@Optional()`, our code must be prepared for a null value. If we + don't register a logger somewhere up the line, the injector will set the + value of `logger` to null. + +.l-main-section +:marked + ## Summary + + We learned the basics of Angular dependency injection in this chapter. + We can register various kinds of providers, + and we know how to ask for an injected object (such as a service) by + adding a parameter to a constructor. + + Angular dependency injection is more capable than we've described. + We can learn more about its advanced features, beginning with its support for + nested injectors, in the + [Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter. + +.l-main-section#explicit-injector +:marked + ## Appendix: Working with injectors directly + + We rarely work directly with an injector, but + here's an `InjectorComponent` that does. + ++makeExample('dependency-injection/ts/app/injector.component.ts', 'injector', 'app/injector.component.ts') + +:marked + An `Injector` is itself an injectable service. + + In this example, Angular injects the component's own `Injector` into the component's constructor. + The component then asks the injected injector for the services it wants. + + Note that the services themselves are not injected into the component. + They are retrieved by calling `injector.get`. + + The `get` method throws an error if it can't resolve the requested service. + We can call `get` with a second parameter (the value to return if the service is not found) + instead, which we do in one case + to retrieve a service (`ROUS`) that isn't registered with this or any ancestor injector. + +.l-sub-section + :marked + The technique we just described is an example of the + [service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern). + + We **avoid** this technique unless we genuinely need it. + It encourages a careless grab-bag approach such as we see here. + It's difficult to explain, understand, and test. + We can't know by inspecting the constructor what this class requires or what it will do. + It could acquire services from any ancestor component, not just its own. + We're forced to spelunk the implementation to discover what it does. + + Framework developers may take this approach when they + must acquire services generically and dynamically. + ++ifDocsFor('ts') + .l-main-section#one-class-per-file + :marked + ## Appendix: Why we recommend one class per file + + Having multiple classes in the same file is confusing and best avoided. + Developers expect one class per file. Keep them happy. + + If we scorn this advice and, say, + combine our `HeroService` class with the `HeroesComponent` in the same file, + **define the component last!** + If we define the component before the service, + we'll get a runtime null reference error. + + .l-sub-section + :marked + We actually can define the component first with the help of the `forwardRef()` method as explained + in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html). + But why flirt with trouble? + Avoid the problem altogether by defining components and services in separate files. diff --git a/public/docs/ts/_cache/guide/displaying-data.jade b/public/docs/ts/_cache/guide/displaying-data.jade new file mode 100644 index 0000000000..5fbf0e7ff7 --- /dev/null +++ b/public/docs/ts/_cache/guide/displaying-data.jade @@ -0,0 +1,283 @@ +block includes + include ../_util-fns + - var _iterableUrl = 'https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols'; + - var _boolean = 'truthy/falsey'; + +:marked + We typically display data in Angular by binding controls in an HTML template + to properties of an Angular component. + + In this chapter, we'll create a component with a list of heroes. Each hero has a name. + We'll display the list of hero names and + conditionally show a message below the list. + + The final UI looks like this: + +figure.image-display + img(src="/resources/images/devguide/displaying-data/final.png" alt="Final UI") + +:marked + # Table Of Contents + + * [Showing component properties with interpolation](#interpolation) + * [Showing !{_an} !{_array} property with NgFor](#ngFor) + * [Conditional display with NgIf](#ngIf) + +.l-sub-section + :marked + The <live-example></live-example> demonstrates all of the syntax and code + snippets described in this chapter. + +.l-main-section#interpolation +:marked + ## Showing component properties with interpolation + The easiest way to display a component property + is to bind the property name through interpolation. + With interpolation, we put the property name in the view template, enclosed in double curly braces: `{{myHero}}`. + + Let's build a small illustrative example together. + + Create a new project folder (<ngio-ex path="displaying-data"></ngio-ex>) and follow the steps in the [QuickStart](../quickstart.html). + +block quickstart-repo + include ../_quickstart_repo + +:marked + Then modify the <ngio-ex path="app.component.ts"></ngio-ex> file by + changing the template and the body of the component. + When we're done, it should look like this: + ++makeExample('app/app.component.1.ts') + +:marked + We added two properties to the formerly empty component: `title` and `myHero`. + + Our revised template displays the two component properties using double curly brace + interpolation: + ++makeExcerpt('app/app.component.1.ts', 'template', '') + ++ifDocsFor('ts') + .l-sub-section + :marked + The template is a multi-line string within ECMAScript 2015 backticks (<code>\`</code>). + The backtick (<code>\`</code>) — which is *not* the same character as a single + quote (`'`) — has many nice features. The feature we're exploiting here + is the ability to compose the string over several lines, which makes for + much more readable HTML. + +:marked + Angular automatically pulls the value of the `title` and `myHero` properties from the component and + inserts those values into the browser. Angular updates the display + when these properties change. + +.l-sub-section + :marked + More precisely, the redisplay occurs after some kind of asynchronous event related to + the view such as a keystroke, a timer completion, or an async `XHR` response. + We don't have those in this sample. + But then the properties aren't changing on their own either. For the moment we must operate on faith. + +:marked + Notice that we haven't called **new** to create an instance of the `AppComponent` class. + Angular is creating an instance for us. How? + + Notice the CSS `selector` in the `@Component` !{_decorator} that specifies an element named `my-app`. + Remember back in [QuickStart](../quickstart.html) that we added the `<my-app>` element to the body of our `index.html` file: + ++makeExcerpt('index.html', 'body') + +:marked + When we bootstrap with the `AppComponent` class (in <ngio-ex path="main.ts"></ngio-ex>), Angular looks for a `<my-app>` + in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it + inside the `<my-app>` tag. + + Try running the app. It should display the title and hero name: +figure.image-display + img(src="/resources/images/devguide/displaying-data/title-and-hero.png" alt="Title and Hero") + ++ifDocsFor('ts') + :marked + Let's review some of the choices we made and consider alternatives. + +:marked + ## Template inline or template file? + + We can store our component's template in one of two places. + We can define it *inline* using the `template` property, as we do here. + Or we can define the template in a separate HTML file and link to it in + the component metadata using the `@Component` !{_decorator}'s `templateUrl` property. + + The choice between inline and separate HTML is a matter of taste, + circumstances, and organization policy. + Here we're using inline HTML because the template is small, and the demo + is simpler without the additional HTML file. + + In either style, the template data bindings have the same access to the component's properties. + ++ifDocsFor('ts') + :marked + ## Constructor or variable initialization? + + We initialized our component properties using variable assignment. + This is a wonderfully concise and compact technique. + + Some folks prefer to declare the properties and initialize them within a constructor like this: + +makeExcerpt('app/app-ctor.component.ts', 'class') + + :marked + That's fine too. The choice is a matter of taste and organization policy. + We'll adopt the more terse "variable assignment" style in this chapter simply because + there will be less code to read. + +.l-main-section#ngFor +:marked + ## Showing !{_an} !{_array} property with ***ngFor** + + We want to display a list of heroes. We begin by adding !{_an} !{_array} of hero names to the component and redefine `myHero` to be the first name in the !{_array}. + ++makeExcerpt('app/app.component.2.ts', 'class') + +:marked + Now we use the Angular `ngFor` directive in the template to display + each item in the `heroes` list. + ++makeExcerpt('app/app.component.2.ts', 'template') + +:marked + Our presentation is the familiar HTML unordered list with `<ul>` and `<li>` tags. Let's focus on the `<li>` tag. + ++makeExcerpt('app/app.component.2.ts ()', 'li', '') + +:marked + We added a somewhat mysterious `*ngFor` to the `<li>` element. + That's the Angular "repeater" directive. + Its presence on the `<li>` tag marks that `<li>` element (and its children) as the "repeater template". + +.alert.is-important + :marked + Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax. + Learn more about this and `ngFor` in the [Template Syntax](./template-syntax.html#ngFor) chapter. + +:marked + Notice the `hero` in the `ngFor` double-quoted instruction; + it is an example of a [template input variable](./template-syntax.html#ngForMicrosyntax). + + Angular duplicates the `<li>` for each item in the list, setting the `hero` variable + to the item (the hero) in the current iteration. Angular uses that variable as the + context for the interpolation in the double curly braces. + +.l-sub-section + :marked + We happened to give `ngFor` !{_an} !{_array} to display. + In fact, `ngFor` can repeat items for any [iterable](!{_iterableUrl}) + object. +:marked + Now the heroes appear in an unordered list. + +figure.image-display + img(src="/resources/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor") + +.l-main-section +:marked + ## Creating a class for the data + + We are defining our data directly inside our component. + That's fine for a demo but certainly isn't a best practice. It's not even a good practice. + Although we won't do anything about that in this chapter, we'll make a mental note to fix this down the road. + + At the moment, we're binding to !{_an} !{_array} of strings. We do that occasionally in real applications, but + most of the time we're binding to more specialized objects. + + Let's turn our !{_array} of hero names into !{_an} !{_array} of `Hero` objects. For that we'll need a `Hero` class. + + Create a new file in the `!{_appDir}` folder called <ngio-ex path="hero.ts"></ngio-ex> with the following code: + ++makeExcerpt('app/hero.ts') + +block hero-class + :marked + We've defined a class with a constructor and two properties: `id` and `name`. + + It might not look like we have properties, but we do. We're taking + advantage of a TypeScript shortcut in our declaration of the constructor parameters. + + Consider the first parameter: + + +makeExcerpt('app/hero.ts ()', 'id') + + :marked + That brief syntax does a lot: + * Declares a constructor parameter and its type + * Declares a public property of the same name + * Initializes that property with the corresponding argument when we "new" an instance of the class + +.l-main-section +:marked + ## Using the Hero class + Let's make the `heroes` property in our component return !{_an} !{_array} of these `Hero` objects. + ++makeExcerpt('app/app.component.3.ts', 'heroes') + +:marked + We'll have to update the template. + At the moment it displays the hero's `id` and `name`. + Let's fix that so we display only the hero's `name` property. + ++makeExcerpt('app/app.component.3.ts', 'template') + +:marked + Our display looks the same, but now we know much better what a hero really is. + +.l-main-section#ngIf +:marked + ## Conditional display with NgIf + + Sometimes an app needs to display a view or a portion of a view only under specific circumstances. + + In our example, we'd like to display a message if we have a large number of heroes, say, more than 3. + + The Angular `ngIf` directive inserts or removes an element based on a !{_boolean} condition. + We can see it in action by adding the following paragraph at the bottom of the template: + ++makeExcerpt('app/app.component.ts', 'message') + +.alert.is-important + :marked + Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax. + Learn more about this and `ngIf` in the [Template Syntax](./template-syntax.html#ngIf) chapter. + +:marked + The [template expression](./template-syntax.html#template-expressions) inside the double quotes + looks much like !{_Lang}, and it _is_ much like !{_Lang}. + When the component's list of heroes has more than 3 items, Angular adds the paragraph to the DOM and the message appears. + If there are 3 or fewer items, Angular omits the paragraph, so no message appears. + +.alert.is-helpful + :marked + Angular isn't showing and hiding the message. It is adding and removing the paragraph element from the DOM. + That hardly matters here. But it would matter a great deal, from a performance perspective, if + we were conditionally including or excluding a big chunk of HTML with many data bindings. + +:marked + Try it out. Because the !{_array} has four items, the message should appear. + Go back into <ngio-ex path="app.component.ts"></ngio-ex> and delete or comment out one of the elements from the hero !{_array}. + The browser should refresh automatically and the message should disappear. + +.l-main-section +:marked + ## Summary + Now we know how to use: + - **Interpolation** with double curly braces to display a component property + - **ngFor** to display !{_an} !{_array} of items + - A !{_Lang} class to shape the **model data** for our component and display properties of that model + - **ngIf** to conditionally display a chunk of HTML based on a boolean expression + + Here's our final code: + +block final-code + +makeTabs(`displaying-data/ts/app/app.component.ts, + displaying-data/ts/app/hero.ts, + displaying-data/ts/app/main.ts`, + 'final,,', + 'app/app.component.ts, app/hero.ts, main.ts') diff --git a/public/docs/ts/_cache/guide/hierarchical-dependency-injection.jade b/public/docs/ts/_cache/guide/hierarchical-dependency-injection.jade new file mode 100644 index 0000000000..6fcf422eda --- /dev/null +++ b/public/docs/ts/_cache/guide/hierarchical-dependency-injection.jade @@ -0,0 +1,178 @@ +block includes + include ../_util-fns + +:marked + We learned the basics of Angular Dependency injection in the + [Dependency Injection](./dependency-injection.html) chapter. + + Angular has a Hierarchical Dependency Injection system. + There is actually a tree of injectors + that parallel an application's component tree. + We can re-configure the injectors at any level of that component tree with + interesting and useful results. + + In this chapter we explore these points and write some code. + + Try the <live-example></live-example>. + +.l-main-section +:marked + ## The Injector Tree + + In the [Dependency Injection](./dependency-injection.html) chapter + we learned how to configure a dependency injector and how to retrieve dependencies where we need them. + + We oversimplified. In fact, there is no such thing as ***the*** injector! + An application may have multiple injectors! + + An Angular application is a tree of components. Each component instance has its own injector! + The tree of components parallels the tree of injectors. + + +.l-sub-section + :marked + Angular doesn't *literally* create a separate injector for each component. + Every component doesn't need its own injector and it would be horribly inefficient to create + masses of injectors for no good purpose. + + But it is true that every component ***has an injector*** (even if it shares that injector with another component) + and there may be many different injector instances operating at different levels of the component tree. + + It is useful to pretend that every component has its own injector. +:marked + Consider a simple variation on the Tour of Heroes application consisting of three different components: + `HeroesApp`, `HeroesListComponent` and `HeroesCardComponent`. + The `HeroesApp` holds a single instance of `HeroesListComponent`. + The new twist is that the `HeroesListComponent` may hold and manage multiple instances of the `HeroesCardComponent`. + + The following diagram represents the state of the component tree when there are three instances of `HeroesCardComponent` + open simultaneously. + +figure.image-display + img(src="/resources/images/devguide/dependency-injection/component-hierarchy.png" alt="injector tree" width="500") + +:marked + Each component instance gets its own injector and an injector at one level is a child injector of the injector above it in the tree. + + When a component at the bottom requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector. + If the component's injector lacks the provider, it passes the request up to its parent component's injector. + If that injector can't satisfy the request, it passes it along to *its* parent component's injector. + The requests keep bubbling up until we find an injector that can handle the request or run out of component ancestors. + If we run out of ancestors, Angular throws an error. + +.l-sub-section + :marked + There's a third possibility. An intermediate component can declare that it is the "host" component. + The hunt for providers will climb no higher than the injector for this host component. + We'll reserve discussion of this option for another day. +:marked + Such a proliferation of injectors makes little sense until we consider the possibility that injectors at different levels can be + configured with different providers. We don't *have* to re-configure providers at every level. But we *can*. + + If we don't re-configure, the tree of injectors appears to be flat. All requests bubble up to the root injector that we + configured with the `bootstrap` method. + + The ability to configure one or more providers at different levels opens up interesting and useful possibilities. + + Let’s return to our Car example. + Suppose we configured the root injector (marked as A) with providers for `Car`, `Engine` and `Tires`. + We create a child component (B) that defines its own providers for `Car` and `Engine` + This child is the parent of another component (C) that defines its own provider for `Car`. + + Behind the scenes each component sets up its own injector with one or more providers defined for that component itself. + + When we resolve an instance of `Car` at the deepest component (C), + its injector produces an instance of `Car` resolved by injector (C) with an `Engine` resolved by injector (B) and + `Tires` resolved by the root injector (A). + +figure.image-display + img(src="/resources/images/devguide/dependency-injection/injector-tree.png" alt="injector tree" width="600") + +.l-main-section +:marked + ## Component Injectors + + In the previous section, we talked about injectors and how they are organized like a tree. Lookups follow the injector tree upwards until they find the requested thing to inject. But when do we actually want to provide providers on the root injector and when do we want to provide them on a child injector? + + Consider you are building a component to show a list of super heroes that displays each super hero in a card with its name and superpower. There should also be an edit button that opens up an editor to change the name and superpower of our hero. + + One important aspect of the editing functionality is that we want to allow multiple heroes to be in edit mode at the same time and that one can always either commit or cancel the proposed changes. + + Let’s take a look at the `HeroesListComponent` which is the root component for this example. + ++makeExample('hierarchical-dependency-injection/ts/app/heroes-list.component.ts', null, 'app/heroes-list.component.ts') + +:marked + Notice that it imports the `HeroService` that we’ve used before so we can skip its declaration. The only difference is that we’ve used a more formal approach for our `Hero`model and defined it upfront as such. + ++makeExample('hierarchical-dependency-injection/ts/app/hero.ts', null, 'app/hero.ts')(format=".") + +:marked + Our `HeroesListComponent` defines a template that creates a list of `HeroCardComponent`s and `HeroEditorComponent`s, each bound to an instance of hero that is returned from the `HeroService`. Ok, that’s not entirely true. It actually binds to an `EditItem<Hero>` which is a simple generic datatype that can wrap any type and indicate if the item being wrapped is currently being edited or not. + ++makeExample('hierarchical-dependency-injection/ts/app/edit-item.ts', null, 'app/edit-item.ts')(format=".") + +:marked + But how is `HeroCardComponent` implemented? Let’s take a look. + ++makeExample('hierarchical-dependency-injection/ts/app/hero-card.component.ts', null, 'app/hero-card.component.ts') + +:marked + The `HeroCardComponent` is basically a component that defines a template to render a hero. Nothing more. + + Let’s get to the interesting part and take a look at the `HeroEditorComponent` + ++makeExample('hierarchical-dependency-injection/ts/app/hero-editor.component.ts', null, 'app/hero-editor.component.ts') + +:marked + Now here it’s getting interesting. The `HeroEditorComponent`defines a template with an input to change the name of the hero and a `cancel` and a `save` button. Remember that we said we want to have the flexibility to cancel our editing and restore the old value? This means we need to maintain two copies of our `Hero` that we want to edit. Thinking ahead, this is a perfect use case to abstract it into its own generic service since we have probably more cases like this in our app. + + And this is where the `RestoreService` enters the stage. + ++makeExample('hierarchical-dependency-injection/ts/app/restore.service.ts', null, 'app/restore.service.ts') + +:marked + All this tiny service does is define an API to set a value of any type which can be altered, retrieved or set back to its initial value. That’s exactly what we need to implement the desired functionality. + + Our `HeroEditComponent` uses this services under the hood for its `hero` property. It intercepts the `get` and `set` method to delegate the actual work to our `RestoreService` which in turn makes sure that we won’t work on the original item but on a copy instead. + + At this point we may be scratching our heads asking what this has to do with component injectors? + Look closely at the metadata for our `HeroEditComponent`. Notice the `providers` property. + ++makeExample('hierarchical-dependency-injection/ts/app/hero-editor.component.ts', 'providers') +:marked + This adds a `RestoreService` provider to the injector of the `HeroEditComponent`. + Couldn’t we simply alter our bootstrap call to this? + ++makeExample('hierarchical-dependency-injection/ts/app/main.ts', 'bad-alternative') +:marked + Technically we could, but our component wouldn’t quite behave the way it is supposed to. Remember that each injector treats the services that it provides as singletons. However, in order to be able to have multiple instances of `HeroEditComponent` edit multiple heroes at the same time we need to have multiple instances of the `RestoreService`. More specifically, each instance of `HeroEditComponent` needs to be bound to its own instance of the `RestoreService`. + + By configuring a provider for the `RestoreService` on the `HeroEditComponent`, we get exactly one new instance of the `RestoreService`per `HeroEditComponent`. + + Does that mean that services aren’t singletons anymore in Angular 2? Yes and no. + There can be only one instance of a service type in a particular injector. + But we've learned that we can have multiple injectors operating at different levels of the application's component tree. + Any of those injectors could have its own instance of the service. + + If we defined a `RestoreService` provider only on the root component, + we would have exactly one instance of that service and it would be shared across the entire application. + + That’s clearly not what we want in this scenario. We want each component to have its own instance of the `RestoreService`. + Defining (or re-defining) a provider at the component level creates a new instance of the service for each new instance + of that component. We've made the `RestoreService` a kind of "private" singleton for each `HeroEditComponent`, + scoped to that component instance and its child components. + + <!-- + ## Advanced Dependency Injection in Angular 2 + + Restrict Dependency Lookups + [TODO] (@Host) This has been postponed for now until we come up with a decent use case + + + .l-main-section + :marked + ## Dependency Visibility + + [TODO] (providers vs viewProviders) This has been postponed for now until come up with a decent use case + --> diff --git a/public/docs/ts/_cache/guide/lifecycle-hooks.jade b/public/docs/ts/_cache/guide/lifecycle-hooks.jade new file mode 100644 index 0000000000..427a1f61ea --- /dev/null +++ b/public/docs/ts/_cache/guide/lifecycle-hooks.jade @@ -0,0 +1,574 @@ +block includes + include ../_util-fns + +- var top="vertical-align:top" + +:marked + # Component Lifecycle + A Component has a lifecycle managed by Angular itself. Angular creates it, renders it, creates and renders its children, + checks it when its data-bound properties change, and destroys it before removing it from the DOM. + + Angular offers **component lifecycle hooks** + that give us visibility into these key moments and the ability to act when they occur. + + We cover these hooks in this chapter and demonstrate how they work in code. + + * [The lifecycle hooks](#hooks-overview) + * [The hook-call sequence](#hook-sequence) + * [Other Angular lifecycle hooks](#other-lifecycles) + * [The lifecycle sample](#the-sample) + * [All](#peek-a-boo) + * [Spying OnInit and OnDestroy](#spy) + * [OnChanges](#onchanges) + * [DoCheck](#docheck) + * [AfterViewInit and AfterViewChecked](#afterview) + * [AfterContentInit and AfterContentChecked](#aftercontent) + + Try the <live-example></live-example>. + +a#hooks-overview +.l-main-section +:marked + ## Component lifecycle Hooks + Directive and component instances have a lifecycle + as Angular creates, updates, and destroys them. + + Developers can tap into key moments in that lifecycle by implementing + one or more of the *Lifecycle Hook* interfaces in the Angular `core` library. + + Each interface has a single hook method whose name is the interface name prefixed with `ng`. + For example, the `OnInit` interface has a hook method named `ngOnInit`. + We might implement it in a component class like this: ++makeExample('lifecycle-hooks/ts/app/peek-a-boo.component.ts', 'ngOnInit', 'peek-a-boo.component.ts (excerpt)')(format='.') +:marked + No directive or component will implement all of them and some of the hooks only make sense for components. + Angular only calls a directive/component hook method *if it is defined*. +block optional-interfaces + .l-sub-section + :marked + ### Interface optional? + The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective. + The JavaScript language doesn't have interfaces. + Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript. + + Fortunately, they aren't necessary. + We don't have to add the lifecycle hook interfaces to our directives and components to benefit from the hooks themselves. + + Angular instead inspects our directive and component classes and calls the hook methods *if they are defined*. + Angular will find and call methods like `ngOnInit()`, with or without the interfaces. + + Nonetheless, we strongly recommend adding interfaces to TypeScript directive classes + in order to benefit from strong typing and editor tooling. + +:marked + Here are the component lifecycle hook methods: + + ### Directives and Components + +table(width="100%") + col(width="20%") + col(width="80%") + tr + th Hook + th Purpose + tr(style=top) + td ngOnInit + td + :marked + Initialize the directive/component after Angular initializes the data-bound input properties. + tr(style=top) + td ngOnChanges + td + :marked + Respond after Angular sets a data-bound input property. + The method receives a `changes` object of current and previous values. + tr(style=top) + td ngDoCheck + td + :marked + Detect and act upon changes that Angular can or won't + detect on its own. Called every change detection run. + tr(style=top) + td ngOnDestroy + td + :marked + Cleanup just before Angular destroys the directive/component. + Unsubscribe observables and detach event handlers to avoid memory leaks. + +:marked + ### Components only + +table(width="100%") + col(width="20%") + col(width="80%") + tr + th Hook + th Purpose + tr(style=top) + td ngAfterContentInit + td + :marked + After Angular projects external content into its view. + tr(style=top) + td ngAfterContentChecked + td + :marked + After Angular checks the bindings of the external content that it projected into its view. + tr(style=top) + td ngAfterViewInit + td + :marked + After Angular creates the component's view(s). + tr(style=top) + td ngAfterViewChecked + td + :marked + After Angular checks the bindings of the component's view(s). +:marked + Angular does not call the hook methods in this order. + +a(id="hook-sequence") +.l-main-section +:marked + ## Lifecycle sequence + *After* Angular creates a component/directive by `new`-ing its constructor, + it calls the lifecycle hook methods in the following sequence at specific moments: +table(width="100%") + col(width="20%") + col(width="80%") + tr + th Hook + th Timing + tr(style=top) + td ngOnChanges + td + :marked + before `ngOnInit` and when a data-bound input property value changes. + tr(style=top) + td ngOnInit + td + :marked + after the first `ngOnChanges`. + tr(style=top) + td ngDoCheck + td + :marked + during every Angular change detection cycle. + tr(style=top) + td ngAfterContentInit + td + :marked + after projecting content into the component. + tr(style=top) + td ngAfterContentChecked + td + :marked + after every check of projected component content. + tr(style=top) + td ngAfterViewInit + td + :marked + after initializing the component's views and child views. + tr(style=top) + td ngAfterViewChecked + td + :marked + after every check of the component's views and child views. + tr(style=top) + td ngOnDestroy + td + :marked + just before Angular destroys the directive/component. + +a(id="other-lifecycles") +.l-main-section +:marked + ## Other lifecycle hooks + + Other Angular sub-systems may have their own lifecycle hooks apart from the component hooks we've listed. + The router, for instance, also has it's own [router lifecycle hooks](router.html#router-lifecycle-hooks) + that allow us to tap into specific moments in route navigation. + + A parallel can be drawn between `ngOnInit` and `routerOnActivate`. + Both are prefixed so as to avoid collision, and both run right when a component is 'booting' up. + + 3rd party libraries might implement their hooks as well in order to give us, the developers, more + control over how these libraries are used. + +.l-main-section#the-sample +:marked + ## Lifecycle exercises + + The <live-example></live-example> + demonstrates the lifecycle hooks in action through a series of exercises + presented as components under the control of the root `AppComponent`. + + They follow a common pattern: a *parent* component serves as a test rig for + a *child* component that illustrates one or more of the lifecycle hook methods. + + Here's a brief description of each exercise: + +table(width="100%") + col(width="20%") + col(width="80%") + tr + th Component + th Description + tr(style=top) + td <a href="#peek-a-boo">Peek-a-boo</a> + td + :marked + Demonstrates every lifecycle hook. + Each hook method writes to the on-screen log. + tr(style=top) + td <a href="#spy">Spy</a> + td + :marked + Directives have lifecycle hooks too. + We create a `SpyDirective` that logs when the element it spies upon is + created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks. + + We apply the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater + managed by the parent `SpyComponent`. + tr(style=top) + td <a href="#onchanges">OnChanges</a> + td + :marked + See how Angular calls the `ngOnChanges` hook with a `changes` object + every time one of the component input properties changes. + Shows how to interpret the `changes` object. + tr(style=top) + td <a href="#docheck">DoCheck</a> + td + :marked + Implements an `ngDoCheck` method with custom change detection. + See how often Angular calls this hook and watch it post changes to a log. + tr(style=top) + td <a href="#afterview">AfterView</a> + td + :marked + Shows what Angular means by a *view*. + Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks. + tr(style=top) + td <a href="#aftercontent">AfterContent</a> + td + :marked + Shows how to project external content into a component and + how to distinguish projected content from a component's view children. + Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks. + tr(style=top) + td Counter + td + :marked + Demonstrates a combination of a component and a directive + each with its own hooks. + + In this example, a `CounterComponent` logs a change (via `ngOnChanges`) + every time the parent component increments its input counter property. + Meanwhile, we apply the `SpyDirective` from the previous example + to the `CounterComponent` log and watch log entries be created and destroyed. + +:marked + We discuss the exercises in further detail over this chapter as we learn more about the lifecycle hooks. + +a(id="peek-a-boo") +.l-main-section +:marked + ## Peek-a-boo: all hooks + The `PeekABooComponent` demonstrates all of the hooks in one component. + + In real life, we'd rarely if ever implement all of the interfaces like this. + We do so in peek-a-boo in order to watch Angular call the hooks in the expected order. + + In this snapshot, we clicked the *Create...* button and then the *Destroy...* button. +figure.image-display + img(src="/resources/images/devguide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo") +:marked + The sequence of log messages follows the prescribed hook calling order: + `OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x), + `AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`. + +.l-sub-section + :marked + The constructor isn't an Angular hook *per se*. + We log in it to confirm that input properties (the `name` property in this case) have no assigned values at construction. +:marked + Had we clicked the *Update Hero* button, we'd have seen another `OnChanges` and two more triplets of + `DoCheck`, `AfterContentChecked` and `AfterViewChecked`. + Clearly these three hooks fire a *lot* and we must keep the logic we put in these hooks + as lean as possible! + + Our next examples focus on hook details. + +.a(id="spy") +.l-main-section +:marked + ## Spying *OnInit* and *OnDestroy* + + We're going undercover for these two hooks. We want to know when an element is initialized or destroyed, + but we don't want *it* to know we're watching. + + This is the perfect infiltration job for a directive. + Our heroes will never know it's there. + +.l-sub-section + :marked + Kidding aside, we're emphasizing two key points: + + 1. Angular calls hook methods for *directives* as well as components. + + 2. A spy directive can gives us insight into a DOM object that we cannot change directly. + Obviously we can't change the implementation of a native `div`. + We can't modify a third party component either. + But we can watch both with a directive. + + +:marked + Our sneaky spy directive is simple, consisting almost entirely of `ngOnInit` and `ngOnDestroy` hooks + that log messages to the parent via an injected `LoggerService`. + ++makeExample('lifecycle-hooks/ts/app/spy.directive.ts', 'spy-directive')(format=".") + +:marked + We can apply the spy to any native or component element and it'll be initialized and destroyed + at the same time as that element. + Here we attach it to the repeated hero `<div>` ++makeExample('lifecycle-hooks/ts/app/spy.component.html', 'template')(format=".") + +:marked + Each spy's birth and death marks the birth and death of the attached hero `<div>` + with an entry in the *Hook Log* as we see here: + +figure.image-display + img(src='/resources/images/devguide/lifecycle-hooks/spy-directive.gif' alt="Spy Directive") + +:marked + Adding a hero results in a new hero `<div>`. The spy's `ngOnInit` logs that event. + We see a new entry for each hero. + + The *Reset* button clears the `heroes` list. + Angular removes all hero divs from the DOM and destroys their spy directives at the same time. + The spy's `ngOnDestroy` method reports its last moments. + + The `ngOnInit` and `ngOnDestroy` methods have more vital roles to play in real applications. + Let's see why we need them. + + ### OnInit + + We turn to `ngOnInit` for two main reasons: + 1. To perform complex initializations shortly after construction + 1. To set up the component after Angular sets the input properties + + An `ngOnInit` often fetches data for the component as shown in the + [Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapters. + + We don't fetch data in a component constructor. Why? + Because experienced developers agree that components should be cheap and safe to construct. + We shouldn't worry that a new component will try to contact a remote server when + created under test or before we decide to display it. + Constructors should do no more than set the initial local variables to simple values. + + When a component must start working _soon_ after creation, + we can count on Angular to call the `ngOnInit` method to jumpstart it. + That's where the heavy initialization logic belongs. + + Remember also that a directive's data-bound input properties are not set until _after construction_. + That's a problem if we need to initialize the directive based on those properties. + They'll have been set when our `ngOninit` runs. +.l-sub-section + :marked + Our first opportunity to access those properties is the `ngOnChanges` method which + Angular calls before `ngOnit`. But Angular calls `ngOnChanges` many times after that. + It only calls `ngOnit` once. +:marked + ### OnDestroy + + Put cleanup logic in `ngOnDestroy`, the logic that *must* run before Angular destroys the directive. + + This is the time to notify another part of the application that this component is going away. + + This is the place to free resources that won't be garbage collected automatically. + Unsubscribe from observables and DOM events. Stop interval timers. + Unregister all callbacks that this directive registered with global or application services. + We risk memory leaks if we neglect to do so. + +.l-main-section +:marked + ## OnChanges + + We monitor the `OnChanges` hook in this example. + Angular calls its `ngOnChanges` method whenever it detects changes to ***input properties*** of the component (or directive). + + Here is our implementation of the hook. ++makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'ng-on-changes', 'OnChangesComponent (ngOnChanges)')(format=".") +:marked + The `ngOnChanges` method takes an object that maps each changed property name to a + [SimpleChange](../api/core/index/SimpleChange-class.html) object with the current and previous property values. + We iterate over the changed properties and log them. + + The input properties for our example `OnChangesComponent` are `hero` and `power`. ++makeExample('lifecycle-hooks/ts/app/on-changes.component.ts', 'inputs')(format=".") +:marked + The parent binds to them like this: + ++makeExample('lifecycle-hooks/ts/app/on-changes-parent.component.html', 'on-changes') +:marked + Here's the sample in action as we make changes. + +figure.image-display + img(src='/resources/images/devguide/lifecycle-hooks/on-changes-anim.gif' alt="OnChanges") + +:marked + We see log entries as the string value of the *power* property changes. But the `ngOnChanges` did not catch changes to `hero.name` + That's surprising at first. + + Angular only calls the hook when the value of the input property changes. + The value of the `hero` property is the *reference to the hero object*. + Angular doesn't care that the hero's own `name` property changed. + The hero object *reference* didn't change so, from Angular's perspective, there is no change to report! + +.l-main-section +:marked + ## DoCheck + We can use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own. +.l-sub-section + :marked + With this method we can detect a change that Angular overlooked. + What we do with that information to refresh the display is a separate matter. +:marked + The *DoCheck* sample extends the *OnChanges* sample with this implementation of `DoCheck`: ++makeExample('lifecycle-hooks/ts/app/do-check.component.ts', 'ng-do-check', 'DoCheckComponent (ngDoCheck)')(format=".") +:marked + We manually check everything that we care about, capturing and comparing against previous values. + We write a special message to the log when there are no substantive changes + to the hero or the power so we can keep an eye on the method's performance characteristics. + + The results are illuminating: + +figure.image-display + img(src='/resources/images/devguide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck") +:marked + We now are able to detect when the hero's `name` has changed. But we must be careful. + + The `ngDoCheck` hook is called with enormous frequency — + after _every_ change detection cycle no matter where the change occurred. + It's called over twenty times in this example before the user can do anything. + + Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*. + Mere mousing into another input box triggers a call. + Relatively few calls reveal actual changes to pertinent data. + Clearly our implementation must be very lightweight or the user experience may suffer. + +.l-sub-section + :marked + We also see that the `ngOnChanges` method is called in contradiction of the + [incorrect API documentation](../api/core/index/DoCheck-class.html). + +.l-main-section +:marked + ## AfterView + The *AfterView* sample explores the `AfterViewInit` and `AfterViewChecked` hooks that Angular calls + *after* it creates a component's child views. + + Here's a child view that displays a hero's name in an input box: ++makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'child-view', 'ChildComponent')(format=".") +:marked + The `AfterViewComponent` displays this child view *within its template*: ++makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'template', 'AfterViewComponent (template)')(format=".") +:marked + The following hooks take action based on changing values *within the child view* + which we can only reach by querying for the child view via the property decorated with + [@ViewChild](../api/core/index/ViewChild-var.html). + ++makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'hooks', 'AfterViewComponent (class excerpts)')(format=".") +.a(id="wait-a-tick") +:marked + ### Abide by the unidirectional data flow rule + The `doSomething` method updates the screen when the hero name exceeds 10 characters. + ++makeExample('lifecycle-hooks/ts/app/after-view.component.ts', 'do-something', 'AfterViewComponent (doSomething)')(format=".") +:marked + Why does the `doSomething` method wait a tick before updating `comment`? + + Because we must adhere to Angular's unidirectional data flow rule which says that + we may not update the view *after* it has been composed. + Both hooks fire after the component's view has been composed. + + Angular throws an error if we update component's data-bound `comment` property immediately (try it!). +block tick-methods + :marked + The `LoggerService.tick` methods, which are implemented by a call to `setTimeout`, postpone the update one turn of the of the browser's JavaScript cycle ... and that's long enough. + +:marked + Here's *AfterView* in action +figure.image-display + img(src='/resources/images/devguide/lifecycle-hooks/after-view-anim.gif' alt="AfterView") +:marked + Notice that Angular frequently calls `AfterViewChecked`, often when there are no changes of interest. + Write lean hook methods to avoid performance problems. + +.l-main-section +:marked + ## AfterContent + The *AfterContent* sample explores the `AfterContentInit` and `AfterContentChecked` hooks that Angular calls + *after* Angular projects external content into the component. + + ### Content projection + *Content projection* is a way to import HTML content from outside the component and insert that content + into the component's template in a designated spot. + +.l-sub-section + :marked + Angular 1 developers know this technique as *transclusion*. + +:marked + We'll illustrate with a variation on the [previous](#afterview) example + whose behavior and output is almost the same. + + This time, instead of including the child view within the template, we'll import it from + the `AfterContentComponent`'s parent. Here's the parent's template. ++makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'parent-template', 'AfterContentParentComponent (template excerpt)')(format=".") +:marked + Notice that the `<my-child>` tag is tucked between the `<after-content>` tags. + We never put content between a component's element tags *unless we intend to project that content + into the component*. + + Now look at the component's template: ++makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'template', 'AfterContentComponent (template)')(format=".") +:marked + The `<ng-content>` tag is a *placeholder* for the external content. + They tell Angular where to insert that content. + In this case, the projected content is the `<my-child>` from the parent. +figure.image-display + img(src='/resources/images/devguide/lifecycle-hooks/projected-child-view.png' width="230" alt="Projected Content") +:marked +.l-sub-section + :marked + The tell-tale signs of *content projection* are (a) HTML between component element tags + and (b) the presence of `<ng-content>` tags in the component's template. +:marked + ### AfterContent hooks + *AfterContent* hooks are similar to the *AfterView* hooks. The key difference is the kind of child component + that we're looking for. + + * The *AfterView* hooks concern `ViewChildren`, the child components whose element tags + appear *within* the component's template. + + * The *AfterContent* hooks concern `ContentChildren`, the child components that Angular + projected into the component. + + The following *AfterContent* hooks take action based on changing values in a *content child* + which we can only reach by querying for it via the property decorated with + [@ContentChild](../api/core/index/ContentChild-var.html). + ++makeExample('lifecycle-hooks/ts/app/after-content.component.ts', 'hooks', 'AfterContentComponent (class excerpts)')(format=".") + +:marked + ### No unidirectional flow worries + + This component's `doSomething` method update's the component's data-bound `comment` property immediately. + There's no [need to wait](#wait-a-tick). + + Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks. + Angular completes composition of the projected content *before* finishing the composition of this component's view. + We still have a window of opportunity to modify that view. diff --git a/public/docs/ts/_cache/guide/pipes.jade b/public/docs/ts/_cache/guide/pipes.jade new file mode 100644 index 0000000000..3d50ef7f42 --- /dev/null +++ b/public/docs/ts/_cache/guide/pipes.jade @@ -0,0 +1,487 @@ +block includes + include ../_util-fns + +:marked + Every application starts out with what seems like a simple task: get data, transform them, and show them to users. + Getting data could be as simple as creating a local variable or as complex as streaming data over a Websocket. + + Once data arrive, we could push their raw `toString` values directly to the view. + That rarely makes for a good user experience. + E.g., almost everyone prefers a simple birthday date like + <samp>April 15, 1988</samp> to the original raw string format + — <samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>. + + Clearly some values benefit from a bit of massage. We soon discover that we + desire many of the same transformations repeatedly, both within and across many applications. + We almost think of them as styles. + In fact, we'd like to apply them in our HTML templates as we do styles. + + Introducing Angular pipes, a way to write display-value transformations that we can declare in our HTML! + Try the <live-example></live-example>. + +.l-main-section +:marked + ## Using Pipes + + A pipe takes in data as input and transforms it to a desired output. + We'll illustrate by transforming a component's birthday property into + a human-friendly date. + ++makeExample('pipes/ts/app/hero-birthday1.component.ts', null, 'app/hero-birthday1.component.ts')(format='.') + +:marked + Focus on the component's template. + ++makeExample('pipes/ts/app/app.component.html', 'hero-birthday-template')(format=".") + +:marked + Inside the interpolation expression we flow the component's `birthday` value through the + [pipe operator](./template-syntax.html#pipe) ( | ) to the [Date pipe](../api/common/index/DatePipe-class.html) + function on the right. All pipes work this way. + +.l-sub-section + :marked + The `Date` and `Currency` pipes need the **ECMAScript Internationalization API**. + Safari and other older browsers don't support it. We can add support with a polyfill. + + code-example(language="html"). + <script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script> + +.l-main-section +:marked + ## Built-in pipes + Angular comes with a stock of pipes such as + `DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`. + They are all immediately available for use in any template. + +.l-sub-section + :marked + Learn more about these and many other built-in pipes in the [API Reference](../api/#!?apiFilter=pipe); + filter for entries that include the word "pipe". + + Angular 2 doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in an [appendix below](#no-filter-pipe). + +.l-main-section +:marked + ## Parameterizing a Pipe + + A pipe may accept any number of optional parameters to fine-tune its output. + We add parameters to a pipe by following the pipe name with a colon ( : ) and then the parameter value + (e.g., `currency:'EUR'`). If our pipe accepts multiple parameters, we separate the values with colons (e.g. `slice:1:5`) + + We'll modify our birthday template to give the date pipe a format parameter. + After formatting the hero's April 15th birthday, it should render as **<samp>04/15/88</samp>**: + ++makeExample('pipes/ts/app/app.component.html', 'format-birthday')(format=".") + +:marked + The parameter value can be any valid + [template expression](./template-syntax.html#template-expressions) + such as a string literal or a component property. + In other words, we can control the format through a binding the same way we control the birthday value through a binding. + + Let's write a second component that *binds* the pipe's format parameter + to the component's `format` property. Here's the template for that component: + ++makeExample('pipes/ts/app/hero-birthday2.component.ts', 'template', 'app/hero-birthday2.component.ts (template)')(format=".") + +:marked + We also added a button to the template and bound its click event to the component's `toggleFormat()` method. + That method toggles the component's `format` property between a short form + (`'shortDate'`) and a longer form (`'fullDate'`). + ++makeExample('pipes/ts/app/hero-birthday2.component.ts', 'class', 'app/hero-birthday2.component.ts (class)')(format='.') + +:marked + As we click the button, the displayed date alternates between + "**<samp>04/15/1988</samp>**" and + "**<samp>Friday, April 15, 1988</samp>**". + +figure.image-display + img(src='/resources/images/devguide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle") +:marked + +.l-sub-section + :marked + Learn more about the `DatePipes` format options in the [API Docs](../api/common/index/DatePipe-class.html). + +:marked + ## Chaining pipes + + We can chain pipes together in potentially useful combinations. + In the following example, we chain the birthday to the `DatePipe` and on to the `UpperCasePipe` + so we can display the birthday in uppercase. The following birthday displays as + **<samp>APR 15, 1988</samp>**. + ++makeExample('pipes/ts/app/app.component.html', 'chained-birthday')(format=".") + +:marked + This example — which displays **<samp>FRIDAY, APRIL 15, 1988</samp>** — + chains the same pipes as above, but passes in a parameter to `date` as well. + ++makeExample('pipes/ts/app/app.component.html', 'chained-parameter-birthday')(format=".") + +.l-main-section +:marked + ## Custom Pipes + + We can write our own custom pipes. + Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers: + ++makeExample('pipes/ts/app/exponential-strength.pipe.ts', null, 'app/exponential-strength.pipe.ts')(format=".") + +:marked + This pipe definition reveals several key points: + + * A pipe is a class decorated with pipe metadata. + + * The pipe class implements the `PipeTransform` interface's `transform` method that + accepts an input value followed by optional parameters and returns the transformed value. + + * There will be one additional argument to the `transform` method for each parameter passed to the pipe. + Our pipe has one such parameter: the `exponent`. + + * We tell Angular that this is a pipe by applying the + `@Pipe` #{_decorator} which we import from the core Angular library. + + * The `@Pipe` #{_decorator} allows us to define the + pipe name that we'll use within template expressions. It must be a valid JavaScript identifier. + Our pipe's name is `exponentialStrength`. + +.l-sub-section + :marked + ### The *PipeTransform* Interface + + The `transform` method is essential to a pipe. + The `PipeTransform` *interface* defines that method and guides both tooling and the compiler. + It is technically optional; Angular looks for and executes the `transform` method regardless. + +:marked + Now we need a component to demonstrate our pipe. ++makeExample('pipes/ts/app/power-booster.component.ts',null,'app/power-booster.component.ts')(format='.') +figure.image-display + img(src='/resources/images/devguide/pipes/power-booster.png' alt="Power Booster") + +:marked + Two things to note: + 1. We use our custom pipe the same way we use the built-in pipes. + + 1. We must include our pipe in the `pipes` #{_array} of the `@Component` #{_decorator}. + +.callout.is-helpful + header Remember the pipes #{_array}! + :marked + Angular reports an error if we neglect to list our custom pipe. + We didn't list the `DatePipe` in our previous example because all + Angular built-in pipes are pre-registered. + Custom pipes must be registered manually. + +:marked + If we try the <live-example></live-example>, + we can probe its behavior by changing the value and the optional exponent in the template. + + ## Power Boost Calculator (extra-credit) + + It's not much fun updating the template to test our custom pipe. + We could upgrade the example to a "Power Boost Calculator" that combines + our pipe and two-way data binding with `ngModel`. + ++makeExample('pipes/ts/app/power-boost-calculator.component.ts', null, '/app/power-boost-calculator.component.ts')(format='.') + +figure.image-display + img(src='/resources/images/devguide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator") + +.l-main-section +a#change-detection +:marked + ## Pipes and Change Detection + + Angular looks for changes to data-bound values through a *change detection* process that runs after every JavaScript event: + every keystroke, mouse move, timer tick, and server response. This could be expensive. + Angular strives to lower the cost whenever possible and appropriate. + + Angular picks a simpler, faster change detection algorithm when we use a pipe. Let's see how. + + ### No pipe + + The component in our next example uses the default, aggressive change detection strategy to monitor and update + its display of every hero in the `heroes` #{_array}. Here's the template: + ++makeExample('pipes/ts/app/flying-heroes.component.html', 'template-1', 'app/flying-heroes.component.html (v1)')(format='.') + +:marked + The companion component class provides heroes, adds new heroes into the #{_array}, and can reset the #{_array}. ++makeExample('pipes/ts/app/flying-heroes.component.ts', 'v1', 'app/flying-heroes.component.ts (v1)')(format='.') + +:marked + We can add a new hero and Angular updates the display when we do. + The `reset` button replaces `heroes` with a new #{_array} of the original heroes and Angular updates the display when we do. + If we added the ability to remove or change a hero, Angular would detect those changes too and update the display as well. + + ### Flying Heroes pipe + + Let's add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly. ++makeExample('pipes/ts/app/flying-heroes.component.html', 'template-flying-heroes', 'app/flying-heroes.component.html (flyers)')(format='.') +:marked + Here's the `FlyingHeroesPipe` implementation which follows the pattern for custom pipes we saw earlier. ++makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pure', 'app/flying-heroes.pipe.ts')(format='.') + +:marked + When we run the sample now we see odd behavior (try it in the <live-example></live-example>). + Every hero we add is a flying hero but none of them are displayed. + + Although we're not getting the behavior we want, Angular isn't broken. + It's just using a different change detection algorithm — one that ignores changes to the list or any of its items. + + Look at how we're adding a new hero: ++makeExample('pipes/ts/app/flying-heroes.component.ts', 'push')(format='.') +:marked + We're adding the new hero into the `heroes` #{_array}. The reference to the #{_array} hasn't changed. + It's the same #{_array}. That's all Angular cares about. From its perspective, *same #{_array}, no change, no display update*. + + We can fix that. Let's create a new #{_array} with the new hero appended and assign that to `heroes`. + This time Angular detects that the #{_array} reference has changed. + It executes the pipe and updates the display with the new #{_array} which includes the new flying hero. + + *If we **mutate** the #{_array}, no pipe is invoked and no display updated; + if we **replace** the #{_array}, then the pipe executes and the display is updated*. + The *Flying Heroes* extends the + code with checkbox switches and additional displays to help us experience these effects. + +figure.image-display + img(src='/resources/images/devguide/pipes/flying-heroes-anim.gif' alt="Flying Heroes") + +:marked + Replacing the #{_array} is an efficient way to signal to Angular that it should update the display. + When do we replace the #{_array}? When the data change. + That's an easy rule to follow in *this toy* example + where the only way to change the data is by adding a new hero. + + More often we don't know when the data have changed, + especially in applications that mutate data in many ways, + perhaps in application locations far away. + A component in such an application usually can't know about those changes. + Moreover, it's unwise to distort our component design to accommodate a pipe. + We strive as much as possible to keep the component class independent of the HTML. + The component should be unaware of pipes. + + Perhaps we should consider a different kind of pipe for filtering flying heroes, an *impure pipe*. + +.l-main-section +:marked + ## Pure and Impure Pipes + + There are two categories of pipes: **pure** and **impure**. + Pipes are pure by default. Every pipe we've seen so far has been pure. + We make a pipe impure by setting its pure flag to false. We could make the `FlyingHeroesPipe` + impure like this: + ++makeExample('pipes/ts/app/flying-heroes.pipe.ts', 'pipe-decorator')(format='.') + +:marked + Before we do that, let's understand the difference between *pure* and *impure*, starting with a *pure* pipe. + + ### Pure pipes + +block pure-change + :marked + Angular executes a *pure pipe* only when it detects a *pure change* to the input value. + A ***pure change*** is *either* a change to a primitive input value (`String`, `Number`, `Boolean`, `Symbol`) + *or* a changed object reference (`Date`, `Array`, `Function`, `Object`). + +:marked + Angular ignores changes *within* (composite) objects. + It won't call a pure pipe if we change an input month, add to an input #{_array}, or update an input object property. + + This may seem restrictive but is is also fast. + An object reference check is fast — much faster than a deep check for + differences — so Angular can quickly determine if it can skip both the + pipe execution and a view update. + + For this reason, we prefer a pure pipe if we can live with the change detection strategy. + When we can't, we *may* turn to the impure pipe. + +.l-sub-section + :marked + Or we might not use a pipe at all. + It may be better to pursue the pipe's purpose with a property of the component, + a point we take up later. + +:marked + ### Impure pipes + + Angular executes an *impure pipe* during *every* component change detection cycle. + An impure pipe will be called a lot, as often as every keystroke or mouse-move. + + With that concern in mind, we must implement an impure pipe with great care. + An expensive, long-running pipe could destroy the user experience. + + <a id="impure-flying-heroes"></a> + ### An impure *FlyingHeroesPipe* + + A flip of the switch turns our `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`. + Here's the complete implementation: ++makeTabs( + 'pipes/ts/app/flying-heroes.pipe.ts, pipes/ts/app/flying-heroes.pipe.ts', + 'impure, pure', + 'FlyingHeroesImpurePipe, FlyingHeroesPipe')(format='.') + +:marked + We inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally. + The only difference is the `pure` flag in the pipe metadata. + + This is a good candidate for an impure pipe because the `transform` function is trivial and fast. ++makeExample('pipes/ts/app/flying-heroes.pipe.ts','filter')(format='.') + + We can derive a `FlyingHeroesImpureComponent` that we derive from the `FlyingHeroesComponent`. ++makeExample('pipes/ts/app/flying-heroes.component.ts','impure-component','app/flying-heroes.component.ts (FlyingHeroesImpureComponent)')(format='.') +:marked + The only substantive change is the pipe. + We can confirm in the <live-example></live-example> that the _flying heroes_ + display updates as we enter new heroes even when we mutate the `heroes` #{_array}. + +- var _dollar = _docsFor === 'ts' ? '$' : ''; +h3#async-pipe The impure #[i AsyncPipe] +:marked + The Angular `AsyncPipe` is an interesting example of an impure pipe. + The `AsyncPipe` accepts a `#{_Promise}` or `#{_Observable}` as input + and subscribes to the input automatically, eventually returning the emitted value(s). + + It is also stateful. + The pipe maintains a subscription to the input `#{_Observable}` and + keeps delivering values from that `#{_Observable}` as they arrive. + + In this next example, we bind an `#{_Observable}` of message strings + (`message#{_dollar}`) to a view with the `async` pipe. + ++makeExample('pipes/ts/app/hero-async-message.component.ts', null, 'app/hero-async-message.component.ts') + +:marked + The Async pipe saves boilerplate in the component code. + The component doesn't have to subscribe to the async data source, + it doesn't extract the resolved values and expose them for binding, + and the component doesn't have to unsubscribe when it is destroyed + (a potent source of memory leaks). + + ### An impure caching pipe + + Let's write one more impure pipe, a pipe that makes an HTTP request to the server. + Normally, that's a horrible idea. + It's probably a horrible idea no matter what we do. + We're forging ahead anyway to make a point. + Remember that impure pipes are called every few microseconds. + If we're not careful, this pipe will punish the server with requests. + + We are careful. Our pipe only makes a server call if the request URL has changed. + It caches the request URL and waits for a result which it also caches when it arrives. + The pipe returns the cached result (which is null while a request is in flight) + after every Angular call and only contacts the server as necessary. + + Here's the code, which uses the [Angular http](server-communication.html) facility + to retrieve a `heroes.json` file: + ++makeExample('pipes/ts/app/fetch-json.pipe.ts', null, 'app/fetch-json.pipe.ts') +:marked + Then we demonstrate it in a harness component whose template defines two bindings to this pipe. ++makeExample('pipes/ts/app/hero-list.component.ts', 'template', 'app/hero-list.component.ts (template)') +:marked + Despite the two bindings and what we know to be frequent pipe calls, + the nework tab in the browser developer tools confirms that there is only one request for the file. + + The component renders like this: + +figure.image-display + img(src='/resources/images/devguide/pipes/hero-list.png' alt="Hero List") + +:marked + ### *JsonPipe* + + The second binding involving the `FetchPipe` uses more pipe chaining. + We take the same fetched results displayed in the first binding + and display them again, this time in JSON format by chaining through to the built-in `JsonPipe`. + +.callout.is-helpful + header Debugging with the json pipe + :marked + The [JsonPipe](../api/common/index/JsonPipe-class.html) + provides an easy way to diagnosis a mysteriously failing data binding or + inspect an object for future binding. + +:marked + Here's the complete component implementation: + ++makeExample('pipes/ts/app/hero-list.component.ts', null, 'app/hero-list.component.ts') + +a(id="pure-pipe-pure-fn") +:marked + ### Pure pipes and pure functions + + A pure pipe uses pure functions. + Pure functions process inputs and return values without detectable side-effects. + Given the same input they should always return the same output. + + The pipes we saw earlier in this chapter were implemented with pure functions. + The built-in `DatePipe` is a pure pipe with a pure function implementation. + So is our `ExponentialStrengthPipe`. + So is our `FlyingHeroesPipe`. + A few steps back we reviewed the `FlyingHeroesImpurePipe` — *an impure pipe with a pure function*. + + But a *pure pipe* must always be implemented with a *pure function*. Failure to heed this warning will bring about many a console errors regarding expressions that have changed after they were checked. + +.l-main-section +:marked + ## Next Steps + + Pipes are a great way to encapsulate and share common display-value + transformations. We use them like styles, dropping them + into our templates expressions to enrich the appeal and usability + of our views. + + Explore Angular's inventory of built-in pipes in the [API Reference](../api/#!?apiFilter=pipe). + Try writing a custom pipe and perhaps contributing it to the community. + +a(id="no-filter-pipe") +.l-main-section +:marked + ## No *FilterPipe* or *OrderByPipe* + + Angular does not ship with pipes for filtering or sorting lists. + Developers familiar with Angular 1 know these as `filter` and `orderBy`. + There are no equivalents in Angular 2. + + This is not an oversight. Angular 2 is unlikely to offer such pipes because + (a) they perform poorly and (b) they prevent aggressive minification. + Both `filter` and `orderBy` require parameters that reference object properties. + We learned earlier that such pipes must be [*impure*](#pure-and-impure-pipes) and that + Angular calls impure pipes in almost every change detection cycle. + + Filtering and especially sorting are expensive operations. + The user experience can degrade severely for even moderate sized lists when Angular calls these pipe methods many times per second. + The `filter` and `orderBy` have often been abused in Angular 1 apps, leading to complaints that Angular itself is slow. + That charge is fair in the indirect sense that Angular 1 prepared this performance trap + by offering `filter` and `orderBy` in the first place. + + The minification hazard is also compelling if less obvious. Imagine a sorting pipe applied to a list of heroes. + We might sort the list by hero `name` and `planet` of origin properties something like this: +code-example(language="html") + <!-- NOT REAL CODE! --> + <div *ngFor="let hero of heroes | orderBy:'name,planet'"></div> +:marked + We identify the sort fields by text strings, expecting the pipe to reference a property value by indexing + (e.g., `hero['name']`). + Unfortunately, aggressive minification *munges* the `Hero` property names so that `Hero.name` and `Hero.planet` + becomes something like `Hero.a` and `Hero.b`. Clearly `hero['name']` is not going to work. + + Some of us may not care to minify this aggressively. That's *our* choice. + But the Angular product should not prevent someone else from minifying aggressively. + Therefore, the Angular team decided that everything shipped in Angular will minify safely. + + The Angular team and many experienced Angular developers strongly recommend that you move + filtering and sorting logic into the component itself. + The component can expose a `filteredHeroes` or `sortedHeroes` property and take control + over when and how often to execute the supporting logic. + Any capabilities that you would have put in a pipe and shared across the app can be + written in a filtering/sorting service and injected into the component. + + If these performance and minification considerations do not apply to you, you can always create your own such pipes + (along the lines of the [FlyingHeroesPipe](#impure-flying-heroes)) or find them in the community. diff --git a/public/docs/ts/_cache/guide/security.jade b/public/docs/ts/_cache/guide/security.jade new file mode 100644 index 0000000000..161e34c0e8 --- /dev/null +++ b/public/docs/ts/_cache/guide/security.jade @@ -0,0 +1,263 @@ +block includes + include ../_util-fns +:marked + Web application security has many aspects. This chapter describes Angular's built in + protections against common web application vulnerabilities and attacks, such as Cross Site + Scripting Attacks. It does not cover application level security, such as authentication (_Who is + this user?_) or authorization (_What can this user do?_). + + The [Open Web Application Security Project (OWASP)](https://www.owasp.org/index.php/Category:OWASP_Guide_Project) + has further information on the attacks and mitigations described below. + +.l-main-section +:marked + # Table Of Contents + + * [Reporting Vulnerabilities](#report-issues) + * [Best Practices](#best-practices) + * [Preventing Cross-Site Scripting (XSS)](#xss) + * [Trusting Safe Values](#bypass-security-apis) + * [HTTP-level Vulnerabilities](#http) + * [Auditing Angular Applications](#code-review) + + Try the <live-example></live-example> of the code shown in this chapter. + +.l-main-section +h2#report-issues Reporting Vulnerabilities +:marked + Email us at [security@angular.io](mailto:security@angular.io) to report vulnerabilities in + Angular itself. + + For further details on how Google handles security issues please refer to [Google's security + philosophy](https://www.google.com/about/appsecurity/). + +.l-main-section +h2#best-practices Best Practices +:marked + * **Keep current with the latest Angular library releases.** + We regularly update our Angular libraries and these updates may fix security defects discovered in + previous version. Check the Angular [change + log](https://github.com/angular/angular/blob/master/CHANGELOG.md) for security-related updates. + + * **Don't modify your copy of Angular.** + Private, customized versions of Angular tend to fall behind the current version and may neglect + important security fixes and enhancements. Instead, share your Angular improvements with the + community and make a pull request. + + * **Avoid Angular APIs marked in the documentation as “[_Security Risk_](#bypass-security-apis)”.** + +.l-main-section +h2#xss Preventing Cross-Site Scripting (XSS) +:marked + [Cross-Site Scripting (XSS)](https://en.wikipedia.org/wiki/Cross-site_scripting) enables attackers + to inject malicious code into web pages. Such code can then, for example, steal user's data (in + particular their login data), or perform actions impersonating the user. This is one of the most + common attacks on the web. + + To block XSS attacks, we must prevent malicious code from entering the DOM. For example, if an + attacker can trick us into inserting a `<script>` tag in the DOM, they can run arbitrary code on + our website. The attack is not limited to `<script>` tags - many elements and properties in the + DOM allow code execution, for example `<img onerror="...">`, `<a href="javascript:...">`. If + attacker controlled data enters the DOM, we have to expect security vulnerabilities. + + ### Angular’s Cross-site Scripting Security Model + + To systematically block XSS bugs, Angular treats all values as untrusted by default. When a value + is inserted into the DOM from a template, via property, attribute, style, or class binding, or via + interpolation, Angular will sanitize and escape untrusted values. + + **Angular templates are the same as executable code**: HTML, attributes, and binding expressions + (but not the values bound!) in templates are trusted to be safe. That means applications must + prevent potentially attacker controlled values from ever making it into the source code of a + template. Never generate template source code by concatenating user input and templates! Using + the [offline template compiler](#offline-template-compiler) is an effective way to prevent these + vulnerabilities, also known as template injection. + + ### Sanitization and security contexts + + Sanitization inspects an untrusted value and turns it into a value that is safe to insert into + the DOM. In many cases, values do not get changed by this at all. Sanitization depends on context: + a value that is harmless in CSS is potentially dangerous in a URL. + + Angular defines four security contexts: HTML, style, URL, and resource URL. + + * HTML is used when interpreting a value as HTML, e.g., when binding to `innerHtml` + * Style is used when binding CSS into the `style` property + * URL is used for URL properties such as `<a href>` + * Resource URLs are URLs that will be loaded and executed as code, e.g., in `<script src>` + + Angular sanitizes untrusted values for the first three items; sanitizing resource URLs is not + possible as they contain arbitrary code. In development mode, Angular prints a console warning + when it has to change a value during sanitization. + + ### Sanitization example + + The template below binds the value of `htmlSnippet`, once by interpolating it into an element's + content, and once by binding it to the `innerHTML` property of an element. + ++makeExample('app/inner-html-binding.component.html') + +:marked + Interpolated content is always escaped - the HTML is not interpreted, and the browser displays + angle brackets in the elements text content. + + For the HTML to be interpreted, we must bind to an HTML property, such as `innerHTML`. But binding + a potentially attacker controlled value into `innerHTML` would normally cause an XSS + vulnerability. For example, code contained in a `<script>` tag would be executed. + ++makeExcerpt('app/inner-html-binding.component.ts ()', 'inner-html-controller') + +:marked + Angular recognizes the value as unsafe, and automatically sanitizes it. It removes the `<script>` + tag but keeps safe content, such as the text content of the `<script>` tag, or the `<b>` element. + +figure.image-display + img(src='/resources/images/devguide/security/binding-inner-html.png' + alt='A screenshot showing interpolated and bound HTML values') +:marked + ### Avoid direct use of the DOM APIs + + The built-in browser DOM APIs do not automatically protect you from security vulnerabilities. + For example, `document`, the node available through `ElementRef`, and many third party APIs + contain unsafe methods. Avoid directly interacting with the DOM, and instead use Angular + templates where possible. + + ### Content Security Policy + + A [Content Security Policy (CSP)](http://www.html5rocks.com/en/tutorials/security/content-security-policy/) is a defense-in-depth + technique to prevent XSS. To enable CSP, configure your web server to return an appropriate + `Content-Security-Policy` HTTP header. + + <a id="offline-template-compiler"></a> + ### Use the Offline Template Compiler + + The offline template compiler prevents a whole class of vulnerabilities called template injection, + and also greatly improves application performance. Use the offline template compiler in production + deployments. Do not dynamically generate templates. Angular trusts template code, so generating + templates, in particular containing user data, circumvents Angular's built-in protections. See the + [Dynamic Forms Cookbook](../cookbook/dynamic-form.html) on how to dynamically construct forms in a + safe way. + + ### Server side XSS protection + + HTML constructed on the server is vulnerable to injection attacks. Injecting template code into an + Angular application is the same as injecting executable code into the + application; it gives the attacker full control over the application. To prevent this, make sure + to use a templating language that automatically escapes values to prevent XSS vulnerabilities on + the server. Do not generate Angular templates on the server side using a templating language, this + carries a high risk of introducing template injection vulnerabilities. + +.l-main-section +h2#bypass-security-apis Trusting Safe Values +:marked + Sometimes applications genuinely need to include executable code, display an `<iframe>` from some + URL, or construct potentially dangerous URLs. To prevent automatic sanitization in this situation, + you can tell Angular that you inspected a value, checked how it is generated, and made sure it is + always secure. But **be careful**! If you trust a value that can be malicious, you will likely + introduce a security vulnerability into your application. If in doubt, find a professional + security reviewer. + + You can mark a value as trusted by injecting `DomSanitizationService`, and calling one of the + following methods. + + * `bypassSecurityTrustHtml` + * `bypassSecurityTrustScript` + * `bypassSecurityTrustStyle` + * `bypassSecurityTrustUrl` + * `bypassSecurityTrustResourceUrl` + + Remember, whether a value is safe depends on context, so you need to choose the right context for + your intended use of the value. Imagine the following template needs to bind a URL to a + `javascript:alert(...)` call. + ++makeExcerpt('app/bypass-security.component.html ()', 'dangerous-url') + +:marked + Normally, Angular automatically sanitizes the URL, disables the dangerous code and, + in development mode, logs this action to the console. To prevent + this, we can mark the URL value as a trusted URL using the `bypassSecurityTrustUrl` call: + ++makeExcerpt('app/bypass-security.component.ts ()', 'trust-url') + +figure.image-display + img(src='/resources/images/devguide/security/bypass-security-component.png' + alt='A screenshot showing an alert box created from a trusted URL') + +:marked + If we need to convert user input into a trusted value, it can be convenient to do so in a + controller method. The template below allows users to enter a YouTube video ID, and load the + corresponding video in an `<iframe>`. The `<iframe src>` attribute is a resource URL security + context, because an untrusted source can, e.g., smuggle in file downloads that unsuspecting users + would execute. So we call a method on the controller to construct a trusted video URL, which + Angular then allows binding into `<iframe src>`. + ++makeExcerpt('app/bypass-security.component.html ()', 'iframe-videoid') ++makeExcerpt('app/bypass-security.component.ts ()', 'trust-video-url') + +.l-main-section +h2#http HTTP-level Vulnerabilities +:marked + Angular has built in support to help prevent two common HTTP vulnerabilities, Cross-site Request + Forgery (XSRF) and Cross-site Script Inclusion (XSSI). Both of these must be primarily mitigated + on the server side, but Angular ships helpers to make integration on the client side easier. + +h3#xsrf Cross-site Request Forgery (XSRF) +:marked + In a Cross-site Request Forgery (XSRF or CSRF), an attacker tricks the user into visiting a + _different_ page, and has them, e.g., submit a form that sends a request to your application's + web server. If the user is logged into your application, the browser will send authentication + cookies, and the attacker could — for example — cause a bank transfer in the user's name with + the right request. + + To prevent this, your application must ensure that user requests originate in your own + application, not on a different site. A common technique is that the server sends a randomly + generated authentication token in a cookie, often with the name `XSRF-TOKEN`. Cookies can only + be read by the website on which they are set, so only your own application can read this token. On + each API request, the server then validates the client by checking that the token is sent back, + usually in an HTTP header called `X-XSRF-TOKEN`. + + The Angular `http` client has built-in support for this technique. The default + `CookieXSRFStrategy` looks for a cookie called `XSRF-TOKEN` and sets an HTTP request header named + `X-XSRF-TOKEN` with the value of that cookie on every request. The server must set the + `XSRF-TOKEN` cookie, and validate the response header for each state modifying request. + + XSRF tokens should be unique per user and session, have a large random value generated by a + cryptographically secure random number generator, and expire. + + Angular applications can customize cookie and header names by binding their own + `CookieXSRFStrategy` value, or implement an entirely custom `XSRFStrategy` by providing a custom + binding for that type, by adding either of the following to your providers list: + +code-example(language="typescript"). + { provide: XSRFStrategy, useValue: new CookieXSRFStrategy('myCookieName', 'My-Header-Name')} + { provide: XSRFStrategy, useClass: MyXSRFStrategy} + +:marked + Learn about Cross Site Request Forgery (XSRF) at the Open Web Application Security Project (OWASP) + [here](https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29) and + [here](https://www.owasp.org/index.php/CSRF_Prevention_Cheat_Sheet). This [Stanford University + paper](https://seclab.stanford.edu/websec/csrf/csrf.pdf) is also a rich source of detail. + +h3#xssi Cross-site Script Inclusion (XSSI) +:marked + Cross-site Script Inclusion, also known as JSON vulnerability, can allow an attacker's website to + read data from a JSON API. The attack works on older browser by overriding native JavaScript + object constructors, and then including an API URL using a `<script>` tag. + + This attack is only successful if the returned JSON is executable as JavaScript. Servers can + prevent it by prefixing all JSON responses to make them non-executable, by convention using the + well-known string `")]}',\n"`. + + Angular's `Http` library recognizes this convention and automatically strips the string + `")]}',\n"` from all responses before further parsing. + + Learn more in the XSSI section of this [Google web security blog + post](https://security.googleblog.com/2011/05/website-security-for-webmasters.html) + +.l-main-section +h2#code-review Auditing Angular Applications +:marked + Angular applications should follow the same security principles as regular web applications, and + should be audited as such. Angular specific APIs that should be audited in a security review, + such as the [_bypassSecurityTrust_](#bypass-security-apis) APIs, are marked in the documentation + as security sensitive. diff --git a/public/docs/ts/_cache/guide/server-communication.jade b/public/docs/ts/_cache/guide/server-communication.jade new file mode 100644 index 0000000000..c254ff9377 --- /dev/null +++ b/public/docs/ts/_cache/guide/server-communication.jade @@ -0,0 +1,702 @@ +block includes + include ../_util-fns + - var _Http = 'Http'; // Angular `Http` library name. + - var _Angular_Http = 'Angular <code>Http</code>' + - var _Angular_http_library = 'Angular HTTP library' + +:marked + [HTTP](https://tools.ietf.org/html/rfc2616) is the primary protocol for browser/server communication. +.l-sub-section + :marked + The [`WebSocket`](https://tools.ietf.org/html/rfc6455) protocol is another important communication technology; + we won't cover it in this chapter. +:marked + Modern browsers support two HTTP-based APIs: + [XMLHttpRequest (XHR)](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) and + [JSONP](https://en.wikipedia.org/wiki/JSONP). A few browsers also support + [Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). + + The !{_Angular_http_library} simplifies application programming of the **XHR** and **JSONP** APIs + as we'll learn in this chapter covering: + + - [HTTP client sample overview](#http-client) + - [Fetch data with http.get](#fetch-data) + <li if-docs="ts"> [RxJS Observable of HTTP Responses](#rxjs)</li> + <li if-docs="ts"> [Enabling RxJS Operators](#enable-rxjs-operators)</li> + - [Extract JSON data](#extract-data) + - [Error handling](#error-handling) + - [Send data to the server](#update) + <li if-docs="ts"> [Promises instead of observables](#promises)</li> + - [Cross-origin requests: Wikipedia example](#cors) + <ul if-docs="ts"> + <li> [Set query string parameters](#search-parameters)</li> + <li> [Debounce search term input](#more-observables)</li> + </ul> + - [Appendix: the in-memory web api service](#in-mem-web-api) + + We illustrate these topics with code that you can <live-example>run live</live-example>. + +.l-main-section +:marked + # Demos + + This chapter describes server communication with the help of the following demos + +block demos-list + :marked + - [HTTP client: Tour of Heroes with Observables](#http-client) + - [HTTP client: Tour of Heroes with !{_Promise}s](#promises) + - [JSONP client: Wikipedia to fetch data from a service that does not support CORS](#cors) + - [JSONP client: Wikipedia using observable operators to reduce server calls](#more-observables) + +:marked + These demos are orchestrated by the root `AppComponent` ++makeExample('server-communication/ts/app/app.component.ts', null, 'app/app.component.ts') + ++ifDocsFor('ts') + :marked + There is nothing remarkable here _except_ for the import of RxJS operators. + +makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs')(format='.') + :marked + We'll talk about that [below](#rxjs) when we're ready to explore observables. +:marked + First, we have to configure our application to use server communication facilities. + +.l-main-section#http-providers +:marked + # Providing HTTP Services + + We use the !{_Angular_Http} client to communicate with a server using a familiar HTTP request/response protocol. + The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}. + ++ifDocsFor('ts') + .l-sub-section + :marked + SystemJS knows how to load services from the !{_Angular_http_library} when we import from the `@angular/http` module + because we registered that module name in the `system.config` file. + +:marked + Before we can use the `!{_Http}` client , we'll have to register it as a service provider with the Dependency Injection system. + +.l-sub-section + :marked + Learn about providers in the [Dependency Injection](dependency-injection.html) chapter. + +:marked + In this demo, we register providers in the `bootstrap()` method of + <span ngio-ex>app/main.ts</span>. + ++makeExample('server-communication/ts/app/main.ts', 'v1', 'app/main.ts (v1)')(format='.') + +block http-providers + :marked + We begin by importing the symbols we need, most of them familiar by now. The newcomer is `HTTP_PROVIDERS`, + a collection of service providers from the !{_Angular_http_library}. + + We register HTTP providers in the bootstrap method by passing them in an array as the second parameter after the root component. + + ### Why register in *bootstrap*? + + We prefer to register application-wide providers in the metadata `providers` array + of the root `AppComponent` like this: + +makeExample('server-communication/ts/app/app.component.ts','http-providers')(format='.') + :marked + Here we register the providers in the `bootstrap` method in the `main.ts` file. Why? + + This is a *sample application* that doesn't talk to a real server. + We're going to reconfigure the (typically-hidden) `XhrBackend` service with a fake provider + that fetches and saves sample data from an in-memory data store. + This replacement service is called the [*in-memory web api*](#in-mem-web-api). + + Such sleight-of-hand is something the root application component should *not* know about. + For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`. + +.l-main-section#http-client +:marked + # The Tour of Heroes _HTTP_ Client Demo + + Our first demo is a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application. + This version gets some heroes from the server, displays them in a list, lets us add new heroes, and saves them to the server. + We use the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`. + + It works like this. +figure.image-display + img(src='/resources/images/devguide/server-communication/http-toh.gif' alt="ToH mini app" width="250") +:marked + This demo has a single component, the `HeroListComponent`. Here's its template: ++makeExample('server-communication/ts/app/toh/hero-list.component.html', null, 'app/toh/hero-list.component.html (template)') +:marked + It presents the list of heroes with an `ngFor`. + Below the list is an input box and an *Add Hero* button where we can enter the names of new heroes + and add them to the database. + We use a [template reference variable](template-syntax.html#ref-vars), `newHeroName`, to access the + value of the input box in the `(click)` event binding. + When the user clicks the button, we pass that value to the component's `addHero` method and then + clear it to make it ready for a new hero name. + + Below the button is an area for an error message. + +a#oninit +a#HeroListComponent +:marked + ## The *HeroListComponent* class + Here's the component class: ++makeExample('server-communication/ts/app/toh/hero-list.component.ts','component', 'app/toh/hero-list.component.ts (class)') +:marked + Angular [injects](dependency-injection.html) a `HeroService` into the constructor + and the component calls that service to fetch and save data. + + The component **does not talk directly to the !{_Angular_Http} client**! + The component doesn't know or care how we get the data. + It delegates to the `HeroService`. + + This is a golden rule: **always delegate data access to a supporting service class**. + + Although _at runtime_ the component requests heroes immediately after creation, + we do **not** call the service's `get` method in the component's constructor. + We call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html) instead + and count on Angular to call `ngOnInit` when it instantiates this component. +.l-sub-section + :marked + This is a *best practice*. + Components are easier to test and debug when their constructors are simple and all real work + (especially calling a remote server) is handled in a separate method. +block getheroes-and-addhero + :marked + The service's `getHeroes()` and `addHero()` methods return an `Observable` of hero data that the !{_Angular_Http} client fetched from the server. + + *Observables* are a big topic, beyond the scope of this chapter. + But we need to know a little about them to appreciate what is going on here. + + We should think of an `Observable` as a stream of events published by some source. + We listen for events in this stream by ***subscribing*** to the `Observable`. + In these subscriptions we specify the actions to take when the web request + produces a success event (with the hero data in the event payload) or a fail event (with the error in the payload). + +:marked + With our basic intuitions about the component squared away, we're ready to look inside the `HeroService`. + +a#HeroService +.l-main-section#fetch-data +:marked + ## Fetch data with the **HeroService** + + In many of our previous samples we faked the interaction with the server by + returning mock heroes in a service like this one: ++makeExample('toh-4/ts/app/hero.service.ts', 'just-get-heroes')(format=".") +:marked + In this chapter, we revise that `HeroService` to get the heroes from the server using the !{_Angular_Http} client service: ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'v1', 'app/toh/hero.service.ts (revised)') + +:marked + Notice that the !{_Angular_Http} client service is + [injected](dependency-injection.html) into the `HeroService` constructor. ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'ctor') +:marked + Look closely at how we call `!{_priv}http.get` ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'http-get', 'app/toh/hero.service.ts (getHeroes)')(format=".") +:marked + We pass the resource URL to `get` and it calls the server which should return heroes. +.l-sub-section + :marked + It *will* return heroes once we've set up the [in-memory web api](#in-mem-web-api) + described in the appendix below. + Alternatively, we can (temporarily) target a JSON file by changing the endpoint URL: + +makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".") + ++ifDocsFor('ts') + :marked + <a id="rxjs"></a> + The return value may surprise us. + Many of us who are familiar with asynchronous methods in modern JavaScript would expect the `get` method to return a + [promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise). + We'd expect to chain a call to `then()` and extract the heroes. + Instead we're calling a `map()` method. + Clearly this is not a promise. + + In fact, the `http.get` method returns an **Observable** of HTTP Responses (`Observable<Response>`) from the RxJS library + and `map` is one of the RxJS *operators*. + + .l-main-section + :marked + # RxJS Library + [RxJS](https://github.com/ReactiveX/RxJS) ("Reactive Extensions") is a 3rd party library, endorsed by Angular, + that implements the [*asynchronous observable*](https://www.youtube.com/watch?v=UHI0AzD_WfY "Rob Wormald on observables") pattern. + + All of our Developer Guide samples have installed the RxJS npm package and loaded via `system.js` + because observables are used widely in Angular applications. + We certainly need it now when working with the HTTP client. + And we must take a critical extra step to make RxJS observables usable. + + ### Enable RxJS Operators + The RxJS library is quite large. + Size matters when we build a production application and deploy it to mobile devices. + We should include only those features that we actually need. + + Accordingly, Angular exposes a stripped down version of `Observable` in the `rxjs/Observable` module, + a version that lacks most of the operators including some we'd like to use here + such as the `map` method we called above in `getHeroes`. + + It's up to us to add the operators we need. + + We could add _every_ RxJS operators with a single import statement. + While that is the easiest thing to do, we'd pay a penalty in extended launch time and application size + because the full library is so big. We only use a few operators in our app. + + Instead, we'll import each `Observable` operator and static class method, one-by-one, until we have a custom *Observable* implementation tuned + precisely to our requirements. We'll put the `import` statements in one `app/rxjs-operators.ts` file. + +makeExample('server-communication/ts/app/rxjs-operators.ts', null, 'app/rxjs-operators.ts')(format=".") + :marked + If we forget an operator, the TypeScript compiler will warn that it's missing and we'll update this file. + .l-sub-section + :marked + We don't need _all_ of these particular operators in the `HeroService` — just `map`, `catch` and `throw`. + We'll need the other operators later, in a *Wiki* example [below](#more-observables). + :marked + Finally, we import `rxjs-operator`_itself_ in our `app.component.ts`: + +makeExample('server-communication/ts/app/app.component.ts', 'import-rxjs', 'app/app.component.ts (import rxjs)')(format=".") + :marked + Let's return to our study of the `HeroService`. + +l-main-section +a#extract-data +:marked + ## Process the response object + Remember that our `getHeroes()` method mapped the `!{_priv}http.get` response object to heroes with an `!{_priv}extractData` helper method: ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'extract-data', 'app/toh/hero.service.ts (excerpt)')(format=".") +:marked + The `response` object does not hold our data in a form we can use directly. + To make it useful in our application we must parse the response data into a JSON object + + #### Parse to JSON +block parse-json + :marked + The response data are in JSON string form. + We must parse that string into JavaScript objects which we do by calling `response.json()`. + + .l-sub-section + :marked + This is not Angular's own design. + The Angular HTTP client follows the ES2015 specification for the + [response object](https://fetch.spec.whatwg.org/#response-class) returned by the `Fetch` function. + That spec defines a `json()` method that parses the response body into a JavaScript object. + +.l-sub-section + :marked + We shouldn't expect the decoded JSON to be the heroes !{_array} directly. + The server we're calling always wraps JSON results in an object with a `data` + property. We have to unwrap it to get the heroes. + This is conventional web api behavior, driven by + [security concerns](https://www.owasp.org/index.php/OWASP_AJAX_Security_Guidelines#Always_return_JSON_with_an_Object_on_the_outside). +.alert.is-important + :marked + Make no assumptions about the server API. + Not all servers return an object with a `data` property. +:marked + ### Do not return the response object + Our `getHeroes()` could have returned the HTTP response. Bad idea! + The point of a data service is to hide the server interaction details from consumers. + The component that calls the `HeroService` wants heroes. + It has no interest in what we do to get them. + It doesn't care where they come from. + And it certainly doesn't want to deal with a response object. + ++ifDocsFor('ts') + .callout.is-important + header HTTP GET is delayed + :marked + The `!{_priv}http.get` does **not send the request just yet!** This observable is + [*cold*](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/creating.md#cold-vs-hot-observables) + which means the request won't go out until something *subscribes* to the observable. + That *something* is the [HeroListComponent](#subscribe). + +a#error-handling +:marked + ### Always handle errors + + Whenever we deal with I/O we must be prepared for something to go wrong as it surely will. + We should catch errors in the `HeroService` and do something with them. + We may also pass an error message back to the component for presentation to the user + but only if we can say something the user can understand and act upon. + + In this simple app we provide rudimentary error handling in both the service and the component. +block error-handling + :marked + The eagle-eyed reader may have spotted our use of the `catch` operator in conjunction with a `handleError` method. + We haven't discussed so far how that actually works. + + We use the Observable `catch` operator on the service level. + It takes an error handling function with an error object as the argument. + Our service handler, `handleError`, logs the response to the console, + transforms the error into a user-friendly message, and returns the message in a new, failed observable via `Observable.throw`. + ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'error-handling', 'app/toh/hero.service.ts (excerpt)')(format=".") + +a#subscribe +a#hero-list-component +h4 #[b HeroListComponent] error handling +block hlc-error-handling + :marked + Back in the `HeroListComponent`, where we called `!{_priv}heroService.getHeroes()`, + we supply the `subscribe` function with a second function parameter to handle the error message. + It sets an `errorMessage` variable which we've bound conditionally in the `HeroListComponent` template. + ++makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'getHeroes', 'app/toh/hero-list.component.ts (getHeroes)')(format=".") + +.l-sub-section + :marked + Want to see it fail? Reset the api endpoint in the `HeroService` to a bad value. Remember to restore it! + + +<a id="update"></a> +<a id="post"></a> +.l-main-section +:marked + ## Send data to the server + + So far we've seen how to retrieve data from a remote location using an HTTP service. + Let's add the ability to create new heroes and save them in the backend. + + We'll create an easy method for the `HeroListComponent` to call, an `addHero()` method that takes + just the name of a new hero: + ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'addhero-sig')(format=".") + +:marked + To implement it, we need to know some details about the server's api for creating heroes. + + [Our data server](#server) follows typical REST guidelines. + It expects a [`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) request + at the same endpoint where we `GET` heroes. + It expects the new hero data to arrive in the body of the request, + structured like a `Hero` entity but without the `id` property. + The body of the request should look like this: + +code-example(format="." language="javascript"). + { "name": "Windstorm" } +:marked + The server will generate the `id` and return the entire `JSON` representation + of the new hero including its generated id. The hero arrives tucked inside a response object + with its own `data` property. + + Now that we know how the API works, we implement `addHero()`like this: + ++ifDocsFor('ts') + +makeExample('server-communication/ts/app/toh/hero.service.ts', 'import-request-options', 'app/toh/hero.service.ts (additional imports)')(format=".") ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'addhero', 'app/toh/hero.service.ts (addHero)')(format=".") + +:marked + ### Headers + + The `Content-Type` header allows us to inform the server that the body will represent JSON. + ++ifDocsFor('ts') + :marked + [Headers](../api/http/index/Headers-class.html) are one of the [RequestOptions](../api/http/index/RequestOptions-class.html). + Compose the options object and pass it in as the *third* parameter of the `post` method, as shown above. + +:marked + ### Body + + Despite the content type being specified as JSON, the POST body must actually be a *string*. + Hence, we explicitly encode the JSON hero content before passing it in as the body argument. + ++ifDocsFor('ts') + .l-sub-section + :marked + We may be able to skip the `JSON.stringify` step in the near future. + +:marked + ### JSON results + + As with `getHeroes()`, we [extract the data](#extract-data) from the response using the + `!{_priv}extractData()` helper. + +block hero-list-comp-add-hero + :marked + Back in the `HeroListComponent`, we see that *its* `addHero()` method subscribes to the observable returned by the *service's* `addHero()` method. + When the data, arrive it pushes the new hero object into its `heroes` array for presentation to the user. ++makeExample('server-communication/ts/app/toh/hero-list.component.ts', 'addHero', 'app/toh/hero-list.component.ts (addHero)')(format=".") + ++ifDocsFor('ts') + h2#promises Fall back to Promises + :marked + Although the Angular `http` client API returns an `Observable<Response>` we can turn it into a + [Promise<Response>](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) if we prefer. + It's easy to do and a promise-based version looks much like the observable-based version in simple cases. + .l-sub-section + :marked + While promises may be more familiar, observables have many advantages. + Don't rush to promises until you give observables a chance. + :marked + Let's rewrite the `HeroService` using promises , highlighting just the parts that are different. + +makeTabs( + 'server-communication/ts/app/toh/hero.service.promise.ts,server-communication/ts/app/toh/hero.service.ts', + 'methods, methods', + 'app/toh/hero.service.promise.ts (promise-based), app/toh/hero.service.ts (observable-based)') + :marked + Converting from an observable to a promise is as simple as calling `toPromise(success, fail)`. + + We move the observable's `map` callback to the first *success* parameter and its `catch` callback to the second *fail* parameter + and we're done! + Or we can follow the promise `then.catch` pattern as we do in the second `addHero` example. + + Our `errorHandler` forwards an error message as a failed promise instead of a failed Observable. + + The diagnostic *log to console* is just one more `then` in the promise chain. + + We have to adjust the calling component to expect a `Promise` instead of an `Observable`. + + +makeTabs( + 'server-communication/ts/app/toh/hero-list.component.promise.ts, server-communication/ts/app/toh/hero-list.component.ts', + 'methods, methods', + 'app/toh/hero-list.component.promise.ts (promise-based), app/toh/hero-list.component.ts (observable-based)') + :marked + The only obvious difference is that we call `then` on the returned promise instead of `subscribe`. + We give both methods the same functional arguments. + .l-sub-section + :marked + The less obvious but critical difference is that these two methods return very different results! + + The promise-based `then` returns another promise. We can keep chaining more `then` and `catch` calls, getting a new promise each time. + + The `subscribe` method returns a `Subscription`. A `Subscription` is not another `Observable`. + It's the end of the line for observables. We can't call `map` on it or call `subscribe` again. + The `Subscription` object has a different purpose, signified by its primary method, `unsubscribe`. + + Learn more about observables to understand the implications and consequences of subscriptions. + +h2#cors Cross-origin requests: Wikipedia example +:marked + We just learned how to make `XMLHttpRequests` using the !{_Angular_Http} service. + This is the most common approach for server communication. + It doesn't work in all scenarios. + + For security reasons, web browsers block `XHR` calls to a remote server whose origin is different from the origin of the web page. + The *origin* is the combination of URI scheme, hostname and port number. + This is called the [Same-origin Policy](https://en.wikipedia.org/wiki/Same-origin_policy). + +.l-sub-section + :marked + Modern browsers do allow `XHR` requests to servers from a different origin if the server supports the + [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol. + If the server requires user credentials, we'll enable them in the [request headers](#headers). + +:marked + Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP). + Wikipedia is one such server. +.l-sub-section + :marked + This [StackOverflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP. +:marked + ### Search wikipedia + + Let's build a simple search that shows suggestions from wikipedia as we type in a text box. + +figure.image-display + img(src='/resources/images/devguide/server-communication/wiki-1.gif' alt="Wikipedia search app (v.1)" width="250") + +block wikipedia-jsonp+ + :marked + Wikipedia offers a modern `CORS` API and a legacy `JSONP` search API. Let's use the latter for this example. + The Angular `Jsonp` service both extends the `!{_Http}` service for JSONP and restricts us to `GET` requests. + All other HTTP methods throw an error because JSONP is a read-only facility. + + As always, we wrap our interaction with an Angular data access client service inside a dedicated service, here called `WikipediaService`. + + +makeExample('server-communication/ts/app/wiki/wikipedia.service.ts',null,'app/wiki/wikipedia.service.ts') + :marked + The constructor expects Angular to inject its `jsonp` service. + We register that service with `JSONP_PROVIDERS` in the [component below](#wikicomponent) that calls our `WikipediaService`. + + <a id="query-parameters"></a> + :marked + ### Search parameters + The [Wikipedia 'opensearch' API](https://www.mediawiki.org/wiki/API:Opensearch) + expects four parameters (key/value pairs) to arrive in the request URL's query string. + The keys are `search`, `action`, `format`, and `callback`. + The value of the `search` key is the user-supplied search term to find in Wikipedia. + The other three are the fixed values "opensearch", "json", and "JSONP_CALLBACK" respectively. + .l-sub-section + :marked + The `JSONP` technique requires that we pass a callback function name to the server in the query string: `callback=JSONP_CALLBACK`. + The server uses that name to build a JavaScript wrapper function in its response which Angular ultimately calls to extract the data. + All of this happens under the hood. + :marked + If we're looking for articles with the word "Angular", we could construct the query string by hand and call `jsonp` like this: + +makeExample('server-communication/ts/app/wiki/wikipedia.service.1.ts','query-string')(format='.') + :marked + In more parameterized examples we might prefer to build the query string with the Angular `URLSearchParams` helper as shown here: + +makeExample('server-communication/ts/app/wiki/wikipedia.service.ts','search-parameters','app/wiki/wikipedia.service.ts (search parameters)')(format=".") + :marked + This time we call `jsonp` with *two* arguments: the `wikiUrl` and an options object whose `search` property is the `params` object. + +makeExample('server-communication/ts/app/wiki/wikipedia.service.ts','call-jsonp','app/wiki/wikipedia.service.ts (call jsonp)')(format=".") + :marked + `Jsonp` flattens the `params` object into the same query string we saw earlier before putting the request on the wire. + + <a id="wikicomponent"></a> + :marked + ### The WikiComponent + + Now that we have a service that can query the Wikipedia API, + we turn to the component that takes user input and displays search results. + + +makeExample('server-communication/ts/app/wiki/wiki.component.ts', null, 'app/wiki/wiki.component.ts') + :marked + The `providers` array in the component metadata specifies the Angular `JSONP_PROVIDERS` collection that supports the `Jsonp` service. + We register that collection at the component level to make `Jsonp` injectable in the `WikipediaService`. + + The component presents an `<input>` element *search box* to gather search terms from the user. + and calls a `search(term)` method after each `keyup` event. + + The `search(term)` method delegates to our `WikipediaService` which returns an observable array of string results (`Observable<string[]>`). + Instead of subscribing to the observable inside the component as we did in the `HeroListComponent`, + we forward the observable result to the template (via `items`) where the [async pipe](pipes.html#async-pipe) + in the `ngFor` handles the subscription. + .l-sub-section + :marked + We often use the [async pipe](pipes.html#async-pipe) in read-only components where the component has no need to interact with the data. + We couldn't use the pipe in the `HeroListComponent` because the "add hero" feature pushes newly created heroes into the list. + + :marked + ## Our wasteful app + + Our wikipedia search makes too many calls to the server. + It is inefficient and potentially expensive on mobile devices with limited data plans. + + ### 1. Wait for the user to stop typing + At the moment we call the server after every key stroke. + The app should only make requests when the user *stops typing* . + Here's how it *should* work — and *will* work — when we're done refactoring: + figure.image-display + img(src='/resources/images/devguide/server-communication/wiki-2.gif' alt="Wikipedia search app (v.2)" width="250") + :marked + ### 2. Search when the search term changes + + Suppose the user enters the word *angular* in the search box and pauses for a while. + The application issues a search request for *Angular*. + + Then the user backspaces over the last three letters, *lar*, and immediately re-types *lar* before pausing once more. + The search term is still "angular". The app shouldn't make another request. + + ### 3. Cope with out-of-order responses + + The user enters *angular*, pauses, clears the search box, and enters *http*. + The application issues two search requests, one for *angular* and one for *http*. + + Which response will arrive first? We can't be sure. + A load balancer could dispatch the requests to two different servers with different response times. + The results from the first *angular* request might arrive after the later *http* results. + The user will be confused if we display the *angular* results to the *http* query. + + When there are multiple requests in-flight, the app should present the responses + in the original request order. That won't happen if *angular* results arrive last. + + <a id="more-observables"></a> + ## More fun with Observables + We can address these problems and improve our app with the help of some nifty observable operators. + + We could make our changes to the `WikipediaService`. + But we sense that our concerns are driven by the user experience so we update the component class instead. + + +makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', null, 'app/wiki/wiki-smart.component.ts') + :marked + We made no changes to the template or metadata, confining them all to the component class. + Let's review those changes. + + ### Create a stream of search terms + + We're binding to the search box `keyup` event and calling the component's `search` method after each keystroke. + + We turn these events into an observable stream of search terms using a `Subject` + which we import from the RxJS observable library: + +makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'import-subject') + :marked + Each search term is a string, so we create a new `Subject` of type `string` called `searchTermStream`. + After every keystroke, the `search` method adds the search box value to that stream + via the subject's `next` method. + +makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'subject')(format='.') + :marked + ### Listen for search terms + + Earlier, we passed each search term directly to the service and bound the template to the service results. + Now we listen to the *stream of terms*, manipulating the stream before it reaches the `WikipediaService`. + +makeExample('server-communication/ts/app/wiki/wiki-smart.component.ts', 'observable-operators')(format='.') + :marked + We wait for the user to stop typing for at least 300 milliseconds + ([debounceTime](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/debounce.md)). + Only changed search values make it through to the service + ([distinctUntilChanged](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/distinctuntilchanged.md)). + + The `WikipediaService` returns a separate observable of string arrays (`Observable<string[]>`) for each request. + We could have multiple requests *in flight*, all awaiting the server's reply, + which means multiple *observables-of-strings* could arrive at any moment in any order. + + The [switchMap](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md) + (formerly known as `flatMapLatest`) returns a new observable that combines these `WikipediaService` observables, + re-arranges them in their original request order, + and delivers to subscribers only the most recent search results. + + The displayed list of search results stays in sync with the user's sequence of search terms. + .l-sub-section + :marked + We added the `debounceTime`, `distinctUntilChanged`, and `switchMap` operators to the RxJS `Observable` class + in `rxjs-operators` as [described above](#rxjs) + +a#in-mem-web-api +.l-main-section +:marked + ## Appendix: Tour of Heroes in-memory server + + If we only cared to retrieve data, we could tell Angular to get the heroes from a `heroes.json` file like this one: ++makeJson('server-communication/ts/app/heroes.json', null, 'app/heroes.json')(format=".") +.l-sub-section + :marked + We wrap the heroes array in an object with a `data` property for the same reason that a data server does: + to mitigate the [security risk](http://stackoverflow.com/questions/3503102/what-are-top-level-json-arrays-and-why-are-they-a-security-risk) + posed by top-level JSON arrays. +:marked + We'd set the endpoint to the JSON file like this: ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint-json')(format=".") + +- var _a_ca_class_with = _docsFor === 'ts' ? 'a custom application class with' : '' +:marked + The *get heroes* scenario would work. + But we want to *save* data too. We can't save changes to a JSON file. We need a web API server. + We didn't want the hassle of setting up and maintaining a real server for this chapter. + So we turned to an *in-memory web API simulator* instead. + +.l-sub-section + :marked + The in-memory web api is not part of the Angular core. + It's an optional service in its own `angular2-in-memory-web-api` library + that we installed with npm (see `package.json`) and + registered for module loading by SystemJS (see `systemjs.config.js`) + +:marked + The in-memory web API gets its data from !{_a_ca_class_with} a `createDb()` + method that returns a map whose keys are collection names and whose values + are !{_array}s of objects in those collections. + + Here's the class we created for this sample based on the JSON data: ++makeExample('server-communication/ts/app/hero-data.ts', null, 'app/hero-data.ts')(format=".") +:marked + Ensure that the `HeroService` endpoint refers to the web API: ++makeExample('server-communication/ts/app/toh/hero.service.ts', 'endpoint')(format=".") +:marked + Finally, we need to redirect client HTTP requests to the in-memory web API. +block redirect-to-web-api + :marked + This redirection is easy to configure because Angular's `http` service delegates the client/server communication tasks + to a helper service called the `XHRBackend`. + + To enable our server simulation, we replace the default `XHRBackend` service with + the in-memory web API service using standard Angular provider registration techniques. + We initialize the in-memory web API with *seed data* from the mock hero dataset at the same time. +:marked + Here is the revised (and final) version of <span ngio-ex>app/main.ts></span> demonstrating these steps. + ++makeExcerpt('app/main.ts', 'final') + +:marked + See the full source code in the <live-example></live-example>. diff --git a/public/docs/ts/_cache/guide/structural-directives.jade b/public/docs/ts/_cache/guide/structural-directives.jade new file mode 100644 index 0000000000..cd832d83d1 --- /dev/null +++ b/public/docs/ts/_cache/guide/structural-directives.jade @@ -0,0 +1,336 @@ +block includes + include ../_util-fns + +:marked + One of the defining features of a single page application is its manipulation + of the DOM tree. Instead of serving a whole new page every time a user + navigates, whole sections of the DOM appear and disappear according + to the application state. In this chapter we'll to look at how Angular + manipulates the DOM and how we can do it ourselves in our own directives. + + In this chapter we will + - [learn what structural directives are](#definition) + - [study *ngIf*](#ngIf) + - [discover the <template> element](#template) + - [understand the asterisk (\*) in **ngFor*](#asterisk) + - [write our own structural directive](#unless) + + Try the <live-example></live-example>. + +<a id="definition"></a> +.l-main-section +:marked + ## What are structural directives? + + There are three kinds of Angular directives: + 1. Components + 1. Attribute directives + 1. Structural directives + + The *Component* is really a directive with a template. + It's the most common of the three directives and we write lots of them as we build our application. + + The [*Attribute* directive](attribute-directives.html) changes the appearance or behavior of an element. + The built-in [NgStyle](template-syntax.html#ngStyle) directive, for example, + can change several element styles at the same time. + We can use it to render text bold, italic, and lime green by binding to a + component property that requests such a sickening result. + + A *Structural* directive changes the DOM layout by adding and removing DOM elements. + We've seen three of the built-in structural directives in other chapters: [ngIf](template-syntax.html#ngIf), + [ngSwitch](template-syntax.html#ngSwitch) and [ngFor](template-syntax.html#ngFor). + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'structural-directives')(format=".") + + +<a id="ngIf"></a> +.l-main-section +:marked + ## NgIf Case Study + + Let’s focus on `ngIf`. It's a great example of a structural + directive: it takes a boolean and makes an entire chunk of DOM appear + or disappear. + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf')(format=".") + +:marked + The `ngIf` directive does not hide the element. + Using browser developer tools we can see that, when the condition is true, the top + paragraph is in the DOM and the bottom disused paragraph is completely + absent from the DOM! In its place are empty `<script>` tags. + +figure.image-display + img(src='/resources/images/devguide/structural-directives/element-not-in-dom.png' alt="element not in dom") + +:marked + ### Why *remove* rather than *hide*? + We could hide the unwanted paragraph by setting its css `display` style to `none`. + The element would remain in the DOM while invisible. Instead we removed it with `ngIf`. + + The difference matters. When we hide an element, + the component's behavior continues. + It remains attached to its DOM element. It continues to listen to events. + Angular keeps checking for changes that could affect data bindings. + Whatever the component was doing it keeps doing. + + Although invisible, the component — and all of its descendent components — + tie up resources that might be more useful elsewhere. + The performance and memory burden can be substantial and the user may not benefit at all. + + On the positive side, showing the element again is very quick. + The component's previous state is preserved and ready to display. + The component doesn't re-initialize — an operation that could be expensive. + + `ngIf` is different. + Setting `ngIf` to false **does** affect the component's resource consumption. + Angular removes the element from DOM, stops change detection for the associated component, + detaches it from DOM events (the attachments that it made) and destroys the component. + The component can be garbage-collected (we hope) and free up memory. + + Components often have child components which themselves have children. + All of them are destroyed when `ngIf` destroys the common ancestor. + This cleanup effort is usually a good thing. + + Of course it isn't *always* a good thing. + It might be a bad thing if we need that particular component again soon. + + The component's state might be expensive to re-construct. + When `ngIf` becomes `true` again, Angular recreates the component and its subtree. + Angular runs every component's initialization logic again. That could be expensive ... as when + a component re-fetches data that had been in memory just moments ago. +.l-sub-section + :marked + *Design thought*: minimize initialization effort and consider caching state in a + companion service. +:marked + Although there are pros and cons to each approach, + in general it is best to use `ngIf` to remove unwanted components rather than + hide them. + + **These same considerations apply to every structural directive, whether built-in or custom.** + We should ask ourselves — and the users of our directives — to think carefully + about the consequences of adding and removing elements and of creating and destroying components. + + Let's see these dynamics at work. For fun, we'll stack the deck *against* + our recommendation and consider a component called `heavy-loader` that + ***pretends*** to load a ton of data when initialized. + + We'll display two instances of the component. We toggle the visibility of the first one with CSS. + We toggle the second into and out of the DOM with `ngIf`. + ++makeTabs( + `structural-directives/ts/app/structural-directives.component.html, + structural-directives/ts/app/heavy-loader.component.ts`, + 'message-log,', + 'template (excerpt), heavy-loader.component.ts') + +:marked + We also log when a component is created or destroyed + using the built-in `ngOnInit` and `ngOnDestroy` [lifecycle hooks](lifecycle-hooks.html). + Here it is in action: + +figure.image-display + img(src='/resources/images/devguide/structural-directives/heavy-loader-toggle.gif' alt="heavy loader toggle") + +:marked + Both components are in the DOM at the start. + First we toggle the component's visibility repeatedly. The component never leaves the DOM. + When visible it's always the same instance and the log is quiet. + + Then we toggle the second component with `ngIf`. + We create a new instance every time and the log shows that we're paying + a heavy price to create and destroy it. + + If we really expected to "wink" the component like this, toggling visibility would be the better choice. + In most UIs, when we "close" a component we're unlikely see it again for a long time, if ever. + The `ngIf` would be preferred in that case. + +<a id="template"></a> +.l-main-section +:marked + ## The *<template>* tag + + Structural directives, like `ngIf`, do their magic by using the + [HTML 5 template tag](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template). + + Outside of an Angular app, the `<template>` tag's default CSS `display` property is `none`. + It's contents are ***invisible*** within + a hidden [document fragment](https://developer.mozilla.org/en/docs/Web/API/DocumentFragment). + + Inside of an app, Angular ***removes*** the`<template>` tags and their children. + The contents are gone — but not forgotten as we'll see soon. + + We can confirm these effects by wrapping the middle "hip" of the phrase "Hip! Hip! Hooray!" within a `<template>` tag. ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'template-tag')(format=".") +:marked + The display is a 'Hip! Hooray!', short of perfect enthusiasm. The DOM effects are different when Angular is in control. +figure.image-display + img(src='/resources/images/devguide/structural-directives/template-in-out-of-a2.png' alt="template outside angular") + +:marked + Evidently Angular replaces the `<template>` tag and its contents with empty `<script>` tags. + That's just its default behavior. + It can do something different as we saw when applying a variety of `ngSwitch` directives to `<template>` tags: + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngSwitch')(format=".") +:marked + When one of those `ngSwitch` conditions is true, Angular inserts the template's content into the DOM. + + What does this have to do with `ngIf` and `ngFor`? We didn't use a `<template>` tag with those directives. + +<a id="asterisk"></a> +.l-main-section +:marked + ## The asterisk (\*) effect + Here are those directives again. See the difference? + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'asterisk')(format=".") +:marked + We're prefixing these directive names with an asterisk (\*). + + The asterisk is "syntactic sugar". It simplifies `ngIf` and `ngFor` for both the writer and the reader. + Under the hood, Angular replaces the asterisk version with a more verbose `<template>` form. + + The next two `ngIf` examples are effectively the same and we may write in either style: + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngIf-template')(format=".") + +:marked + Most of us would rather write in style (A). + + It's worth knowing that Angular expands style (A) into style (B). + It moves the paragraph and its contents inside a `<template>` tag. + It moves the directive up to the `<template>` tag where it becomes a property binding, + surrounded in square brackets. The boolean value of the host component's `condition` property + determines whether the templated content is displayed or not. + + Angular transforms `*ngFor` in a similar manner: + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'ngFor-template')(format=".") +:marked + The basic pattern is the same:  create a `<template>`, relocate the content, + and move the directive onto the `<template>`. + + There are extra nuances stemming from + Angular's [ngFor micro-syntax](template-syntax.html#ngForMicrosyntax) which expands + into an additional `ngForOf` property binding (the iterable) and + the `hero` template input variable (the current item in each iteration). + +<a id="unless"></a> +.l-main-section +:marked + ## Make a structural directive + Let's write our own structural directive, an `Unless` directive, the not-so-evil twin of `ngIf`. + + Unlike `ngIf` which displays the template content when `true`, + our directive displays the content when the condition is ***false***. + +block unless-intro + :marked + Creating a directive is similar to creating a component. + * import the `Directive` decorator. + + * add a CSS **attribute selector** (in brackets) that identifies our directive. + + * specify the name of the public `input` property for binding + (typically the name of the directive itself). + + * apply the decorator to our implementation class. + + Here is how we begin: + ++makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-declaration', 'unless.directive.ts (excerpt)')(format=".") +.l-sub-section + :marked + ### Selector brackets [ ] + The CSS syntax for selecting an attribute is a name in square brackets. + We surround our directive name in square brackets. See *Directive configuration* on the + [cheatsheet](cheatsheet.html). + + ### Selector name prefixes + + We recommend picking a selector name with a prefix to ensure + that it cannot conflict with any standard HTML attribute, now or in the future. + + We do **not** prefix our `unless` directive name with **`ng`**. + That prefix belongs to Angular and + we don't want to confuse our directives with their directives. + + Our prefix is `my`. +:marked + We'll need access to the template *and* something that can render its contents. + We access the template with a `TemplateRef`. The renderer is a `ViewContainerRef`. + We inject both into our constructor as private variables. + ++makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-constructor')(format=".") + +:marked + The consumer of our directive will bind a boolean value to our directive's `myUnless` input property. + The directive adds or removes the template based on that value. + + Let's add the `myUnless` property now as a setter-only property. + ++makeExample('structural-directives/ts/app/unless.directive.ts', 'unless-set')(format=".") +.l-sub-section + :marked + The `@Input()` annotation marks this property as an input for the directive. +:marked + Nothing fancy here: if the condition is false, + we render the template, otherwise we clear the element content. + + The end result should look like this: + ++makeExample('structural-directives/ts/app/unless.directive.ts', null, 'unless.directive.ts') + +:marked + Now we add it to the `directives`array of the host component and try it. + First we add some test HTML to the template: + ++makeExample('structural-directives/ts/app/structural-directives.component.html', 'myUnless')(format=".") +:marked + We run it and it behaves as expected, doing the opposite of `ngIf`. + When `condition` is `true`, the top paragraph is removed (replaced by `<script>` tags) and the bottom paragraph appears. +figure.image-display + img(src='/resources/images/devguide/structural-directives/myUnless-is-true.png' alt="myUnless is true" ) + +:marked + Our `myUnless` directive is dead simple. Surely we left something out. + Surely `ngIf` is more complex? + + [Look at the source code](https://github.com/angular/angular/blob/master/modules/%40angular/common/src/directives/ng_if.ts). + It's well documented and we shouldn't be shy + about consulting the source when we want to know how something works. + + `ngIf` isn't much different! There are a few + additional checks to improve performance (don't clear or recreate the + view unless necessary) but otherwise it's much the same. + +.l-main-section +:marked + ## Wrap up + Here is the pertinent source for this chapter. + ++makeTabs(` + structural-directives/ts/app/unless.directive.ts, + structural-directives/ts/app/heavy-loader.component.ts, + structural-directives/ts/app/structural-directives.component.ts, + structural-directives/ts/app/structural-directives.component.html + `, + null, + `unless.directive.ts, + heavy-loader.component.ts, + structural-directives.component.ts, + structural-directives.component.html + `) +:marked + We learned that we can manipulate our HTML layout with + structural directives like `ngFor` and `ngIf` and we + wrote our own structural directive, `myUnless`, to do something similar. + + Angular offers more sophisticated techniques for managing layout + such as *structural components* that can take external content + and incorporate that content within their own templates. + Tab and tab pane controls are good examples. + + We'll learn about structural components in a future chapter. diff --git a/public/docs/ts/_cache/guide/template-syntax.jade b/public/docs/ts/_cache/guide/template-syntax.jade new file mode 100644 index 0000000000..3e0fb06ce8 --- /dev/null +++ b/public/docs/ts/_cache/guide/template-syntax.jade @@ -0,0 +1,1502 @@ +block includes + include ../_util-fns + - var _JavaScript = 'JavaScript'; + //- Double underscore means don't escape var, use !{__var}. + - var __chaining_op = '<code>;</code> or <code>,</code>'; + - var __new_op = '<code>new</code>'; + - var __objectAsMap = 'object'; + +:marked + Our Angular application manages what the user sees and can do, achieving this through the interaction of a Component class instance (the *component*) and its user-facing template. + + Many of us are familiar with the component/template duality from our experience with model-view-controller (MVC) or model-view-viewmodel (MVVM). In Angular, the component plays the part of the controller/viewmodel, and the template represents the view. + + Let’s find out what it takes to write a template for our view. We’ll cover these basic elements of template syntax: + + * [HTML](#html) + * [Interpolation](#interpolation) + * [Template expressions](#template-expressions) + * [Template statements](#template-statements) + * [Binding syntax](#binding-syntax) + * [Property binding](#property-binding) + * [Attribute, class, and style bindings](#other-bindings) + * [Event binding](#event-binding) + * [Two-way data binding with `NgModel`](#ngModel) + * [Built-in directives](#directives) + * [NgClass](#ngClass) + * [NgStyle](#ngStyle) + * [NgIf](#ngIf) + * [NgSwitch](#ngSwitch) + * [NgFor](#ngFor) + * [* and <template>](#star-template) + * [Template reference variables](#ref-vars) + * [Input and output properties](#inputs-outputs) + * [Template expression operators](#expression-operators) + * [pipe](#pipe) + * [safe navigation operator (?.)](#safe-navigation-operator) + + The <live-example></live-example> + demonstrates all of the syntax and code snippets described in this chapter. + +.l-main-section +:marked + ## HTML + HTML is the language of the Angular template. Our [QuickStart](../quickstart.html) application has a template that is pure HTML: + +code-example(language="html" escape="html"). + <h1>My First Angular 2 App</h1> + +:marked + Almost all HTML syntax is valid template syntax. The `<script>` element is a notable exception; it is forbidden, eliminating the risk of script injection attacks. (In practice, `<script>` is simply ignored.) + + Some legal HTML doesn’t make much sense in a template. The `<html>`, `<body>`, and `<base>` elements have no useful role in our repertoire. Pretty much everything else is fair game. + + We can extend the HTML vocabulary of our templates with components and directives that appear as new elements and attributes. In the following sections we are going to learn how to get and set DOM (Document Object Model) values dynamically through data binding. + + Let’s turn to the first form of data binding — interpolation — to see how much richer template HTML can be. + +.l-main-section +:marked + ## Interpolation + We met the double-curly braces of interpolation, `{{` and `}}`, early in our Angular education. ++makeExample('template-syntax/ts/app/app.component.html', 'first-interpolation')(format=".") +:marked + We use interpolation to weave calculated strings into the text between HTML element tags and within attribute assignments. + ++makeExample('template-syntax/ts/app/app.component.html', 'title+image')(format=".") +:marked + The material between the braces is often the name of a component property. Angular replaces that name with the + string value of the corresponding component property. In the example above, Angular evaluates the `title` and `heroImageUrl` properties + and "fills in the blanks", first displaying a bold application title and then a heroic image. + + More generally, the material between the braces is a **template expression** that Angular first **evaluates** + and then **converts to a string**. The following interpolation illustrates the point by adding the two numbers within braces: ++makeExample('template-syntax/ts/app/app.component.html', 'sum-1')(format=".") +:marked + The expression can invoke methods of the host component, as we do here with `getVal()`: ++makeExample('template-syntax/ts/app/app.component.html', 'sum-2')(format=".") +:marked + Angular evaluates all expressions in double curly braces, converts the expression results to strings, and links them with neighboring literal strings. Finally, + it assigns this composite interpolated result to an **element or directive property**. + + We appear to be inserting the result between element tags and assigning it to attributes. + It's convenient to think so, and we rarely suffer for this mistake. + Though this is not exactly true. Interpolation is a special syntax that Angular converts into a + [property binding](#property-binding), and is explained below. + + But first, let's take a closer look at template expressions and statements. + +<a id="template-expressions"></a> +.l-main-section +:marked + ## Template expressions + A template **expression** produces a value. + Angular executes the expression and assigns it to a property of a binding target; + the target might be an HTML element, a component, or a directive. + + We put a template expression within the interpolation braces when we wrote `{{1 + 1}}`. + We’ll see template expressions again in the [property binding](#property-binding) section, + appearing in quotes to the right of the `=` symbol as in `[property]="expression"`. + + We write template expressions in a language that looks like #{_JavaScript}. + Many #{_JavaScript} expressions are legal template expressions, but not all. + + #{_JavaScript} expressions that have or promote side effects are prohibited, + including: + + * assignments (`=`, `+=`, `-=`, ...) + * !{__new_op} + * chaining expressions with !{__chaining_op} + * increment and decrement operators (`++` and `--`) + +:marked + Other notable differences from #{_JavaScript} syntax include: + +block notable-differences + :marked + * no support for the bitwise operators `|` and `&` + * new [template expression operators](#expression-operators), such as `|` and `?.` + +h3#expression-context Expression context + +block template-expressions-cannot + :marked + Perhaps more surprising, template expressions cannot refer to anything in + the global namespace. They can’t refer to `window` or `document`. They + can’t call `console.log` or `Math.max`. They are restricted to referencing + members of the expression context. + +:marked + The *expression context* is typically the **component instance**, which is + the source of binding values. + + When we see *title* wrapped in double-curly braces, `{{title}}`, + we know that `title` is a property of the data-bound component. + When we see *isUnchanged* in `[disabled]="isUnchanged"`, + we know we are referring to that component's `isUnchanged` property. + + The component itself is usually the expression *context*, in which case + the template expression usually references that component. + + The expression context can include objects other than the component. + A [template reference variable](#ref-vars) is one such alternative context object. + +:marked + <a id="no-side-effects"></a> + ### Expression guidelines + Template expressions can make or break an application. + Please follow these guidelines: + + * [No visible side effects](#no-visible-side-effects) + * [Quick execution](#quick-execution) + * [Simplicity](#simplicity) + * [Idempotence](#idempotence) + + The only exceptions to these guidelines should be in specific circumstances that you thoroughly understand. + + #### No visible side effects + + A template expression should not change any application state other than the value of the + target property. + + This rule is essential to Angular's "unidirectional data flow" policy. + We should never worry that reading a component value might change some other displayed value. + The view should be stable throughout a single rendering pass. + + #### Quick execution + Angular executes template expressions more often than we think. + They can be called after every keypress or mouse move. + Expressions should finish quickly or the user experience may drag, especially on slower devices. + Consider caching values computed from other values when the computation is expensive. + + #### Simplicity + Although it's possible to write quite complex template expressions, we really shouldn't. + + A property name or method call should be the norm. + An occasional Boolean negation (`!`) is OK. + Otherwise, confine application and business logic to the component itself, + where it will be easier to develop and test. + + #### Idempotence + An [idempotent](https://en.wikipedia.org/wiki/Idempotence) expression is ideal because + it is free of side effects and improves Angular's change detection performance. + + In Angular terms, an idempotent expression always returns *exactly the same thing* until + one of its dependent values changes. +:marked + Dependent values should not change during a single turn of the event loop. + If an idempotent expression returns a string or a number, it returns the same string or number + when called twice in a row. If the expression returns an object (including #{_an} `#{_Array}`), + it returns the same object *reference* when called twice in a row. + +.l-main-section#template-statements +:marked + ## Template statements + + A template **statement** responds to an **event** raised by a binding target + such as an element, component, or directive. + + We’ll see template statements in the [event binding](#event-binding) section, + appearing in quotes to the right of the `=` symbol as in `(event)="statement"`. + + A template statement *has a side effect*. + It's how we update application state from user input. + There would be no point to responding to an event otherwise. + +.l-sub-section + :marked + Responding to events is the other side of Angular's "unidirectional data flow". + We're free to change anything, anywhere, during this turn of the event loop. + +:marked + Like template expressions, template *statements* use a language that looks like #{_JavaScript}. + The template statement parser is different than the template expression parser and + specifically supports both basic assignment (`=`) and chaining expressions + (with !{__chaining_op}). + + However, certain #{_JavaScript} syntax is not allowed: + * !{__new_op} + * increment and decrement operators, `++` and `--` + * operator assignment, such as `+=` and `-=` + * the bitwise operators `|` and `&` + * the [template expression operators](#expression-operators) + +:marked + ### Statement context + + As with expressions, statements can refer only to what's in the statement context — typically the + **component instance** to which we're binding the event. + +block statement-context + :marked + Template statements cannot refer to anything in the global namespace. They + can’t refer to `window` or `document`. They can’t call `console.log` or + `Math.max`. + +:marked + The *onSave* in `(click)="onSave()"` is sure to be a method of the data-bound component instance. + + The statement context may include an object other than the component. + A [template reference variable](#ref-vars) is one such alternative context object. + We'll frequently see the reserved `$event` symbol in event binding statements, + representing the "message" or "payload" of the raised event. + + ### Statement guidelines + + As with expressions, avoid writing complex template statements. + A method call or simple property assignment should be the norm. + + Now that we have a feel for template expressions and statements, + we’re ready to learn about the varieties of data binding syntax beyond interpolation. + +.l-main-section +:marked + <a id="binding-syntax"></a> + ## Binding syntax: An overview + Data binding is a mechanism for coordinating what users see with application data values. + While we could push values to and pull values from HTML, + the application is easier to write, read, and maintain if we turn these chores over to a binding framework. + We simply declare bindings between binding sources and target HTML elements and let the framework do the work. + + Angular provides many kinds of data binding, and we’ll discuss each of them in this chapter. + First we'll take a high-level view of Angular data binding and its syntax. + + We can group all bindings into three categories by the direction in which data flows. + Each category has its distinctive syntax: +table + tr + th Data direction + th Syntax + th Binding type + tr + td One-way<br>from data source<br>to view target + td + code-example(). + {{expression}} + [target] = "expression" + bind-target = "expression" + td. + Interpolation<br> + Property<br> + Attribute<br> + Class<br> + Style + tr + td One-way<br>from view target<br>to data source + td + code-example(). + (target) = "statement" + on-target = "statement" + td Event + tr + td Two-way + td + code-example(). + [(target)] = "expression" + bindon-target = "expression" + td Two-way + +:marked + Binding types other than interpolation have a **target name** to the left of the equal sign, + either surrounded by punctuation (`[]`, `()`) or preceded by a prefix (`bind-`, `on-`, `bindon-`). + + What is that target? Before we can answer that question, we must challenge ourselves to look at template HTML in a new way. + + ### A new mental model + + With all the power of data binding and our ability to extend the HTML vocabulary + with custom markup, it is tempting to think of template HTML as *HTML Plus*. + + Well, it *is* HTML Plus. + But it’s also significantly different than the HTML we’re used to. + We really need a new mental model. + + In the normal course of HTML development, we create a visual structure with HTML elements, and + we modify those elements by setting element attributes with string constants. + ++makeExample('template-syntax/ts/app/app.component.html', 'img+button')(format=".") +:marked + We still create a structure and initialize attribute values this way in Angular templates. + + Then we learn to create new elements with components that encapsulate HTML + and drop them into our templates as if they were native HTML elements. ++makeExample('template-syntax/ts/app/app.component.html', 'hero-detail-1')(format=".") +:marked + That’s HTML Plus. + + Now we start to learn about data binding. The first binding we meet might look like this: + ++makeExample('template-syntax/ts/app/app.component.html', 'disabled-button-1')(format=".") +:marked + We’ll get to that peculiar bracket notation in a moment. Looking beyond it, + our intuition tells us that we’re binding to the button's `disabled` attribute and setting + it to the current value of the component’s `isUnchanged` property. + + Our intuition is wrong! Our everyday HTML mental model is misleading us. + In fact, once we start data binding, we are no longer working with HTML *attributes*. We aren't setting attributes. + We are setting the *properties* of DOM elements, components, and directives. + +.l-sub-section + :marked + ### HTML attribute vs. DOM property + + The distinction between an HTML attribute and a DOM property is crucial to understanding how Angular binding works. + + **Attributes are defined by HTML. Properties are defined by the DOM (Document Object Model).** + + * A few HTML attributes have 1:1 mapping to properties. `id` is one example. + + * Some HTML attributes don't have corresponding properties. `colspan` is one example. + + * Some DOM properties don't have corresponding attributes. `textContent` is one example. + + * Many HTML attributes appear to map to properties ... but not in the way we might think! + + That last category can be especially confusing ... until we understand this general rule: + + **Attributes *initialize* DOM properties and then they are done. + Property values can change; attribute values can't.** + + For example, when the browser renders `<input type="text" value="Bob">`, it creates a + corresponding DOM node with a `value` property *initialized* to "Bob". + + When the user enters "Sally" into the input box, the DOM element `value` *property* becomes "Sally". + But the HTML `value` *attribute* remains unchanged as we discover if we ask the input element + about that attribute: `input.getAttribute('value') // returns "Bob"` + + The HTML attribute `value` specifies the *initial* value; the DOM `value` property is the *current* value. + + The `disabled` attribute is another peculiar example. A button's `disabled` *property* is + `false` by default so the button is enabled. + When we add the `disabled` *attribute*, its presence alone initializes the button's `disabled` *property* to `true` + so the button is disabled. + + Adding and removing the `disabled` *attribute* disables and enables the button. The value of the *attribute* is irrelevant, + which is why we cannot enable a button by writing `<button disabled="false">Still Disabled</button>`. + + Setting the button's `disabled` *property* (say, with an Angular binding) disables or enables the button. + The value of the *property* matters. + + **The HTML attribute and the DOM property are not the same thing, even when they have the same name.** + +:marked + This is so important, we’ll say it again. + + **Template binding works with *properties* and *events*, not *attributes*.** + +.callout.is-helpful + header A world without attributes + :marked + In the world of Angular 2, the only role of attributes is to initialize element and directive state. + When we data bind, we're dealing exclusively with element and directive properties and events. + Attributes effectively disappear. +:marked + With this model firmly in mind, let's learn about binding targets. + + ### Binding targets + The **target of a data binding** is something in the DOM. + Depending on the binding type, the target can be an + (element | component | directive) property, an + (element | component | directive) event, or (rarely) an attribute name. + The following table summarizes: + +// If you update this table, UPDATE it in Dart & JS, too. +<div width="90%"> +table + tr + th Binding type + th Target + th Examples + tr + td Property + td. + Element property<br> + Component property<br> + Directive property + td + +makeExample('template-syntax/ts/app/app.component.html', 'property-binding-syntax-1')(format=".") + tr + td Event + td. + Element event<br> + Component event<br> + Directive event + td + +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-syntax-1')(format=".") + tr + td Two-way + td. + Event and property + td + +makeExample('template-syntax/ts/app/app.component.html', '2-way-binding-syntax-1')(format=".") + tr + td Attribute + td. + Attribute + (the exception) + td + +makeExample('template-syntax/ts/app/app.component.html', 'attribute-binding-syntax-1')(format=".") + tr + td Class + td. + <code>class</code> property + td + +makeExample('template-syntax/ts/app/app.component.html', 'class-binding-syntax-1')(format=".") + tr + td Style + td. + <code>style</code> property + td + +makeExample('template-syntax/ts/app/app.component.html', 'style-binding-syntax-1')(format=".") +</div> + +:marked + Let’s descend from the architectural clouds and look at each of these binding types in concrete detail. + +.l-main-section +:marked + ## Property binding + We write a template **property binding** when we want to set a property of a view element to the value of + a [template expression](#template-expressions). + + The most common property binding sets an element property to a component property value. An example is + binding the `src` property of an image element to a component’s `heroImageUrl` property: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-1')(format=".") +:marked + Another example is disabling a button when the component says that it `isUnchanged`: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-2')(format=".") +:marked + Another is setting a property of a directive: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-3')(format=".") +:marked + Yet another is setting the model property of a custom component (a great way + for parent and child components to communicate): ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".") +:marked + ### One-way *in* + People often describe property binding as *one-way data binding* because it flows a value in one direction, + from a component’s data property into a target element property. + + We cannot use property binding to pull values *out* of the target element. + We can't bind to a property of the target element to read it. We can only set it. + +.l-sub-section + :marked + Nor can we use property binding to *call* a method on the target element. + + If the element raises events we can listen to them with an [event binding](#event-binding). + + If we must read a target element property or call one of its methods, + we'll need a different technique. + See the API reference for + [viewChild](../api/core/index/ViewChild-var.html) and + [contentChild](../api/core/index/ContentChild-var.html). + +// TODO (global): once we have api docs everywhere, change /docs/ts/latest/ to ../ + +:marked + ### Binding target + An element property between enclosing square brackets identifies the target property. The target property in the following code is the image element’s `src` property. + ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-1')(format=".") +:marked + Some people prefer the `bind-` prefix alternative, known as the *canonical form*: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-5')(format=".") +:marked + The target name is always the name of a property, even when it appears to be the name of something else. We see `src` and may think it’s the name of an attribute. No. It’s the name of an image element property. + + Element properties may be the more common targets, + but Angular looks first to see if the name is a property of a known directive, + as it is in the following example: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-3')(format=".") + +.l-sub-section + :marked + Technically, Angular is matching the name to a directive [input](#inputs-outputs), + one of the property names listed in the directive’s `inputs` array or a property decorated with `@Input()`. + Such inputs map to the directive’s own properties. +:marked + If the name fails to match a property of a known directive or element, Angular reports an “unknown directive” error. + + ### Avoid side effects + As we've already discussed, evaluation of a template expression should have no visible side effects. The expression language itself does its part to keep us safe. We can’t assign a value to anything in a property binding expression nor use the increment and decrement operators. + + Of course, our expression might invoke a property or method that has side effects. Angular has no way of knowing that or stopping us. + + The expression could call something like `getFoo()`. Only we know what `getFoo()` does. + If `getFoo()` changes something and we happen to be binding to that something, we risk an unpleasant experience. Angular may or may not display the changed value. Angular may detect the change and throw a warning error. Our general advice: stick to data properties and to methods that return values and do no more. + + ### Return the proper type + The template expression should evaluate to the type of value expected by the target property. + Return a string if the target property expects a string. + Return a number if the target property expects a number. + Return an object if the target property expects an object. + + The `hero` property of the `HeroDetail` component expects a `Hero` object, which is exactly what we’re sending in the property binding: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-4')(format=".") + +block dart-type-exceptions + //- N/A + +:marked + ### Remember the brackets + The brackets tell Angular to evaluate the template expression. + If we forget the brackets, Angular treats the string as a constant and *initializes the target property* with that string. + It does *not* evaluate the string! + + Don't make the following mistake: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-6')(format=".") + +block dart-type-exception-example + //- N/A + +a(id="one-time-initialization") +:marked + ### One-time string initialization + We *should* omit the brackets when all of the following are true: + * The target property accepts a string value. + * The string is a fixed value that we can bake into the template. + * This initial value never changes. + + We routinely initialize attributes this way in standard HTML, and it works + just as well for directive and component property initialization. + The following example initializes the `prefix` property of the `HeroDetailComponent` to a fixed string, + not a template expression. Angular sets it and forgets about it. ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-7')(format=".") +:marked + The `[hero]` binding, on the other hand, remains a live binding to the component's `currentHero` property. + + ### Property binding or interpolation? + We often have a choice between interpolation and property binding. + The following binding pairs do the same thing: ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation')(format=".") +:marked + Interpolation is a convenient alternative for property binding in many cases. + In fact, Angular translates those interpolations into the corresponding property bindings + before rendering the view. + + There is no technical reason to prefer one form to the other. + We lean toward readability, which tends to favor interpolation. + We suggest establishing coding style rules and choosing the form that + both conforms to the rules and feels most natural for the task at hand. + + +:marked + #### Content Security + Imagine the following *malicious content*. ++makeExample('template-syntax/ts/app/app.component.ts', 'evil-title')(format=".") +:marked + Fortunately, Angular data binding is on alert for dangerous HTML. + It *sanitizes* the values before displaying them. + It **will not** allow HTML with script tags to leak into the browser, neither with interpolation + nor property binding. ++makeExample('template-syntax/ts/app/app.component.html', 'property-binding-vs-interpolation-sanitization')(format=".") +:marked + Interpolation handles the script tags differently than property binding but both approaches render the + content harmlessly. +figure.image-display + img(src='/resources/images/devguide/template-syntax/evil-title.png' alt="evil title made safe" width='500px') + +.l-main-section +:marked + <a id="other-bindings"></a> + ## Attribute, Class, and Style Bindings + The template syntax provides specialized one-way bindings for scenarios less well suited to property binding. + + ### Attribute Binding + We can set the value of an attribute directly with an **attribute binding**. +.l-sub-section + :marked + This is the only exception to the rule that a binding sets a target property. This is the only binding that creates and sets an attribute. + +:marked + We have stressed throughout this chapter that setting an element property with a property binding is always preferred to setting the attribute with a string. Why does Angular offer attribute binding? + + **We must use attribute binding when there is no element property to bind.** + + Consider the [ARIA](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA), + [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG), and + table span attributes. They are pure attributes. + They do not correspond to element properties, and they do not set element properties. + There are no property targets to bind to. + + We become painfully aware of this fact when we try to write something like this: +code-example(language="html"). + <tr><td colspan="{{1 + 1}}">Three-Four</td></tr> +:marked + We get this error: +code-example(format="nocode"). + Template parse errors: + Can't bind to 'colspan' since it isn't a known native property +:marked + As the message says, the `<td>` element does not have a `colspan` property. + It has the "colspan" *attribute*, but + interpolation and property binding can set only *properties*, not attributes. + + We need attribute bindings to create and bind to such attributes. + + Attribute binding syntax resembles property binding. + Instead of an element property between brackets, we start with the prefix **`attr`**, + followed by a dot (`.`) and the name of the attribute. We then set the attribute + value, using an expression that resolves to a string. + + Here we bind `[attr.colspan]` to a calculated value: ++makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-colspan')(format=".") +:marked + Here's how the table renders: + <table border="1px"> + <tr><td colspan="2">One-Two</td></tr> + <tr><td>Five</td><td>Six</td></tr> + </table> + + One of the primary use cases for attribute binding + is to set ARIA attributes, as in this example: ++makeExample('template-syntax/ts/app/app.component.html', 'attrib-binding-aria')(format=".") +:marked + ### Class Binding + + We can add and remove CSS class names from an element’s `class` attribute with + a **class binding**. + + Class binding syntax resembles property binding. + Instead of an element property between brackets, we start with the prefix `class`, + optionally followed by a dot (`.`) and the name of a CSS class: `[class.class-name]`. + + The following examples show how to add and remove the application's "special" class + with class bindings. Here's how we set the attribute without binding: ++makeExample('template-syntax/ts/app/app.component.html', 'class-binding-1')(format=".") +:marked + We can replace that with a binding to a string of the desired class names; this is an all-or-nothing, replacement binding. ++makeExample('template-syntax/ts/app/app.component.html', 'class-binding-2')(format=".") + +block dart-class-binding-bug + //- N/A + +:marked + Finally, we can bind to a specific class name. + Angular adds the class when the template expression evaluates to #{_truthy}. + It removes the class when the expression is #{_falsey}. ++makeExample('template-syntax/ts/app/app.component.html', 'class-binding-3')(format=".") + +.l-sub-section + :marked + While this is a fine way to toggle a single class name, + we generally prefer the [NgClass directive](#ngClass) for managing multiple class names at the same time. + +:marked + ### Style Binding + + We can set inline styles with a **style binding**. + + Style binding syntax resembles property binding. + Instead of an element property between brackets, we start with the prefix `style`, + followed by a dot (`.`) and the name of a CSS style property: `[style.style-property]`. + ++makeExample('template-syntax/ts/app/app.component.html', 'style-binding-1')(format=".") +:marked + Some style binding styles have unit extension. Here we conditionally set the font size in “em” and “%” units . ++makeExample('template-syntax/ts/app/app.component.html', 'style-binding-2')(format=".") + +.l-sub-section + :marked + While this is a fine way to set a single style, + we generally prefer the [NgStyle directive](#ngStyle) when setting several inline styles at the same time. + +.l-sub-section + :marked + Note that a _style property_ name can be written in either + [dash-case](glossary.html#dash-case), as shown above, or + [camelCase](glossary.html#camelcase), such as `fontSize`. + +block style-property-name-dart-diff + //- N/A + +.l-main-section +:marked + ## Event Binding + The bindings we’ve met so far flow data in one direction: *from the component to an element*. + + Users don’t just stare at the screen. They enter text into input boxes. They pick items from lists. + They click buttons. Such user actions may result in a flow of data in the opposite direction: + *from an element to the component*. + + The only way to know about a user action is to listen for certain events such as + keystrokes, mouse movements, clicks, and touches. + We declare our interest in user actions through Angular event binding. + + Event binding syntax consists of a **target event** within parentheses on the left of an equal sign, and a quoted + [template statement](#template-statements) on the right. + The following event binding listens for the button’s click event, calling + the component's `onSave()` method whenever a click occurs: ++makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") +:marked + ### Target Event + A **name between enclosing parentheses** — for example, `(click)` — + identifies the target event. In the following example, the target is the button’s click event. ++makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") +:marked + Some people prefer the `on-` prefix alternative, known as the *canonical form*: ++makeExample('template-syntax/ts/app/app.component.html', 'event-binding-2')(format=".") +:marked + Element events may be the more common targets, but Angular looks first to see if the name matches an event property + of a known directive, as it does in the following example: ++makeExample('template-syntax/ts/app/app.component.html', 'event-binding-3')(format=".") + +.l-sub-section + :marked + The `myClick` directive is further described below in the section + on [Aliasing input/output properties](#aliasing-io). + +:marked + If the name fails to match an element event or an output property of a known directive, + Angular reports an “unknown directive” error. + + ### *$event* and event handling statements + In an event binding, Angular sets up an event handler for the target event. + + When the event is raised, the handler executes the template statement. + The template statement typically involves a receiver that wants to do something + in response to the event, such as take a value from the HTML control and store it + in a model. + + The binding conveys information about the event, including data values, through + an **event object named `$event`**. + + The shape of the event object is determined by the target event itself. + If the target event is a native DOM element event, the `$event` is a + [DOM event object]( https://developer.mozilla.org/en-US/docs/Web/Events), + with properties such as `target` and `target.value`. + + Consider this example: ++makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".") +:marked + We’re binding the input box `value` to a `firstName` property, and we’re listening for changes by binding to the input box’s `input` event. + When the user makes changes, the `input` event is raised, and the binding executes the statement within a context that includes the DOM event object, `$event`. + + To update the `firstName` property, we must get the changed text by following + the path `$event.target.value`. + + If the event belongs to a directive (remember: components are directives), `$event` has whatever shape the directive chose to produce. + + <a id="eventemitter"></a> + <a id="custom-event"></a> + ### Custom Events with EventEmitter + + Directives typically raise custom events with an Angular [EventEmitter](../api/core/index/EventEmitter-class.html). + A directive creates an `EventEmitter` and exposes it as a property. + The directive calls `EventEmitter.emit(payload)` to fire an event, passing in a message payload that can be anything. + Parent directives listen for the event by binding to this property and accessing the payload through the `$event` object. + + Consider a `HeroDetailComponent` that presents hero information and responds to user actions. + Although the `HeroDetailComponent` has a delete button it doesn't know how to delete the hero itself. + The best it can do is raise an event reporting the user's delete request. + + Here are the pertinent excerpts from that `HeroDetailComponent`: ++makeExample('template-syntax/ts/app/hero-detail.component.ts', +'template-1', 'HeroDetailComponent.ts (template)')(format=".") ++makeExample('template-syntax/ts/app/hero-detail.component.ts', +'deleteRequest', 'HeroDetailComponent.ts (delete logic)')(format=".") + +:marked + The component defines a `deleteRequest` property that returns an `EventEmitter`. + When the user clicks *delete*, the component invokes the `delete()` method + which tells the `EventEmitter` to emit a `Hero` object. + + Now imagine a hosting parent component that binds to the `HeroDetailComponent`'s `deleteRequest` event. + ++makeExample('template-syntax/ts/app/app.component.html', +'event-binding-to-component')(format=".") +:marked + When the `deleteRequest` event fires, Angular calls the parent component's `deleteHero` method, + passing the *hero-to-delete* (emitted by `HeroDetail`) in the `$event` variable. + + ### Template statements have side effects + The `deleteHero` method has a side effect: it deletes a hero. + Template statement side effects are not just OK, they are expected. + + Deleting the hero updates the model, perhaps triggering other changes + including queries and saves to a remote server. + These changes percolate through the system and are ultimately displayed in this and other views. + It's all good. + +// + :marked + ### Event bubbling and propagation [TODO: reinstate this section when it becomes true] + Angular invokes the event-handling statement if the event is raised by the current element or one of its child elements. + +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-bubbling')(format=".") + :marked + Many DOM events, both [native](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Overview_of_Events_and_Handlers ) and [custom](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events ), bubble up their ancestor tree of DOM elements until an event handler along the way prevents further propagation. + + .l-sub-section + :marked + `EventEmitter` events don’t bubble. + + :marked + The result of an event binding statement determines whether + [event propagation](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Examples#Example_5:_Event_Propagation) + continues or stops with the current element. + + Event propagation stops if the binding statement returns a falsey value (as does a method with no return value). + Clicking the button in the next example triggers a save; + the click doesn't make it to the outer `<div>` so the div's save handler is not called. + +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-no-propagation')(format=".") + :marked + Propagation continues if the statement returns a truthy value. In the next example, the click is heard by both the button + and the outer `<div>`, causing a double save. + +makeExample('template-syntax/ts/app/app.component.html', 'event-binding-propagation')(format=".") + + +.l-main-section +:marked + <a id="ngModel"></a> + ## Two-way binding with NgModel + When developing data entry forms, we often want to both display a data property and update that property when the user makes changes. + + The `[(ngModel)]` two-way data binding syntax makes that easy. Here's an example: ++makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".") +.callout.is-important + header [()] = banana in a box + :marked + To remember that the parentheses go inside the brackets, visualize a *banana in a box*. +:marked + Alternatively, we can use the canonical prefix form: ++makeExample('template-syntax/ts/app/app.component.html', 'NgModel-2')(format=".") +:marked + There’s a story behind this construction, a story that builds on the property and event binding techniques we learned previously. + + ### Inside [(ngModel)] + We could have achieved the same result with separate bindings to + the `<input>` element's `value` property and `input` event. ++makeExample('template-syntax/ts/app/app.component.html', 'without-NgModel')(format=".") +:marked + That’s cumbersome. Who can remember which element property to set and what event reports user changes? + How do we extract the currently displayed text from the input box so we can update the data property? + Who wants to look that up each time? + + That `ngModel` directive hides these onerous details behind its own `ngModel` input and `ngModelChange` output properties. ++makeExample('template-syntax/ts/app/app.component.html', 'NgModel-3')(format=".") +.l-sub-section + :marked + The `ngModel` input property sets the element's value property and the `ngModelChange` output property + listens for changes to the element's value. + The details are specific to each kind of element and therefore the `NgModel` directive only works for elements, + such as the input text box, that are supported by a [ControlValueAccessor](../api/common/index/ControlValueAccessor-interface.html). + We can't apply `[(ngModel)]` to our custom components until we write a suitable *value accessor*, + a technique that is beyond the scope of this chapter. + +:marked + Separate `ngModel` bindings is an improvement. We can do better. + + We shouldn't have to mention the data property twice. Angular should be able to capture the component’s data property and set it + with a single declaration — which it can with the `[( )]` syntax: ++makeExample('template-syntax/ts/app/app.component.html', 'NgModel-1')(format=".") + +.l-sub-section + :marked + `[(ngModel)]` is a specific example of a more general pattern in which Angular "de-sugars" the `[(x)]` syntax + into an `x` input property for property binding and an `xChange` output property for event binding. + Angular constructs the event property binding's template statement by appending `=$event` + to the literal string of the template expression. + + > <span style="font-family:courier">[(_x_)]="_e_" <==> [_x_]="_e_" (<i>x</i>Change)="_e_=$event"</span> + + We can write a two-way binding directive of our own to exploit this behavior. + +:marked + Is `[(ngModel)]` all we need? Is there ever a reason to fall back to its expanded form? + + The `[( )]` syntax can only _set_ a data-bound property. + If we need to do something more or something different, we need to write the expanded form ourselves. + + Let's try something silly like forcing the input value to uppercase: ++makeExample('template-syntax/ts/app/app.component.html', 'NgModel-4')(format=".") +:marked + Here are all variations in action, including the uppercase version: +figure.image-display + img(src='/resources/images/devguide/template-syntax/ng-model-anim.gif' alt="NgModel variations") + +.l-main-section +:marked + <a id="directives"></a> + ## Built-in directives + + Earlier versions of Angular included over seventy built-in directives. + The community contributed many more, and countless private directives + have been created for internal applications. + + We don’t need many of those directives in Angular 2. + Quite often we can achieve the same results with the more capable and expressive Angular 2 binding system. + Why create a directive to handle a click when we can write a simple binding such as this? ++makeExample('template-syntax/ts/app/app.component.html', 'event-binding-1')(format=".") +:marked + We still benefit from directives that simplify complex tasks. + Angular still ships with built-in directives; just not as many. + We'll write our own directives, just not as many. + + This segment reviews some of the most frequently used built-in directives. + +<a id="ngClass"></a> +.l-main-section +:marked + ### NgClass + + We typically control how elements appear + by adding and removing CSS classes dynamically. + We can bind to `NgClass` to add or remove several classes simultaneously. + + A [class binding](#class-binding) is a good way to add or remove a *single* class. ++makeExample('template-syntax/ts/app/app.component.html', 'class-binding-3a')(format=".") +:marked + The `NgClass` directive may be the better choice + when we want to add or remove *many* CSS classes at the same time. + + A good way to apply `NgClass` is by binding it to a key:value control !{__objectAsMap}. Each key of the object is a CSS class name; its value is `true` if the class should be added, `false` if it should be removed. + +:marked + Consider a component method such as `setClasses` that manages the state of three CSS classes: ++makeExample('template-syntax/ts/app/app.component.ts', 'setClasses')(format=".") +:marked + Now we can add an `NgClass` property binding that calls `setClasses` + and sets the element's classes accordingly: ++makeExample('template-syntax/ts/app/app.component.html', 'NgClass-1')(format=".") + +<a id="ngStyle"></a> +.l-main-section +:marked + ### NgStyle + We can set inline styles dynamically, based on the state of the component. + Binding to `NgStyle` lets us set many inline styles simultaneously. + + A [style binding](#style-binding) is an easy way to set a *single* style value. ++makeExample('template-syntax/ts/app/app.component.html', 'NgStyle-1')(format=".") +:marked + The `NgStyle` directive may be the better choice + when we want to set *many* inline styles at the same time. + + We apply `NgStyle` by binding it to a key:value control !{__objectAsMap}. + Each key of the object is a style name; its value is whatever is appropriate for that style. + + Consider a component method such as `setStyles` that returns an object defining three styles: ++makeExample('template-syntax/ts/app/app.component.ts', 'setStyles')(format=".") +:marked + Now we just add an `NgStyle` property binding that calls `setStyles` + and sets the element's styles accordingly: ++makeExample('template-syntax/ts/app/app.component.html', 'NgStyle-2')(format=".") + +<a id="ngIf"></a> +.l-main-section +:marked + ### NgIf + We can add an element subtree (an element and its children) to the DOM by binding an `NgIf` directive to a #{_truthy} expression. ++makeExample('template-syntax/ts/app/app.component.html', 'NgIf-1')(format=".") + +.alert.is-critical + :marked + Don't forget the asterisk (`*`) in front of `ngIf`. + For more information, see [\* and <template>](#star-template). +:marked + Binding to a #{_falsey} expression removes the element subtree from the DOM. ++makeExample('template-syntax/ts/app/app.component.html', 'NgIf-2')(format=".") + +block dart-no-truthy-falsey + //- N/A + +:marked + #### Visibility and NgIf are not the same + We can show and hide an element subtree (the element and its children) with a + [class](#class-binding) or [style](#style-binding) binding: ++makeExample('template-syntax/ts/app/app.component.html', 'NgIf-3')(format=".") +:marked + Hiding a subtree is quite different from excluding a subtree with `NgIf`. + + When we hide the element subtree, it remains in the DOM. + Components in the subtree are preserved, along with their state. + Angular may continue to check for changes even to invisible properties. + The subtree may tie up substantial memory and computing resources. + + When `NgIf` is `false`, Angular physically removes the element subtree from the DOM. + It destroys components in the subtree, along with their state, potentially freeing up substantial resources and + resulting in better performance for the user. + + The show/hide technique is probably fine for small element trees. + We should be wary when hiding large trees; `NgIf` may be the safer choice. Always measure before leaping to conclusions. + +<a id="ngSwitch"></a> +.l-main-section +:marked + ### NgSwitch + We bind to `NgSwitch` when we want to display *one* element tree (an element and its children) + from a *set* of possible element trees, based on some condition. + Angular puts only the *selected* element tree into the DOM. + + Here’s an example: ++makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch')(format=".") +:marked + We bind the parent `NgSwitch` directive to an expression returning a *switch value*. + The value is a string in this example, but it can be a value of any type. + + In this example, the parent `NgSwitch` directive controls a set of child `<span>` elements. + A `<span>` is either pegged to a *match value* expression or marked as the default. + + **At any particular moment, at most one of these *spans* is in the DOM.** + + If the *span*’s *match value* equals the switch value, Angular adds the `<span>` to the DOM. + If none of the *spans* is a match, Angular adds the default *span* to the DOM. + Angular removes and destroys all other *spans*. +.l-sub-section + :marked + We could substitute any element for the *span* in this example. + That element could be a `<div>` with a vast subtree of its own elements. + Only the matching `<div>` and its subtree would appear in the DOM; + the others would be removed. +:marked + Three collaborating directives are at work here: + 1. `ngSwitch`: bound to an expression that returns the switch value + 1. `ngSwitchCase`: bound to an expression returning a match value + 1. `ngSwitchDefault`: a marker attribute on the default element + +.alert.is-critical + :marked + **Do *not*** put the asterisk (`*`) in front of `ngSwitch`. Use the property binding instead. + + **Do** put the asterisk (`*`) in front of `ngSwitchCase` and `ngSwitchDefault`. + For more information, see [\* and <template>](#star-template). + +<a id="ngFor"></a> +.l-main-section +:marked + ### NgFor + `NgFor` is a _repeater_ directive — a way to customize data display. + + Our goal is to present a list of items. We define a block of HTML that defines how a single item should be displayed. + We tell Angular to use that block as a template for rendering each item in the list. + + Here is an example of `NgFor` applied to a simple `<div>`: ++makeExample('template-syntax/ts/app/app.component.html', 'NgFor-1')(format=".") +:marked + We can also apply an `NgFor` to a component element, as in this example: ++makeExample('template-syntax/ts/app/app.component.html', 'NgFor-2')(format=".") + +.alert.is-critical + :marked + Don't forget the asterisk (`*`) in front of `ngFor`. + For more information, see [\* and <template>](#star-template). +:marked + The text assigned to `*ngFor` is the instruction that guides the repeater process. + +<a id="ngForMicrosyntax"></a> +:marked + #### NgFor microsyntax + The string assigned to `*ngFor` is not a [template expression](#template-expressions). + It’s a *microsyntax* — a little language of its own that Angular interprets. In this example, the string `"let hero of heroes"` means: + + > *Take each hero in the `heroes` #{_array}, store it in the local `hero` variable, and make it available to the templated HTML for each iteration.* + + Angular translates this instruction into a new set of elements and bindings. + + In the two previous examples, the `ngFor` directive iterates over the `heroes` #{_array} returned by the parent component’s `heroes` property, + stamping out instances of the element to which it is applied. + Angular creates a fresh instance of the template for each hero in the array. + + The `let` keyword before `hero` creates a template input variable called `hero`. + +.alert.is-critical + :marked + A template input variable is **not** the same as a [template reference variable](#ref-vars)! + +:marked + We use this variable within the template to access a hero’s properties, + as we’re doing in the interpolation. + We can also pass the variable in a binding to a component element, + as we're doing with `hero-detail`. + +:marked + #### NgFor with index + The `ngFor` directive supports an optional `index` that increases from 0 to the length of the array for each iteration. + We can capture the index in a template input variable and use it in our template. + + The next example captures the index in a variable named `i`, using it to stamp out rows like "1 - Hercules Son of Zeus". ++makeExample('template-syntax/ts/app/app.component.html', 'NgFor-3')(format=".") +.l-sub-section + :marked + Learn about other special *index-like* values such as `last`, `even`, and `odd` in the [NgFor API reference](../api/common/index/NgFor-directive.html). + +:marked + #### NgForTrackBy + The `ngFor` directive has the potential to perform poorly, especially with large lists. + A small change to one item, an item removed, or an item added can trigger a cascade of DOM manipulations. + + For example, we could refresh the list of heroes by re-querying the server. + The refreshed list probably contains most, if not all, of the previously displayed heroes. + + *We* know this because the `id` of each hero hasn't changed. + But Angular sees only a fresh list of new object references. + It has no choice but to tear down the old list, discard those DOM elements, and re-build a new list with new DOM elements. + + Angular can avoid this churn if we give it a *tracking* function that tells it what we know: + that two objects with the same `hero.id` are the same *hero*. Here is such a function: ++makeExample('template-syntax/ts/app/app.component.ts', 'trackByHeroes')(format=".") +:marked + Now set the `NgForTrackBy` directive to that *tracking* function. ++makeExample('template-syntax/ts/app/app.component.html', 'NgForTrackBy-2')(format=".") +:marked + The *tracking* function doesn't eliminate all DOM changes. + Angular may have to update the DOM element if the same-hero *properties* have changed. + But if the properties haven't changed — and most of the time they will not have changed — + Angular can leave those DOM elements alone. The list UI will be smoother and more responsive. + + Here is an illustration of the `NgForTrackBy` effect. +figure.image-display + img(src='/resources/images/devguide/template-syntax/ng-for-track-by-anim.gif' alt="NgForTrackBy") + +<a id="star-template"></a> +<a id="structural-directive"></a> +.l-main-section +:marked + ## * and <template> + When we reviewed the `NgFor`, `NgIf`, and `NgSwitch` built-in directives, we called out an oddity of the syntax: the asterisk (`*`) that appears before the directive names. + + The `*` is a bit of syntactic sugar that makes it easier to read and write directives that modify HTML layout + with the help of templates. + `NgFor`, `NgIf`, and `NgSwitch` all add and remove element subtrees that are wrapped in `<template>` tags. + + We didn't see the `<template>` tags because the `*` prefix syntax allowed us to skip those tags and + focus directly on the HTML element that we are including, excluding, or repeating. + + In this section we go under the hood and see how + Angular strips away the `*` and expands the HTML into the `<template>` tags for us. + +:marked + ### Expanding `*ngIf` + We can do what Angular does ourselves and expand the `*` prefix syntax to template syntax. Here's some code with `*ngIf`: ++makeExample('template-syntax/ts/app/app.component.html', 'Template-1')(format=".") +:marked + The `currentHero` is referenced twice, first as the true/false condition for `NgIf` and + again as the actual hero passed into the `HeroDetailComponent`. + + The first expansion step transports the `ngIf` (without the `*` prefix) and its contents + into an expression assigned to a `template` directive. ++makeExample('template-syntax/ts/app/app.component.html', 'Template-2a')(format=".") +:marked + The next (and final) step unfolds the HTML into a `<template>` tag and `[ngIf]` [property binding](#property-binding): ++makeExample('template-syntax/ts/app/app.component.html', 'Template-2')(format=".") +:marked + Notice that the `[hero]="currentHero"` binding remains on the child `<hero-detail>` + element inside the template. + +block remember-the-brackets + .callout.is-critical + header Remember the brackets! + :marked + Don’t make the mistake of writing `ngIf="currentHero"`! + That syntax assigns the *string* value `"currentHero"` to `ngIf`. + In JavaScript a non-empty string is a truthy value, so `ngIf` would always be + `true` and Angular would always display the `hero-detail` + … even when there is no `currentHero`! + +:marked + ### Expanding `*ngSwitch` + A similar transformation applies to `*ngSwitch`. We can de-sugar the syntax ourselves. + Here's an example, first with `*ngSwitchCase` and `*ngSwitchDefault` and then again with `<template>` tags: ++makeExample('template-syntax/ts/app/app.component.html', 'NgSwitch-expanded')(format=".") +:marked + The `*ngSwitchCase` and `*ngSwitchDefault` expand in exactly the same manner as `*ngIf`, + wrapping their former elements in `<template>` tags. + + Now we can see why the `ngSwitch` itself is not prefixed with an asterisk (*). + It does not define content. It's job is to control a collection of templates. + + In this case, it governs two sets of `ngSwitchCase` and `NgSwitchDefault` directives. + We should expect it to display the values of the selected template twice, + once for the (*) prefixed version and once for the expanded template version. + That's exactly what we see in this example: +figure.image-display + img(src='/resources/images/devguide/template-syntax/ng-switch-anim.gif' alt="NgSwitch") +:marked + ### Expanding `*ngFor` + The `*ngFor` undergoes a similar transformation. We begin with an `*ngFor` example: ++makeExample('template-syntax/ts/app/app.component.html', 'Template-3a')(format=".") +:marked + Here's the same example after transporting the `ngFor` to the `template` directive: ++makeExample('template-syntax/ts/app/app.component.html', 'Template-3')(format=".") +:marked + And here it is expanded further into a `<template>` tag wrapping the original `<hero-detail>` element: ++makeExample('template-syntax/ts/app/app.component.html', 'Template-4')(format=".") +:marked + The `NgFor` code is a bit more complex than `NgIf` because a repeater has more moving parts to configure. + In this case, we have to remember to create and assign the `NgForOf` directive that identifies the list and the `NgForTrackBy` directive. + Using the `*ngFor` syntax is much easier than writing out this expanded HTML ourselves. + +<a id="ref-vars"></a> +.l-main-section +:marked + ## Template reference variables + + A **template reference variable** is a reference to a DOM element or directive within a template. + + It can be used with native DOM elements but also with Angular 2 components — in fact, it will work with any custom web component. + +:marked + ### Referencing a template reference variable + + We can reference a template reference variable on the same element, on a sibling element, or on + any child elements. + + Here are two other examples of creating and consuming a Template reference variable: ++makeExample('template-syntax/ts/app/app.component.html', 'ref-phone')(format=".") +:marked + The hash (`#`) prefix to "phone" means that we're defining a `phone` variable. +.l-sub-section + :marked + Folks who don't like using the `#` character can use its canonical alternative, + the `ref-` prefix. For example, we can declare the our `phone` variable using + either `#phone` or `ref-phone`. + +:marked + ### How a variable gets its value + + Angular sets the variable's value to the element on which it was defined. + We defined these variables on the `input` elements. + We’re passing those `input` element objects across to the + button elements, where they're used in arguments to the `call` methods in the event bindings. + +:marked + ### NgForm and template reference variables + Let's look at one final example: a form, the poster child for template reference variables. + + The HTML for a form can be quite involved, as we saw in the [Forms](forms.html) chapter. + The following is a *simplified* example — and it's not simple at all. ++makeExample('template-syntax/ts/app/app.component.html', 'ref-form')(format=".") +:marked + A template reference variable, `theForm`, appears three times in this example, separated + by a large amount of HTML. ++makeExample('template-syntax/ts/app/app.component.html', 'ref-form-a')(format=".") +:marked + What is the value of `theForm`? + + It would be the [HTMLFormElement](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement) + if Angular hadn't taken it over. + It's actually `ngForm`, a reference to the Angular built-in `NgForm` directive that wraps the native `HTMLFormElement` + and endows it with additional superpowers such as the ability to + track the validity of user input. + + This explains how we can disable the submit button by checking `theForm.form.valid` + and pass an object with rich information to the parent component's `onSubmit` method. + +<a id="inputs-outputs"></a> +.l-main-section +:marked + ## Input and output properties + So far, we’ve focused mainly on binding to component members within template expressions and statements + that appear on the *right side of the binding declaration*. + A member in that position is a data binding **source**. + + This section concentrates on binding to **targets**, which are directive + properties on the *left side of the binding declaration*. + These directive properties must be declared as **inputs** or **outputs**. + +.alert.is-important + :marked + Remember: All **components** are **directives**. +:marked +.l-sub-section + :marked + We're drawing a sharp distinction between a data binding **target** and a data binding **source**. + + The *target* of a binding is to the *left* of the `=`. + The *source* is on the *right* of the `=`. + + The *target* of a binding is the property or event inside the binding punctuation: `[]`, `()` or `[()]`. + The *source* is either inside quotes (`" "`) or within an interpolation (`{{}}`). + + Every member of a **source** directive is automatically available for binding. + We don't have to do anything special to access a directive member in a template expression or statement. + + We have *limited* access to members of a **target** directive. + We can only bind to properties that are explicitly identified as *inputs* and *outputs*. +:marked + In the following example, `iconUrl` and `onSave` are members of a component + that are referenced within quoted syntax to the right of the `=`. ++makeExample('template-syntax/ts/app/app.component.html', 'io-1')(format=".") +:marked + They are *neither inputs nor outputs* of the component. They are data sources for their bindings. + + Now look at `HeroDetailComponent` when it is the **target of a binding**. ++makeExample('template-syntax/ts/app/app.component.html', 'io-2')(format=".") +:marked + Both `HeroDetailComponent.hero` and `HeroDetailComponent.deleteRequest` are on the **left side** of binding declarations. + `HeroDetailComponent.hero` is inside brackets; it is the target of a property binding. + `HeroDetailComponent.deleteRequest` is inside parentheses; it is the target of an event binding. + + ### Declaring input and output properties + Target properties must be explicitly marked as inputs or outputs. + + When we peek inside `HeroDetailComponent`, we see that these properties are marked + with decorators as input and output properties. ++makeExample('template-syntax/ts/app/hero-detail.component.ts', 'input-output-1')(format=".") + +:marked +.l-sub-section + :marked + Alternatively, we can identify members in the `inputs` and `outputs` #{_array}s + of the directive metadata, as in this example: + +makeExample('template-syntax/ts/app/hero-detail.component.ts', 'input-output-2')(format=".") + <br> + :marked + We can specify an input/output property either with a decorator or in a metadata #{_array}. + Don't do both! +:marked + ### Input or output? + + *Input* properties usually receive data values. + *Output* properties expose event producers, such as `EventEmitter` objects. + + The terms _input_ and _output_ reflect the perspective of the target directive. +figure.image-display + img(src='/resources/images/devguide/template-syntax/input-output.png' alt="Inputs and outputs") +:marked + `HeroDetailComponent.hero` is an **input** property from the perspective of `HeroDetailComponent` + because data flows *into* that property from a template binding expression. + + `HeroDetailComponent.deleteRequest` is an **output** property from the perspective of `HeroDetailComponent` + because events stream *out* of that property and toward the handler in a template binding statement. + +h3#aliasing-io Aliasing input/output properties +:marked + Sometimes we want the public name of an input/output property to be different from the internal name. + + This is frequently the case with [attribute directives](attribute-directives.html). + Directive consumers expect to bind to the name of the directive. + For example, when we apply a directive with a `myClick` selector to a `<div>` tag, + we expect to bind to an event property that is also called `myClick`. ++makeExample('template-syntax/ts/app/app.component.html', 'my-click')(format=".") +:marked + However, the directive name is often a poor choice for the name of a property within the directive class. + The directive name rarely describes what the property does. + The `myClick` directive name is not a good name for a property that emits click messages. + + Fortunately, we can have a public name for the property that meets conventional expectations, + while using a different name internally. + In the example immediately above, we are actually binding *through the* `myClick` *alias* to + the directive's own `clicks` property. + + We can specify the alias for the property name by passing it into the input/output decorator like this: + ++makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-1')(format=".") + +.l-sub-section + :marked + We can also alias property names in the `inputs` and `outputs` #{_array}s. + We write a colon-delimited (`:`) string with + the directive property name on the *left* and the public alias on the *right*: + +makeExample('template-syntax/ts/app/my-click.directive.ts', 'my-click-output-2')(format=".") + +<a id="expression-operators"></a> +.l-main-section +:marked + ## Template expression operators + The template expression language employs a subset of #{_JavaScript} syntax supplemented with a few special operators + for specific scenarios. We'll cover two of these operators: _pipe_ and _safe navigation operator_. + +:marked + <a id="pipe"></a> + ### The pipe operator ( | ) + The result of an expression might require some transformation before we’re ready to use it in a binding. For example, we might want to display a number as a currency, force text to uppercase, or filter a list and sort it. + + Angular [pipes](./pipes.html) are a good choice for small transformations such as these. + Pipes are simple functions that accept an input value and return a transformed value. + They're easy to apply within template expressions, using the **pipe operator (`|`)**: ++makeExample('template-syntax/ts/app/app.component.html', 'pipes-1')(format=".") +:marked + The pipe operator passes the result of an expression on the left to a pipe function on the right. + + We can chain expressions through multiple pipes: ++makeExample('template-syntax/ts/app/app.component.html', 'pipes-2')(format=".") +:marked + And we can also [apply parameters](./pipes.html#parameterizing-a-pipe) to a pipe: ++makeExample('template-syntax/ts/app/app.component.html', 'pipes-3')(format=".") + +block json-pipe + :marked + The `json` pipe is particularly helpful for debugging our bindings: + +makeExample('template-syntax/ts/app/app.component.html', 'pipes-json')(format=".") + :marked + The generated output would look something like this + code-example(language="json"). + { "firstName": "Hercules", "lastName": "Son of Zeus", + "birthdate": "1970-02-25T08:00:00.000Z", + "url": "http://www.imdb.com/title/tt0065832/", + "rate": 325, "id": 1 } + +:marked + <a id="safe-navigation-operator"></a> + ### The safe navigation operator ( ?. ) and null property paths + + The Angular **safe navigation operator (`?.`)** is a fluent and convenient way to guard against null and undefined values in property paths. + Here it is, protecting against a view render failure if the `currentHero` is null. ++makeExample('template-syntax/ts/app/app.component.html', 'safe-2')(format=".") + +block dart-safe-nav-op + //- N/A + +:marked + Let’s elaborate on the problem and this particular solution. + + What happens when the following data bound `title` property is null? ++makeExample('template-syntax/ts/app/app.component.html', 'safe-1')(format=".") +:marked + The view still renders but the displayed value is blank; we see only "The title is" with nothing after it. + That is reasonable behavior. At least the app doesn't crash. + + Suppose the template expression involves a property path, as in this next example + where we’re displaying the `firstName` of a null hero. + +code-example(language="html"). + The null hero's name is {{nullHero.firstName}} + +block null-deref-example + :marked + JavaScript throws a null reference error, and so does Angular: + code-example(format="nocode"). + TypeError: Cannot read property 'firstName' of null in [null]. + +:marked + Worse, the *entire view disappears*. + + We could claim that this is reasonable behavior if we believed that the `hero` property must never be null. + If it must never be null and yet it is null, + we've made a programming error that should be caught and fixed. + Throwing an exception is the right thing to do. + + On the other hand, null values in the property path may be OK from time to time, + especially when we know the data will arrive eventually. + + While we wait for data, the view should render without complaint, and + the null property path should display as blank just as the `title` property does. + + Unfortunately, our app crashes when the `currentHero` is null. + + We could code around that problem with [NgIf](#ngIf). ++makeExample('template-syntax/ts/app/app.component.html', 'safe-4')(format=".") + +block safe-op-alt + :marked + Or we could try to chain parts of the property path with `&&`, knowing that the expression bails out + when it encounters the first null. + +makeExample('template-syntax/ts/app/app.component.html', 'safe-5')(format=".") + +:marked + These approaches have merit but can be cumbersome, especially if the property path is long. + Imagine guarding against a null somewhere in a long property path such as `a.b.c.d`. + + The Angular safe navigation operator (`?.`) is a more fluent and convenient way to guard against nulls in property paths. + The expression bails out when it hits the first null value. + The display is blank, but the app keeps rolling without errors. ++makeExample('template-syntax/ts/app/app.component.html', 'safe-6')(format=".") +:marked + It works perfectly with long property paths such as `a?.b?.c?.d`. + +.l-main-section +:marked + ## Summary + We’ve completed our survey of template syntax. Now it's time to put that knowledge to work as we write our own components and directives. diff --git a/public/docs/ts/_cache/quickstart.jade b/public/docs/ts/_cache/quickstart.jade new file mode 100644 index 0000000000..5de3efe99c --- /dev/null +++ b/public/docs/ts/_cache/quickstart.jade @@ -0,0 +1,582 @@ +block includes + include _util-fns + - var _Install = 'Install' + - var _prereq = 'Node.js' + - var _angular_browser_uri = '@angular/platform-browser-dynamic' + - var _angular_core_uri = '@angular/core' + +:marked + Our QuickStart goal is to build and run a super-simple + Angular 2 application in #{_Lang}, and + establish a development environment for the remaining documentation samples + that also can be the foundation for real world applications. + +.callout.is-helpful + header Don't want #{_Lang}? + p. + Although we're getting started in #{_Lang}, you can also write Angular 2 apps + in #{_docsFor == 'ts' ? 'Dart' : 'TypeScript'} and JavaScript. + Just select either of those languages from the combo-box in the banner. + +:marked + # Try it! + + Try the <live-example></live-example> which loads the sample app + <span if-docs="ts"> + in <a href="http://plnkr.co/" title="Plunker" target="_blank">plunker</a> + </span> + and displays the simple message: + +figure.image-display + img(src='/resources/images/devguide/quickstart/my-first-app.png' alt="Output of QuickStart app") + +:marked + # Build this app! + + - [Prerequisite](#prereq): Install #{_prereq} + - [Step 1](#create-and-configure): Create the app’s project folder and + define package dependencies and special project setup + - [Step 2](#root-component): Create the app’s Angular root component + - [Step 3](#main): Add <span ngio-ex>main.ts</span>, identifying the root component to Angular + - [Step 4](#index): Add `index.html`, the web page that hosts the application + - [Step 5](#build-and-run): Build and run the app + - [Make some changes to the app](#make-some-changes) + - [Wrap up](#wrap-up) + +.l-main-section +h2#prereq Prerequisite: #{_prereq} + +block setup-tooling + :marked + Install **[Node.js® and npm](https://nodejs.org/en/download/)** + if they are not already on your machine. + .l-sub-section + :marked + **Verify that you are running at least node `v4.x.x` and npm `3.x.x`** + by running `node -v` and `npm -v` in a terminal/console window. + Older versions produce errors. + +block download-source + .l-main-section + .callout.is-helpful + header Download the source + :marked + Instead of following each step of these instructions, we can + [download the QuickStart source](https://github.com/angular/quickstart/blob/master/README.md) + from github and follow its brief instructions. + +.l-main-section +button(class="verbose off md-primary md-button md-ink-ripple", type="button", onclick="verbose(false)"). + Hide explanations +button(class="verbose on md-primary md-button md-ink-ripple", type="button", onclick="verbose(true)"). + View explanations +.l-verbose-section + :marked + *Explanations* describe the concepts and reasons behind the instructions. + Explanations have a thin border on the left like *this* block of text. + + Click *Hide Explanations* to show only the instructions. + Click *View Explanations* to see everything again. + +.l-sub-section + :marked + We'll see many code blocks as we build the QuickStart app. They're all easy to copy and paste: + code-example(format="nocode"). + Click the glyph on the right to copy code snippets to the clipboard ==> + +.l-main-section +h2#create-and-configure Step 1: Create and configure the project + +- var _package_and_config_files = _docsFor == 'dart' ? 'pubspec.yaml' : 'package definition and configuration files' + +:marked + In this step we: + * [(a) Create the project folder](#create-the-project-folder) + * [(b) Add #{_package_and_config_files}](#add-config-files) + * [(c) #{_Install} packages](#install-packages) + +h3 (a) Create the project folder + +- var _ = _docsFor == 'dart' ? '_' : '-'; +code-example(language="sh"). + mkdir angular2!{_}quickstart + cd angular2!{_}quickstart + +h3#add-config-files (b) Add #{_package_and_config_files} +block package-and-config-files + - var _tsconfigUri = 'guide/typescript-configuration.html#tsconfig' + - var _typingsUri = 'guide/typescript-configuration.html#!#typings' + + p Add the following package definition and configuration files to the project folder: + ul + li. + #[b package.json] lists packages the QuickStart app depends on and + defines some useful scripts. + See #[a(href="guide/npm-packages.html") Npm Package Configuration] for details. + li. + #[b tsconfig.json] is the TypeScript compiler configuration file. + See #[a(href="#{_tsconfigUri}") TypeScript Configuration] for details. + li. + #[b typings.json] identifies TypeScript definition files. + See #[a(href="#{_typingsUri}") TypeScript Configuration] for details. + li. + #[b systemjs.config.js], the SystemJS configuration file. + See discussion #[a(href="#systemjs") below]. + + a#config-files + +makeTabs(` + quickstart/ts/package.1.json, + quickstart/ts/tsconfig.1.json, + quickstart/ts/typings.1.json, + quickstart/ts/systemjs.config.1.js + `, '', ` + package.json, + tsconfig.json, + typings.json, + systemjs.config.js + `) + +h3#install-packages (c) #{_Install} packages +block install-packages + :marked + We install the packages listed in `package.json` using `npm`. Enter the + following command in a terminal window (command window in Windows): + + code-example(language="sh"). + npm install + + .l-sub-section + :marked + The `typings` folder could not show up after `npm install`. If so, please install them manually. + + code-example(language="sh"). + npm run typings install + + .alert.is-important + :marked + Scary <span style="color:red; font-weight: bold">error messages in red</span> may appear **during** install. + The install typically recovers from these errors and finishes successfully. + .l-verbose-section(class="l-verbose-inherit") + :marked + #### npm errors and warnings + + All is well if there are no console messages starting with `npm ERR!` *at the end* of **npm install**. + There might be a few `npm WARN` messages along the way — and that is perfectly fine. + + We often see an `npm WARN` message after a series of `gyp ERR!` messages. + Ignore them. A package may try to recompile itself using `node-gyp`. + If the recompile fails, the package recovers (typically with a pre-built version) + and everything works. + + Just make sure there are no `npm ERR!` messages at the end of `npm install`. + + .l-verbose-section + :marked + #### Adding the libraries and packages we need with *npm* + Angular application developers rely on the _[npm](https://docs.npmjs.com)_ + package manager to install the libraries and packages their apps require. + The Angular team recommends the starter-set of packages specified in the + `dependencies` and `devDependencies` sections. + See the [npm packages](guide/npm-packages.html) chapter for details. + + #### Helpful scripts + We've included a number of npm scripts in our suggested `package.json` to handle common development tasks: + +makeJson('quickstart/ts/package.1.json',{ paths: 'scripts'}, 'package.json (scripts)')(format=".") + + :marked + We execute most npm scripts in the following way: `npm run` followed by a *script-name*. + Some commands (such as `start`) don't require the `run` keyword. + + Here's what these scripts do: + + * `npm start` - runs the compiler and a server at the same time, both in "watch mode" + + * `npm run tsc` - runs the TypeScript compiler once + + * `npm run tsc:w` - runs the TypeScript compiler in watch mode; + the process keeps running, awaiting changes to TypeScript files and recompiling when it sees them + + * `npm run lite` - runs the <a href="https://www.npmjs.com/package/lite-server" target="_blank">lite-server</a>, + a light-weight, static file server with excellent support for Angular apps that use routing + + * `npm run typings` - runs the [*typings* tool](#{_typingsUri}) separately + + * `npm run postinstall` - called by *npm* automatically *after* it successfully completes package installation. + This script installs the [TypeScript definition files](#{_typingsUri}) defined in `typings.json` + +:marked + **We're all set.** Let's write some code. + +.l-main-section +h2#root-component Step 2: Our first Angular component +:marked + Let's create a folder to hold our application and add a super-simple Angular component. + + **Create #{_an} #{_appDir} subfolder** off the project root directory: + +code-example. + mkdir #{_appDir} + +a#app-component +p. + #[b Create the component file] + #[code #[+adjExPath('app/app.component.ts')]] (in this newly created directory) with the following content: + ++makeExample('app/app.component.ts') + +.l-verbose-section + :marked + ### AppComponent is the root of the application + + Every Angular app has at least one **root component**, conventionally named `AppComponent`, + that hosts the client user experience. + Components are the basic building blocks of Angular applications. + A component controls a portion of the screen — a *view* — through its associated template. + + This QuickStart has only one, extremely simple component. + But it has the essential structure of every component we'll ever write: + + * One or more [import](#component-import) + statements to reference the things we need. + * A [@Component #{_decorator}](#component-decorator) + that tells Angular what template to use and how to create the component. + * A [component class](#component-class) + that controls the appearance and behavior of a view through its template. + + a#component-import + :marked + ### Import + + Angular apps are modular. They consist of many files each dedicated to a purpose. + Angular itself is modular. It is a collection of library modules + each made up of several, related features that we'll use to build our application. + + When we need something from a module or library, we import it. + Here we import the Angular 2 core so that our component code can have access to + the `@Component` #{_decorator}. + + +makeExcerpt('app/app.component.ts', 'import') + + h3#component-decorator @Component #{_decorator} + +ifDocsFor('ts') + :marked + `Component` is a *decorator function* that takes a *metadata object* as argument. + We apply this function to the component class by prefixing the function with the + **@** symbol and invoking it with a metadata object, just above the class. + :marked + `@Component` is #{_a} *#{_decorator}* that allows us to associate *metadata* with the + component class. + The metadata tells Angular how to create and use this component. + + +makeExcerpt('app/app.component.ts', 'metadata') + + block annotation-fields + :marked + This particular metadata object has two fields, a `selector` and a `template`. + :marked + The **selector** specifies a simple CSS selector for an HTML element that represents the component. + + >The element for this component is named `my-app`. + Angular creates and displays an instance of our `AppComponent` + wherever it encounters a `my-app` element in the host HTML. + + The **template** specifies the component's companion template, + written in an enhanced form of HTML that tells Angular how to render this component's view. + + >Our template is a single line of HTML announcing "*My First Angular 2 App*". + + >A more advanced template could contain data bindings to component properties + and might identify other application components which have their own templates. + These templates might identify yet other components. + In this way an Angular application becomes a tree of components. + + :marked + ### Component class + At the bottom of the file is an empty, do-nothing class named `AppComponent`. + +makeExcerpt('app/app.component.ts', 'class') + :marked + When we're ready to build a substantive application, + we can expand this class with properties and application logic. + Our `AppComponent` class is empty because we don't need it to do anything in this QuickStart. + +ifDocsFor('ts') + :marked + We **export** `AppComponent` so that we can **import** it elsewhere in our application, + as we'll see when we create `main.ts`. + +.l-main-section +h2#main Step 3: Add #[code #[+adjExPath('main.ts')]] + +block create-main + p. + Now we need something to tell Angular to load the root component. + Create the file #[code #[+adjExPath('app/main.ts')]] with the following content: + ++makeExample('app/main.ts') + +.l-verbose-section + :marked + We import the two things we need to launch the application: + + 1. Angular's browser `bootstrap` function + 1. The application root component, `AppComponent`. + + Then we call `bootstrap` with `AppComponent`. + + ### Bootstrapping is platform-specific + Notice that we import the `bootstrap` function from `#{_angular_browser_uri}`, + not `#{_angular_core_uri}`. + Bootstrapping isn't core because there isn't a single way to bootstrap the app. + True, most applications that run in a browser call the bootstrap function from + this library. + + But it is possible to load a component in a different environment. + We might load it on a mobile device with [Apache Cordova](https://cordova.apache.org/) or [NativeScript](https://www.nativescript.org/). + We might wish to render the first page of our application on the server + to improve launch performance or facilitate + [SEO](http://www.google.com/webmasters/docs/search-engine-optimization-starter-guide.pdf). + These targets require a different kind of bootstrap function that we'd import from a different library. + + ### Why create separate *<span ngio-ex>main.ts</span>* and app component files? + + Both <span ngio-ex>main.ts</span> and the app component files are tiny. + This is just a QuickStart. + We could have merged these two files into one + and spared ourselves some complexity. + + We'd rather demonstrate the proper way to structure an Angular application. + App bootstrapping is a separate concern from presenting a view. + Mixing concerns creates difficulties down the road. + We might launch the `AppComponent` in multiple environments with different bootstrappers. + Testing the component is much easier if it doesn't also try to run the entire application. + Let's make the small extra effort to do it *the right way*. + +.l-main-section +h2#index Step 4: Add #[code index.html] +:marked + In the *#{_indexHtmlDir}* folder + create an `index.html` file and paste the following lines into it: + ++makeExample('index.html') + +.l-verbose-section + :marked + The `index.html` file defines the web page that hosts the application. + + block index-html-commentary-for-ts + :marked + The noteworthy sections of HTML are: + + 1. The JavaScript [libraries](#libraries) + 2. Configuration file for [SystemJS](#systemjs), and a script + where we import and run the `app` module which refers to the `main` file that we just wrote. + 3. The [`<my-app>`](#my-app) tag in the `<body>` which is *where our app lives!* + + :marked + ### Libraries + We loaded the following scripts + +makeExcerpt('index.html', 'libraries') + :marked + We begin with `core-js`'s ES2015/ES6 shim which monkey patches the global context (window) with essential features of ES2015 (ES6). + Next are the polyfills for Angular2, `zone.js` and `reflect-metadata`. + Then the [SystemJS](#systemjs) library for module loading. + + We'll make different choices as we gain experience and + become more concerned about production qualities such as + load times and memory footprint. + + h3#systemjs SystemJS + :marked + QuickStart uses <a href="https://github.com/systemjs/systemjs" target="_blank">SystemJS</a> + to load application and library modules. [Earlier](#add-config-files) we + added the `systemjs.config.js` file to the project root. + There are alternatives that work just fine including the well-regarded + [webpack](guide/webpack.html). + SystemJS happens to be a good choice. + But we want to be clear that it was a *choice* and not a *preference*. + + All module loaders require configuration and all loader configuration + becomes complicated rather quickly as soon as the file structure diversifies and + we start thinking about building for production and performance. + + We suggest becoming well-versed in the loader of your choice. + Learn more about SystemJS configuration + <a href="https://github.com/systemjs/systemjs/blob/master/docs/config-api.md" target="_blank">here</a>. + + With those cautions in mind, what are we doing in the + QuickStart [`systemjs.config.js` configuration file we added earlier](#config-files)? + First, we create a map to tell SystemJS where to look when we import some module. + Then, we register all our packages to SystemJS: + all the project dependencies and our application package, `app`. + + .l-sub-section + :marked + Our QuickStart doesn't use all of the listed packages + but any substantial application will want many of them + and all of the listed packages are required by at least one of the documentation samples. + + There is no runtime harm in listing packages that we don't need as they will only be loaded when requested. + :marked + The `app` package tells SystemJS what to do when it sees a request for a + module from the `app/` folder. + + Our QuickStart makes such requests when one of its + application TypeScript files has an import statement like this: + +makeExcerpt('app/main.ts', 'import') + :marked + Notice that the module name (after `from`) does not mention a filename extension. + In the configuration we tell SystemJS to default the extension to `js`, a JavaScript file. + + That makes sense because we transpile TypeScript to JavaScript + *before* running the application. + + .l-sub-section + :marked + #### Transpiling in the browser + In the live example on plunker we transpile (AKA compile) to JavaScript in the browser + on the fly. _That's fine for a demo_. + + **Do not transpile in the browser during development or for production**. + + We strongly recommend transpiling (AKA compiling) to JavaScript during a build phase + before running the application for several reasons including: + + * We see compiler warnings and errors that are hidden from us in the browser. + + * Precompilation simplifies the module loading process and + it's much easier to diagnose problems when this is a separate, external step. + + * Precompilation means a faster user experience because the browser doesn't waste time compiling. + + * We iterate development faster because we only recompile changed files. + We notice the difference as soon as the app grows beyond a handful of files. + + * Precompilation fits into a continuous integration process of build, test, deploy. + + :marked + The `System.import` call tells SystemJS to import the `main` file + (`main.js` ... after transpiling `main.ts`, remember?); + `main` is where we tell Angular to launch the application. + We also catch and log launch errors to the console. + + All other modules are loaded upon request + either by an import statement or by Angular itself. + + ### *<my-app>* + + a(id="my-app") + :marked + When Angular calls the `bootstrap` function in <span ngio-ex>main.ts</span>, it reads the `AppComponent` + metadata, finds the `my-app` selector, locates an element tag named `my-app`, + and renders our application's view between those tags. + +:marked + ### Add some style + Styles aren't essential but they're nice, and `index.html` assumes we have + a stylesheet called `styles.css`. + + Create a `styles.css` file in the *#{_indexHtmlDir}* folder and start styling, perhaps with the minimal + styles shown below. For the full set of master styles used by the documentation samples, + see [styles.css](https://github.com/angular/angular.io/blob/master/public/docs/_examples/styles.css). ++makeExcerpt('styles.1.css') + +.l-main-section +h2#build-and-run Step 5: Build and run the app! +block run-app + :marked + Open a terminal window and enter this command: + code-example. + npm start + :marked + That command runs two parallel node processes + 1. The TypeScript compiler in watch mode + 1. A static server called **lite-server** that loads `index.html` in a browser + and refreshes the browser when application files change + + In a few moments, a browser tab should open and display + +figure.image-display + img(src='/resources/images/devguide/quickstart/my-first-app.png' alt="Output of QuickStart app") + +:marked + **Great job!** + +block build-app + //- Nothing for ts. + +:marked + ## Make some changes + + Try changing the message to "My SECOND Angular 2 app". +block server-watching + :marked + The TypeScript compiler and `lite-server` are watching. + They should detect the change, recompile the TypeScript into JavaScript, + refresh the browser, and display the revised message. + It's a nifty way to develop an application! + + We close the terminal window when we're done to terminate both the compiler and the server. + +.l-main-section +:marked + # Wrap up + + Our final project folder structure looks like this: +block project-file-structure + .filetree + .file angular2-quickstart + .children + .file app + .children + .file app.component.ts + .file main.ts + .file node_modules ... + .file typings ... + .file index.html + .file package.json + .file styles.css + .file systemjs.config.js + .file tsconfig.json + .file typings.json +:marked + Here are the file contents: + +block project-files + +makeTabs(` + quickstart/ts/app/app.component.ts, + quickstart/ts/app/main.ts, + quickstart/ts/index.html, + quickstart/ts/package.1.json, + quickstart/ts/tsconfig.1.json, + quickstart/ts/typings.1.json, + quickstart/ts/styles.1.css, + quickstart/ts/systemjs.config.1.js` + ,null, + `app/app.component.ts, + app/main.ts, + index.html, + package.json, + tsconfig.json, + typings.json, + styles.css, + systemjs.config.js`) + +.l-main-section +:marked + ## What next? + Our first application doesn't do much. It's basically "Hello, World" for Angular 2. + + We kept it simple in our first pass: we wrote a little Angular component, + created a simple `index.html`, and launched with a + static file server. That's about all we'd expect to do for a "Hello, World" app. + + **We have greater ambitions!** +block what-next-ts-overhead + :marked + The good news is that the overhead of setup is (mostly) behind us. + We'll probably only touch the `package.json` to update libraries. + We'll likely open `index.html` only if we need to add a library or some css stylesheets. +:marked + We're about to take the next step and build a small application that + demonstrates the great things we can build with Angular 2. + + Join us on the [Tour of Heroes Tutorial](./tutorial)! diff --git a/public/docs/ts/_cache/tutorial/toh-pt6.jade b/public/docs/ts/_cache/tutorial/toh-pt6.jade new file mode 100644 index 0000000000..c93ec199ca --- /dev/null +++ b/public/docs/ts/_cache/tutorial/toh-pt6.jade @@ -0,0 +1,632 @@ +- var _example = 'toh-6'; + +block includes + include ../_util-fns + - var _Http = 'Http'; // Angular `Http` library name. + - var _Angular_Http = 'Angular <code>Http</code>' + - var _Angular_http_library = 'Angular HTTP library' + - var _HTTP_PROVIDERS = 'HTTP_PROVIDERS' + - var _JSON_stringify = 'JSON.stringify' + +:marked + # Getting and Saving Data with HTTP + + Our stakeholders appreciate our progress. + Now they want to get the hero data from a server, let users add, edit, and delete heroes, + and save these changes back to the server. + + In this chapter we teach our application to make the corresponding HTTP calls to a remote server's web API. + + Run the <live-example></live-example> for this part. + +.l-main-section +:marked + ## Where We Left Off + In the [previous chapter](toh-pt5.html), we learned to navigate between the dashboard and the fixed heroes list, editing a selected hero along the way. + That's our starting point for this chapter. + +block start-server-and-watch + :marked + ### Keep the app transpiling and running + Open a terminal/console window and enter the following command to + start the TypeScript compiler, start the server, and watch for changes: + + code-example(language="bash"). + npm start + +:marked + The application runs and updates automatically as we continue to build the Tour of Heroes. + +.l-main-section#http-providers +h1 Providing HTTP Services +block http-library + :marked + `Http` is ***not*** a core Angular module. + It's Angular's optional approach to web access and it exists as a separate add-on module called `@angular/http`, + shipped in a separate script file as part of the Angular npm package. + + Fortunately we're ready to import from `@angular/http` because `systemjs.config` configured *SystemJS* to load that library when we need it. + +:marked + ### Register (provide) *HTTP* services + +block http-providers + :marked + Our app will depend upon the Angular `http` service which itself depends upon other supporting services. + The `HTTP_PROVIDERS` array from `@angular/http` library holds providers for the complete set of http services. + +:marked + We should be able to access `!{_Http}` services from anywhere in the application. + So we register them in the `bootstrap` call of <span ngio-ex>main.ts</span> where we + launch the application and its root `AppComponent`. + ++makeExcerpt('app/main.ts','v1') + +:marked + Notice that we supply `!{_HTTP_PROVIDERS}` in !{_an} !{_array} as the second parameter to the `bootstrap` method. + This has the same effect as the `providers` !{_array} in `@Component` !{_decorator}. + +.l-main-section +:marked + ## Simulating the web API + + We generally recommend registering application-wide services in the root `AppComponent` *providers*. + Here we're registering in `main` for a special reason. + + Our application is in the early stages of development and far from ready for production. + We don't even have a web server that can handle requests for heroes. + Until we do, *we'll have to fake it*. + + We're going to *trick* the HTTP client into fetching and saving data from + a mock service, the *in-memory web API*. + + The application itself doesn't need to know and shouldn't know about this. + So we'll slip the in-memory web API into the configuration *above* the `AppComponent`. + + Here is a version of `main` that performs this trick ++makeExcerpt('app/main.ts', 'final') + +block backend + :marked + We're replacing the default `XHRBackend`, the service that talks to the remote server, + with the in-memory web API service after priming it as follows: + ++makeExample('app/in-memory-data.service.ts', 'init') + +p This file replaces the #[code #[+adjExPath('mock-heroes.ts')]] which is now safe to delete. + +block dont-be-distracted-by-backend-subst + .alert.is-helpful + :marked + This chapter is an introduction to the !{_Angular_http_library}. + Please don't be distracted by the details of this backend substitution. Just follow along with the example. + + Learn more later about the in-memory web API in the [HTTP client chapter](../guide/server-communication.html#!#in-mem-web-api). + Remember, the in-memory web API is only useful in the early stages of development and for demonstrations such as this Tour of Heroes. + Skip it when you have a real web API server. + +.l-main-section +:marked + ## Heroes and HTTP + + Look at our current `HeroService` implementation + ++makeExcerpt('toh-4/ts/app/hero.service.ts (old getHeroes)', 'get-heroes') + +:marked + We returned a !{_Promise} resolved with mock heroes. + It may have seemed like overkill at the time, but we were anticipating the + day when we fetched heroes with an HTTP client and we knew that would have to be an asynchronous operation. + + That day has arrived! Let's convert `getHeroes()` to use HTTP: + ++makeExcerpt('app/hero.service.ts (new constructor and revised getHeroes)', 'getHeroes') + +:marked + ### HTTP !{_Promise} + + We're still returning a !{_Promise} but we're creating it differently. + +block get-heroes-details + :marked + The Angular `http.get` returns an RxJS `Observable`. + *Observables* are a powerful way to manage asynchronous data flows. + We'll learn about [Observables](#observables) later in this chapter. + + For *now* we get back on familiar ground by immediately by + converting that `Observable` to a `Promise` using the `toPromise` operator. + +makeExcerpt('app/hero.service.ts', 'to-promise', '') + :marked + Unfortunately, the Angular `Observable` doesn't have a `toPromise` operator ... not out of the box. + The Angular `Observable` is a bare-bones implementation. + + There are scores of operators like `toPromise` that extend `Observable` with useful capabilities. + If we want those capabilities, we have to add the operators ourselves. + That's as easy as importing them from the RxJS library like this: + +makeExcerpt('app/hero.service.ts', 'rxjs', '') + + :marked + ### Extracting the data in the *then* callback + In the *promise*'s `then` callback we call the `json` method of the http `Response` to extract the + data within the response. + +makeExcerpt('app/hero.service.ts', 'to-data', '') + +:marked + That response JSON has a single `data` property. + The `data` property holds the !{_array} of *heroes* that the caller really wants. + So we grab that !{_array} and return it as the resolved !{_Promise} value. + +.alert.is-important + :marked + Pay close attention to the shape of the data returned by the server. + This particular *in-memory web API* example happens to return an object with a `data` property. + Your API might return something else. + + Adjust the code to match *your web API*. +:marked + The caller is unaware of these machinations. It receives a !{_Promise} of *heroes* just as it did before. + It has no idea that we fetched the heroes from the (mock) server. + It knows nothing of the twists and turns required to convert the HTTP response into heroes. + Such is the beauty and purpose of delegating data access to a service like this `HeroService`. +:marked + ### Error Handling + + At the end of `getHeroes()` we `catch` server failures and pass them to an error handler: + ++makeExcerpt('app/hero.service.ts', 'catch', '') + +:marked + This is a critical step! + We must anticipate HTTP failures as they happen frequently for reasons beyond our control. + ++makeExcerpt('app/hero.service.ts', 'handleError', '') + +- var rejected_promise = _docsFor == 'dart' ? 'propagated exception' : 'rejected promise'; +:marked + In this demo service we log the error to the console; we should do better in real life. + + We've also decided to return a user friendly form of the error to + the caller in a !{rejected_promise} so that the caller can display a proper error message to the user. + + ### !{_Promise}s are !{_Promise}s + Although we made significant *internal* changes to `getHeroes()`, the public signature did not change. + We still return a !{_Promise}. We won't have to update any of the components that call `getHeroes()`. + +.l-main-section +:marked + ## Add, Edit, Delete + + Our stakeholders are incredibly pleased with the added flexibility from the API integration, but it doesn't stop there. Next we want to add the capability to add, edit and delete heroes. + + We'll complete `HeroService` by creating `post`, `put` and `delete` methods to meet our new requirements. + +:marked + ### Post + + We will be using `post` to add new heroes. Post requests require a little bit more setup than Get requests: + ++makeExcerpt('app/hero.service.ts', 'post') + +:marked + For Post requests we create a header and set the content type to `application/json`. We'll call `!{_JSON_stringify}` before we post to convert the hero object to a string. + + ### Put + + Put will be used to update an individual hero. Its structure is very similar to Post requests. The only difference is that we have to change the URL slightly by appending the id of the hero we want to update. + ++makeExcerpt('app/hero.service.ts', 'put') + +:marked + ### Delete + Delete will be used to delete heroes and its format is like `put` except for the function name. + ++makeExcerpt('app/hero.service.ts', 'delete') + +:marked + We add a `catch` to handle errors for all three methods. + +:marked + ### Save + + We combine the call to the private `post` and `put` methods in a single `save` method. This simplifies the public API and makes the integration with `HeroDetailComponent` easier. `HeroService` determines which method to call based on the state of the `hero` object. If the hero already has an id we know it's an edit. Otherwise we know it's an add. + ++makeExcerpt('app/hero.service.ts', 'save') + +:marked + After these additions our `HeroService` looks like this: + ++makeExample('app/hero.service.ts') + +.l-main-section +:marked + ## Updating Components + + Loading heroes using `Http` required no changes outside of `HeroService`, but we added a few new features as well. + In the following section we will update our components to use our new methods to add, edit and delete heroes. + +block hero-detail-comp-extra-imports-and-vars + :marked + Before we can add those methods, we need to initialize some variables with their respective imports. + + +makeExcerpt('app/hero-detail.component.ts ()', 'variables-imports') + +block hero-detail-comp-updates + :marked + ### Add/Edit in the *HeroDetailComponent* + + We already have `HeroDetailComponent` for viewing details about a specific hero. + Add and Edit are natural extensions of the detail view, so we are able to reuse `HeroDetailComponent` with a few tweaks. + + The original component was created to render existing data, but to add new data we have to initialize the `hero` property to an empty `Hero` object. + + +makeExcerpt('app/hero-detail.component.ts', 'ngOnInit') + + :marked + In order to differentiate between add and edit we are adding a check to see if an id is passed in the URL. If the id is absent we bind `HeroDetailComponent` to an empty `Hero` object. In either case, any edits made through the UI will be bound back to the same `hero` property. + +:marked + Add a save method to `HeroDetailComponent` and call the corresponding save method in `HeroesService`. + ++makeExcerpt('app/hero-detail.component.ts', 'save') + +block hero-detail-comp-save-and-goback + :marked + The same save method is used for both add and edit since `HeroService` will know when to call `post` vs `put` based on the state of the `Hero` object. + + After we save a hero, we redirect the browser back to the previous page using the `goBack()` method. + + +makeExcerpt('app/hero-detail.component.ts', 'goBack') + + :marked + Here we call `emit` to notify that we just added or modified a hero. `HeroesComponent` is listening for this notification and will automatically refresh the list of heroes to include our recent updates. + + .l-sub-section + :marked + The `emit` "handshake" between `HeroDetailComponent` and `HeroesComponent` is an example of component to component communication. This is a topic for another day, but we have detailed information in our <a href="/docs/ts/latest/cookbook/component-communication.html#!#child-to-parent">Component Interaction Cookbook</a> + +:marked + Here is `HeroDetailComponent` with its new save button and the corresponding HTML. + +figure.image-display + img(src='/resources/images/devguide/toh/hero-details-save-button.png' alt="Hero Details With Save Button") + ++makeExcerpt('app/hero-detail.component.html', 'save') + +:marked + ### Add/Delete in the *HeroesComponent* + + We'll be reporting propagated HTTP errors, let's start by adding the following + field to the `HeroesComponent` class: + ++makeExcerpt('app/heroes.component.ts', 'error', '') + +:marked + The user can *add* a new hero by clicking a button and entering a name. + +block add-new-hero-via-detail-comp + :marked + When the user clicks the *Add New Hero* button, we display the `HeroDetailComponent`. + We aren't navigating to the component so it won't receive a hero `id`; + as we noted above, that is the component's cue to create and present an empty hero. + +- var _below = _docsFor == 'dart' ? 'before' : 'below'; +:marked + Add the following to the heroes component HTML, just !{_below} the hero list (`<ul class="heroes">...</ul>`). ++makeExcerpt('app/heroes.component.html', 'add-and-error') +:marked + The first line will display an error message if there is any. The remaining HTML is for adding heroes. + + The user can *delete* an existing hero by clicking a delete button next to the hero's name. + Add the following to the heroes component HTML right after the hero name in the repeated `<li>` tag: ++makeExcerpt('app/heroes.component.html', 'delete') +:marked + Add the following to the bottom of the `HeroesComponent` CSS file: ++makeExcerpt('app/heroes.component.css', 'additions') +:marked + Now let's fix-up the `HeroesComponent` to support the *add* and *delete* actions used in the template. + Let's start with *add*. + +block heroes-comp-directives + :marked + We're using the `HeroDetailComponent` to capture the new hero information. + We have to tell Angular about that by importing the `HeroDetailComponent` and referencing it in the component metadata `directives` array. + +makeExcerpt('app/heroes.component.ts (HeroDetailComponent)', 'hero-detail-component') + .l-sub-section + :marked + These are the same lines that we removed in the previous [Routing](toh-pt5.html) chapter. + We didn't know at the time that we'd need the *HeroDetailComponent* again. So we tidied up. + + Now we *must* put these lines back. If we don't, Angular will ignore the `<my-hero-detail>` + tag and pushing the *Add New Hero* button will have no visible effect. +:marked + Implement the click handler for the *Add New Hero* button. + ++makeExcerpt('app/heroes.component.ts', 'addHero') + +block heroes-comp-add + :marked + The `HeroDetailComponent` does most of the work. All we do is toggle an `*ngIf` flag that + swaps it into the DOM when we add a hero and removes it from the DOM when the user is done. + +:marked + The *delete* logic is a bit trickier. ++makeExcerpt('app/heroes.component.ts', 'deleteHero') + +:marked + Of course we delegate the persistence of hero deletion to the `HeroService`. + But the component is still responsible for updating the display. + So the *delete* method removes the deleted hero from the list. + +block review + :marked + ### Let's see it + Here are the fruits of labor in action: + figure.image-display + img(src='/resources/images/devguide/toh/toh-http.anim.gif' alt="Heroes List Editing w/ HTTP") + +:marked + ## !{_Observable}s + +block observables-section-intro + :marked + Each `Http` method returns an `Observable` of HTTP `Response` objects. + + Our `HeroService` converts that `Observable` into a `Promise` and returns the promise to the caller. + In this section we learn to return the `Observable` directly and discuss when and why that might be + a good thing to do. + + ### Background + An *observable* is a stream of events that we can process with array-like operators. + + Angular core has basic support for observables. We developers augment that support with + operators and extensions from the [RxJS Observables](http://reactivex.io/rxjs/) library. + We'll see how shortly. + + Recall that our `HeroService` quickly chained the `toPromise` operator to the `Observable` result of `http.get`. + That operator converted the `Observable` into a `Promise` and we passed that promise back to the caller. + + Converting to a promise is often a good choice. We typically ask `http` to fetch a single chunk of data. + When we receive the data, we're done. + A single result in the form of a promise is easy for the calling component to consume + and it helps that promises are widely understood by JavaScript programmers. + +:marked + But requests aren't always "one and done". We may start one request, + then cancel it, and make a different request before the server has responded to the first request. + Such a _request-cancel-new-request_ sequence is difficult to implement with *!{_Promise}s*. + It's easy with *!{_Observable}s* as we'll see. + + ### Search-by-name + We're going to add a *hero search* feature to the Tour of Heroes. + As the user types a name into a search box, we'll make repeated HTTP requests for heroes filtered by that name. + + We start by creating `HeroSearchService` that sends search queries to our server's web api. + ++makeExample('app/hero-search.service.ts') + +:marked + The `!{_priv}http.get()` call in `HeroSearchService` is similar to the one + in the `HeroService`, although the URL now has a query string. + <span if-docs="ts">Another notable difference: we no longer call `toPromise`, + we simply return the *observable* instead.</span> + + ### HeroSearchComponent + + Let's create a new `HeroSearchComponent` that calls this new `HeroSearchService`. + + The component template is simple — just a text box and a list of matching search results. + ++makeExample('app/hero-search.component.html') +:marked + We'll also want to add styles for the new component. ++makeExample('app/hero-search.component.css') +:marked + As the user types in the search box, a *keyup* event binding calls the component's `search` method with the new search box value. + + The `*ngFor` repeats *hero* objects from the component's `heroes` property. No surprise there. + + But, as we'll soon see, the `heroes` property is now !{_an} *!{_Observable}* of hero !{_array}s, rather than just a hero !{_array}. + The `*ngFor` can't do anything with !{_an} `!{_Observable}` until we flow it through the `async` pipe (`AsyncPipe`). + The `async` pipe subscribes to the `!{_Observable}` and produces the !{_array} of heroes to `*ngFor`. + + Time to create the `HeroSearchComponent` class and metadata. + ++makeExample('app/hero-search.component.ts') + +:marked + #### Search terms + + Let's focus on the `!{_priv}searchTerms`: + ++makeExcerpt('app/hero-search.component.ts', 'searchTerms', '') + +block search-criteria-intro + :marked + A `Subject` is a producer of an _observable_ event stream; + `searchTerms` produces an `Observable` of strings, the filter criteria for the name search. + + Each call to `search` puts a new string into this subject's _observable_ stream by calling `next`. + +:marked + <a id="ngoninit"></a> + #### Initialize the _**heroes**_ property (_**ngOnInit**_) + + <span if-docs="ts">A `Subject` is also an `Observable`.</span> + We're going to turn the stream + of search terms into a stream of `Hero` !{_array}s and assign the result to the `heroes` property. + ++makeExcerpt('app/hero-search.component.ts', 'search', '') + +:marked + If we passed every user keystroke directly to the `HeroSearchService`, we'd unleash a storm of HTTP requests. + Bad idea. We don't want to tax our server resources and burn through our cellular network data plan. + +block observable-transformers + :marked + Fortunately, we can chain `Observable` operators to the string `Observable` that reduce the request flow. + We'll make fewer calls to the `HeroSearchService` and still get timely results. Here's how: + + * `debounceTime(300)` waits until the flow of new string events pauses for 300 milliseconds + before passing along the latest string. We'll never make requests more frequently than 300ms. + + * `distinctUntilChanged` ensures that we only send a request if the filter text changed. + There's no point in repeating a request for the same search term. + + * `switchMap` calls our search service for each search term that makes it through the `debounce` and `distinctUntilChanged` gauntlet. + It cancels and discards previous search observables, returning only the latest search service observable. + + .l-sub-section + :marked + The [switchMap operator](https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/flatmaplatest.md) + (formerly known as "flatMapLatest") is very clever. + + Every qualifying key event can trigger an http call. + Even with a 300ms pause between requests, we could have multiple http requests in flight + and they may not return in the order sent. + + `switchMap` preserves the original request order while returning + only the observable from the most recent http call. + Results from prior calls are canceled and discarded. + + We also short-circuit the http call and return an observable containing an empty array + if the search text is empty. + + Note that _canceling_ the `HeroSearchService` observable won't actually abort a pending http request + until the service supports that feature, a topic for another day. + We are content for now to discard unwanted results. + :marked + * `catch` intercepts a failed observable. + Our simple example prints the error to the console; a real life application should do better. + Then we return an observable containing an empty array to clear the search result. + + ### Import RxJS operators + The RxJS operators are not available in Angular's base `Observable` implementation. + We have to extend `Observable` by *importing* them. + + We could extend `Observable` with just the operators we need here by + including the pertinent `import` statements at the top of this file. + + .l-sub-section + :marked + Many authorities say we should do just that. + :marked + We take a different approach in this example. + We combine all of the RxJS `Observable` extensions that _our entire app_ requires into a single RxJS imports file. + + +makeExample('app/rxjs-extensions.ts') + + :marked + We load them all at once by importing `rxjs-extensions` in `AppComponent`. + + +makeExcerpt('app/app.component.ts', 'rxjs-extensions') + +:marked + ### Add the search component to the dashboard + + We add the hero search HTML element to the bottom of the `DashboardComponent` template. + ++makeExample('app/dashboard.component.html') + +:marked + And finally, we import the `HeroSearchComponent` and add it to the `directives` !{_array}. + ++makeExcerpt('app/dashboard.component.ts', 'search') + +:marked + Run the app again, go to the *Dashboard*, and enter some text in the search box. + At some point it might look like this. + +figure.image-display + img(src='/resources/images/devguide/toh/toh-hero-search.png' alt="Hero Search Component") + +.l-main-section +:marked + ## Application structure and code + + Review the sample source code in the <live-example></live-example> for this chapter. + Verify that we have the following structure: + +block filetree + .filetree + .file angular2-tour-of-heroes + .children + .file app + .children + .file app.component.ts + .file app.component.css + .file app.routes.ts + .file dashboard.component.css + .file dashboard.component.html + .file dashboard.component.ts + .file hero.ts + .file hero-detail.component.css + .file hero-detail.component.html + .file hero-detail.component.ts + .file hero-search.component.html (new) + .file hero-search.component.css (new) + .file hero-search.component.ts (new) + .file hero-search.service.ts (new) + .file rxjs-operators.ts + .file hero.service.ts + .file heroes.component.css + .file heroes.component.html + .file heroes.component.ts + .file main.ts + .file in-memory-data.service.ts (new) + .file node_modules ... + .file typings ... + .file index.html + .file package.json + .file styles.css + .file systemjs.config.json + .file tsconfig.json + .file typings.json + +.l-main-section +:marked + ## Home Stretch + + We are at the end of our journey for now, but we have accomplished a lot. + - We added the necessary dependencies to use HTTP in our application. + - We refactored `HeroService` to load heroes from a web API. + - We extended `HeroService` to support post, put and delete methods. + - We updated our components to allow adding, editing and deleting of heroes. + - We configured an in-memory web API. + - We learned how to use !{_Observable}s. + + Here are the files we added or changed in this chapter. + +block file-summary + +makeTabs( + `toh-6/ts/app/app.component.ts, + toh-6/ts/app/heroes.component.ts, + toh-6/ts/app/heroes.component.html, + toh-6/ts/app/heroes.component.css, + toh-6/ts/app/hero-detail.component.ts, + toh-6/ts/app/hero-detail.component.html, + toh-6/ts/app/hero.service.ts, + toh-6/ts/app/in-memory-data.service.ts`, + null, + `app.comp...ts, + heroes.comp...ts, + heroes.comp...html, + heroes.comp...css, + hero-detail.comp...ts, + hero-detail.comp...html, + hero.service.ts, + in-memory-data.service.ts` + ) + + +makeTabs( + `toh-6/ts/app/hero-search.service.ts, + toh-6/ts/app/hero-search.component.ts, + toh-6/ts/app/hero-search.component.html, + toh-6/ts/app/hero-search.component.css, + toh-6/ts/app/rxjs-operators.ts`, + null, + `hero-search.service.ts, + hero-search.component.ts, + hero-search.service.html, + hero-search.component.css, + rxjs-operators.ts` + ) diff --git a/scripts/refresh-cache.sh b/scripts/refresh-cache.sh new file mode 100755 index 0000000000..dccd79016f --- /dev/null +++ b/scripts/refresh-cache.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +set -e -o pipefail + +BASE="public/docs/ts" +LATEST="$BASE/latest" +CACHE="$BASE/_cache" + +FILES=" +guide/architecture.jade +guide/attribute-directives.jade +guide/component-styles.jade +guide/dependency-injection.jade +guide/displaying-data.jade +guide/hierarchical-dependency-injection.jade +guide/lifecycle-hooks.jade +guide/pipes.jade +guide/security.jade +guide/server-communication.jade +guide/structural-directives.jade +guide/template-syntax.jade +quickstart.jade +tutorial/toh-pt6.jade" + +function main() { + local allFound=true; + + for f in $FILES; do + local srcPath="$LATEST/$f"; + local destPath="$CACHE/$f"; + local destDir=`dirname $destPath`; + if [[ -e $srcPath ]]; then + [[ -d "$destDir" ]] || (set -x; mkdir $destDir); + (set -x; cp $srcPath $destPath) + else + echo Cannot find $srcPath + allFound=false; + fi + done + + [[ $allFound ]] || exit 1; +} + +main; From 841db9e817642cc80c50756a9a7807461ccc40c6 Mon Sep 17 00:00:00 2001 From: Patrice Chalin <chalin@users.noreply.github.com> Date: Tue, 9 Aug 2016 10:41:05 -0700 Subject: [PATCH 07/21] docs(toh-6): show provider to use with a real back end (#2059) Fixes #2055. --- public/docs/_examples/toh-6/dart/web/main.dart | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/public/docs/_examples/toh-6/dart/web/main.dart b/public/docs/_examples/toh-6/dart/web/main.dart index 1075856c61..ff344d1a54 100644 --- a/public/docs/_examples/toh-6/dart/web/main.dart +++ b/public/docs/_examples/toh-6/dart/web/main.dart @@ -10,7 +10,10 @@ import 'package:angular2_tour_of_heroes/in_memory_data_service.dart'; void main() { bootstrap(AppComponent, - const [const Provider(Client, useClass: InMemoryDataService)]); + [provide(Client, useClass: InMemoryDataService)] + // Using a real back end? Import browser_client.dart and change the above to + // [provide(Client, useFactory: () => new BrowserClient(), deps: [])] + ); } // #enddocregion final /* @@ -23,7 +26,7 @@ void main() { ]); // Simplify bootstrap provider list to [BrowserClient] // once there is a fix for: - // https://github.com/angular/angular/issues/9673 + // https://github.com/dart-lang/angular2/issues/37 } // #enddocregion v1 */ From ef1171bd36b433533507182fe7affb6ac8baf28a Mon Sep 17 00:00:00 2001 From: Benjamin Dopplinger <b.dopplinger@gmail.com> Date: Wed, 10 Aug 2016 03:43:42 +1000 Subject: [PATCH 08/21] docs(toh-pt5/dart): fix grammar mistakes (#2057) --- public/docs/dart/latest/tutorial/toh-pt5.jade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/docs/dart/latest/tutorial/toh-pt5.jade b/public/docs/dart/latest/tutorial/toh-pt5.jade index ae8c6ef823..8c24c76abf 100644 --- a/public/docs/dart/latest/tutorial/toh-pt5.jade +++ b/public/docs/dart/latest/tutorial/toh-pt5.jade @@ -403,7 +403,7 @@ code-example(format=''). We import the `OnInit` interface because we'll call the `HeroService` inside the `ngOnInit` component lifecycle hook. +makeExample('toh-5/dart/lib/hero_detail_component.dart', 'import-oninit')(format=".") :marked - We inject the both the `RouteParams` service and the `HeroService` into the constructor as we've done before, + We inject both the `RouteParams` service and the `HeroService` into the constructor as we've done before, making private variables for both: +makeExample('toh-5/dart/lib/hero_detail_component.dart', 'ctor', 'lib/hero_detail_component.dart (constructor)')(format=".") :marked @@ -584,7 +584,7 @@ figure.image-display :marked .l-sub-section :marked - The `styleUrls` property is an list of style file names (with paths). + The `styleUrls` property is a list of style file names (with paths). We could list multiple style files from different locations if we needed them. As with `templateUrl`, we must specify the path _all the way back to the application root_. :marked From 2fb503dabfbbb74ae88f27428dc3b3af15349e6b Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin <pete@bacondarwin.com> Date: Tue, 9 Aug 2016 17:38:25 +0100 Subject: [PATCH 09/21] doc-gen: fix filterable API list Closes #2054 --- tools/api-builder/angular.io-package/index.js | 2 +- .../processors/addJadeDataDocsProcessor.js | 17 +++++++++-- .../addJadeDataDocsProcessor.spec.js | 29 ++++++++++++------- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/tools/api-builder/angular.io-package/index.js b/tools/api-builder/angular.io-package/index.js index 054d7837f7..493ea7f1be 100644 --- a/tools/api-builder/angular.io-package/index.js +++ b/tools/api-builder/angular.io-package/index.js @@ -138,7 +138,7 @@ module.exports = new Package('angular.io', [basePackage, targetPackage, cheatshe }); computePathsProcessor.pathTemplates.push({ - docTypes: ['api-list-data'], + docTypes: ['api-list-audit'], pathTemplate: 'api-list-audit.json', outputPathTemplate: '${path}' }); diff --git a/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.js b/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.js index b3fde862f5..a7caee0555 100644 --- a/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.js +++ b/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.js @@ -26,15 +26,26 @@ module.exports = function addJadeDataDocsProcessor() { $process: function(docs) { var extraDocs = []; var modules = []; + var data = {}; var appDataDoc = { id: 'api-list-data', aliases: ['api-list-data'], docType: 'api-list-data', - data: {} + data: data }; extraDocs.push(appDataDoc); + // create additional doc for auditing + var appDataAuditDoc = { + id: 'api-list-audit', + aliases: ['api-list-audit'], + docType: 'api-list-audit', + data: data + }; + extraDocs.push(appDataAuditDoc); + + /* * Create Data for Modules * @@ -76,7 +87,7 @@ module.exports = function addJadeDataDocsProcessor() { stability = 'deprecated'; exportDoc.showDeprecatedNotes = true; } - + var howToUse = ''; if(_.has(exportDoc, 'howToUse')) { var howToUseArray = exportDoc.tags.tags.filter(function(tag) { @@ -135,7 +146,7 @@ module.exports = function addJadeDataDocsProcessor() { doc.childPages = modulePageInfo; // ADD TO APP DATA DOC - appDataDoc.data[doc.id] = modulePageInfo; + data[doc.id] = modulePageInfo; // COMBINE WITH INDEX PAGE DATA var allPageData = indexPageInfo.concat(modulePageInfo); diff --git a/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.spec.js b/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.spec.js index 0423be79e1..e5f5daa60d 100644 --- a/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.spec.js +++ b/tools/api-builder/angular.io-package/processors/addJadeDataDocsProcessor.spec.js @@ -32,8 +32,18 @@ describe('addJadeDataDocsProcessor', function() { originalDoc : docs[0], data : [ { name : 'index', title : 'X Y', intro : 'some description second line', docType : 'module' }, - { name : 'someObj-var', title : 'someObj', varType : 'MyClass', docType: 'var', - originalModule: 'some/private/module', exportDoc: exportDoc } + { + name : 'someObj-var', + title : 'someObj', + varType : 'MyClass', + docType: 'var', + originalModule: 'some/private/module', + exportDoc: exportDoc, + stability : '', + howToUse : '', + whatItDoes : '', + security : false + } ] }); }); @@ -56,14 +66,13 @@ describe('addJadeDataDocsProcessor', function() { ]; docs = processor.$process(docs); - expect(docs[2].data).toEqual([ - { name : 'index', title : 'X Y', intro : 'some description second line', docType : 'module' }, - { name: 'Alpha-class', title: 'Alpha', docType: 'class', exportDoc: alpha }, - { name: 'Beta-class', title: 'Beta', docType: 'class', exportDoc: beta }, - { name: 'Gamma-class', title: 'Gamma', docType: 'class', exportDoc: gamma }, - { name: 'Mu-class', title: 'Mu', docType: 'class', exportDoc: mu }, - { name: 'Nu-class', title: 'Nu', docType: 'class', exportDoc: nu } - ]); + expect(docs[2].data).toEqual({ someModule : [ + { name: 'Alpha-class', title: 'Alpha', docType: 'class', exportDoc: alpha, stability : '', howToUse : '', whatItDoes : '', security : false }, + { name: 'Beta-class', title: 'Beta', docType: 'class', exportDoc: beta, stability : '', howToUse : '', whatItDoes : '', security : false }, + { name: 'Gamma-class', title: 'Gamma', docType: 'class', exportDoc: gamma, stability : '', howToUse : '', whatItDoes : '', security : false }, + { name: 'Mu-class', title: 'Mu', docType: 'class', exportDoc: mu, stability : '', howToUse : '', whatItDoes : '', security : false }, + { name: 'Nu-class', title: 'Nu', docType: 'class', exportDoc: nu, stability : '', howToUse : '', whatItDoes : '', security : false } + ]}); }); }); From d79adb2422907f9cc652b570db6d48f9a080cca3 Mon Sep 17 00:00:00 2001 From: Peter Bacon Darwin <pete@bacondarwin.com> Date: Tue, 9 Aug 2016 17:38:25 +0100 Subject: [PATCH 10/21] docs(rc5): update docs and plunkers for rc5 --- gulpfile.js | 2 +- package.json | 2 +- .../_examples/animations/ts/app/app.module.ts | 33 + .../ts/app/hero-team-builder.component.ts | 22 - .../docs/_examples/animations/ts/app/main.ts | 7 +- .../architecture/ts/app/app.component.ts | 9 +- .../architecture/ts/app/app.module.ts | 36 ++ .../ts/app/hero-detail.component.ts | 3 +- .../ts/app/hero-list.component.ts | 9 +- .../_examples/architecture/ts/app/main.ts | 14 +- .../_examples/architecture/ts/app/mini-app.ts | 45 ++ .../ts/app/sales-tax.component.ts | 2 +- .../ts/mini-app.html} | 11 +- .../ts/app/app.component.ts | 6 +- .../attribute-directives/ts/app/app.module.ts | 16 + .../attribute-directives/ts/app/main.ts | 8 +- .../ts/app/app.component.ts | 5 - .../ts/app/app.module.1.ts | 12 + .../ts/app/app.module.ts | 23 + .../ts/app/app.routes.ts | 8 +- .../cb-a1-a2-quick-reference/ts/app/main.1.ts | 5 - .../cb-a1-a2-quick-reference/ts/app/main.ts | 9 +- .../ts/app/movie-list.component.ts | 7 +- .../ts/app/app.component.ts | 27 +- .../ts/app/app.module.ts | 48 ++ .../ts/app/countdown-parent.component.ts | 2 - .../ts/app/hero-parent.component.ts | 4 +- .../cb-component-communication/ts/app/main.ts | 6 +- .../ts/app/missioncontrol.component.ts | 2 - .../ts/app/name-parent.component.ts | 5 +- .../ts/app/version-parent.component.ts | 5 +- .../ts/app/votetaker.component.ts | 5 +- .../ts/app/app.module.ts | 15 + .../ts/app/main.ts | 6 +- .../ts/app/app.component.ts | 17 - .../ts/app/app.module.ts | 74 +++ .../ts/app/hero-bios.component.ts | 4 - .../cb-dependency-injection/ts/app/main.ts | 23 +- .../ts/app/parent-finder.component.ts | 17 +- .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 .../cb-dynamic-form-deprecated/ts/app/main.ts | 3 +- .../cb-dynamic-form/ts/app/app.component.ts | 2 - .../cb-dynamic-form/ts/app/app.module.ts | 18 + .../ts/app/dynamic-form-question.component.ts | 5 +- .../ts/app/dynamic-form.component.ts | 10 +- .../_examples/cb-dynamic-form/ts/app/main.ts | 12 +- .../ts/app/app.module.ts | 19 + .../cb-set-document-title/ts/app/main.ts | 22 +- .../hero-di-inject-additional.component.js | 18 +- .../js/app/hero-di-inject.component.js | 22 +- .../js/app/hero-di-inline.component.js | 13 +- .../cb-ts-to-js/js/app/hero-di.component.js | 11 + .../cb-ts-to-js/js/app/hero-dsl.component.js | 10 +- .../cb-ts-to-js/js/app/hero-io.component.js | 17 +- .../js/app/hero-lifecycle.component.js | 10 +- .../cb-ts-to-js/js/app/hero.component.js | 10 + .../js/app/heroes-bindings.component.js | 11 +- .../js/app/heroes-queries.component.js | 25 +- .../docs/_examples/cb-ts-to-js/js/app/main.js | 29 +- .../hero-di-inject-additional.component.ts | 21 +- .../ts/app/hero-di-inject.component.ts | 13 +- .../cb-ts-to-js/ts/app/hero-di.component.ts | 13 +- .../cb-ts-to-js/ts/app/hero-io.component.ts | 27 +- .../ts/app/hero-lifecycle.component.ts | 17 +- .../cb-ts-to-js/ts/app/hero.component.ts | 12 + .../ts/app/heroes-bindings.component.ts | 19 +- .../ts/app/heroes-queries.component.ts | 23 +- .../docs/_examples/cb-ts-to-js/ts/app/main.ts | 39 +- .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 .../component-styles/ts/app/app.module.ts | 23 + .../ts/app/hero-app-main.component.ts | 6 +- .../ts/app/hero-app.component.ts | 4 +- .../ts/app/hero-details.component.ts | 4 +- .../_examples/component-styles/ts/app/main.ts | 7 +- .../ts/app/app.component.1.ts | 6 +- .../ts/app/app.component.2.ts | 17 +- .../ts/app/app.component.ts | 22 +- .../dependency-injection/ts/app/app.module.ts | 36 ++ .../ts/app/heroes/hero-list.component.2.ts | 2 +- .../ts/app/heroes/heroes.component.1.ts | 19 +- .../ts/app/heroes/heroes.component.ts | 4 +- .../dependency-injection/ts/app/main.1.ts | 13 - .../dependency-injection/ts/app/main.ts | 8 +- .../displaying-data/ts/app/app.component.3.ts | 1 + .../displaying-data/ts/app/app.component.ts | 1 + .../displaying-data/ts/app/app.module.ts | 16 + .../_examples/displaying-data/ts/app/main.ts | 6 +- .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 .../_examples/forms/js/app/app.component.js | 3 +- .../docs/_examples/forms/js/app/app.module.js | 19 + .../forms/js/app/hero-form.component.js | 4 +- public/docs/_examples/forms/js/app/main.js | 7 +- public/docs/_examples/forms/js/index.html | 1 + .../_examples/forms/ts/app/app.component.ts | 6 +- .../docs/_examples/forms/ts/app/app.module.ts | 20 + .../forms/ts/app/hero-form.component.html | 67 +- .../forms/ts/app/hero-form.component.ts | 3 +- public/docs/_examples/forms/ts/app/main.ts | 15 +- public/docs/_examples/forms/ts/index.html | 3 +- .../ts/app/app.module.ts | 43 ++ .../ts/app/heroes-list.component.ts | 5 +- .../ts/app/main.ts | 15 +- .../homepage-hello-world/ts/app/app.module.ts | 16 + .../homepage-hello-world/ts/app/main.ts | 7 +- .../homepage-todo/ts/app/app.module.ts | 16 + .../_examples/homepage-todo/ts/app/main.ts | 7 +- .../_examples/lifecycle-hooks/e2e-spec.ts | 2 +- .../ts/app/after-content.component.ts | 3 +- .../ts/app/after-view.component.ts | 10 +- .../lifecycle-hooks/ts/app/app.component.ts | 23 +- .../lifecycle-hooks/ts/app/app.module.ts | 67 ++ .../ts/app/counter.component.ts | 5 +- .../ts/app/do-check-parent.component.html | 13 + .../ts/app/do-check.component.ts | 5 +- .../_examples/lifecycle-hooks/ts/app/main.ts | 8 +- .../ts/app/on-changes-parent.component.html | 1 - .../ts/app/on-changes.component.ts | 4 +- .../ts/app/peek-a-boo-parent.component.ts | 4 +- .../lifecycle-hooks/ts/app/spy.component.ts | 2 - public/docs/_examples/package.json | 20 +- .../_examples/pipes/ts/app/app.component.ts | 21 +- .../docs/_examples/pipes/ts/app/app.module.ts | 48 ++ .../app/flying-heroes-impure.component.html | 38 ++ .../pipes/ts/app/flying-heroes.component.ts | 8 +- .../pipes/ts/app/flying-heroes.pipe.ts | 2 +- .../pipes/ts/app/hero-list.component.ts | 5 +- public/docs/_examples/pipes/ts/app/main.ts | 8 +- .../app/power-boost-calculator.component.ts | 5 +- .../pipes/ts/app/power-booster.component.ts | 5 +- .../_examples/quickstart/js/app/app.module.js | 15 + .../docs/_examples/quickstart/js/app/main.js | 6 +- .../docs/_examples/quickstart/js/index.html | 1 + .../_examples/quickstart/js/package.1.json | 20 +- .../_examples/quickstart/ts/app/app.module.ts | 12 + .../docs/_examples/quickstart/ts/app/main.ts | 8 +- .../_examples/quickstart/ts/package.1.json | 20 +- .../_examples/quickstart/ts/typings.1.json | 2 +- .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 public/docs/_examples/router/e2e-spec.ts | 32 +- .../router/ts/app/app.component.1.ts | 10 +- .../router/ts/app/app.component.2.ts | 25 +- .../router/ts/app/app.component.3.ts | 8 +- .../router/ts/app/app.component.4.ts | 11 +- .../_examples/router/ts/app/app.component.ts | 11 +- .../_examples/router/ts/app/app.module.1.ts | 35 + .../_examples/router/ts/app/app.module.2.ts | 35 + .../_examples/router/ts/app/app.module.3.ts | 33 + .../_examples/router/ts/app/app.module.4.ts | 28 + .../_examples/router/ts/app/app.module.ts | 35 + .../_examples/router/ts/app/app.routes.3.ts | 15 - .../_examples/router/ts/app/app.routes.4.ts | 14 - .../_examples/router/ts/app/app.routes.5.ts | 19 - .../_examples/router/ts/app/app.routes.ts | 22 - .../app/{app.routes.1.ts => app.routing.1.ts} | 18 +- .../app/{app.routes.2.ts => app.routing.2.ts} | 10 +- .../_examples/router/ts/app/app.routing.3.ts | 15 + .../_examples/router/ts/app/app.routing.4.ts | 12 + .../_examples/router/ts/app/app.routing.5.ts | 15 + .../_examples/router/ts/app/app.routing.ts | 45 ++ .../router/ts/app/auth-guard.service.ts | 5 +- .../ts/app/can-deactivate-guard.service.1.ts | 32 + .../ts/app/can-deactivate-guard.service.ts | 2 +- .../crisis-center/crisis-admin.component.1.ts | 3 +- .../crisis-center/crisis-admin.component.ts | 17 +- .../crisis-center.component.1.ts | 18 - .../crisis-center/crisis-center.component.ts | 12 +- .../crisis-center/crisis-center.module.1.ts | 32 + .../app/crisis-center/crisis-center.module.ts | 42 ++ ...routes.1.ts => crisis-center.routing.1.ts} | 7 +- ...routes.2.ts => crisis-center.routing.2.ts} | 7 +- ...routes.3.ts => crisis-center.routing.3.ts} | 7 +- ...routes.4.ts => crisis-center.routing.4.ts} | 7 +- ...r.routes.ts => crisis-center.routing.5.ts} | 21 +- .../crisis-center/crisis-center.routing.ts | 50 ++ .../crisis-detail-resolve.service.ts | 25 + .../crisis-detail.component.1.ts | 18 +- .../crisis-center/crisis-detail.component.ts | 51 +- .../crisis-center/crisis-list.component.1.ts | 3 +- .../crisis-center/crisis-list.component.ts | 5 +- .../ts/app/heroes/hero-detail.component.1.ts | 5 +- .../ts/app/heroes/hero-detail.component.2.ts | 2 +- .../ts/app/heroes/hero-detail.component.ts | 7 +- .../ts/app/heroes/hero-list.component.ts | 5 +- .../router/ts/app/heroes/heroes.module.1.ts | 25 + .../router/ts/app/heroes/heroes.module.ts | 30 + .../{heroes.routes.ts => heroes.routing.ts} | 7 +- .../router/ts/app/login.component.1.ts | 46 ++ .../router/ts/app/login.component.ts | 18 +- .../_examples/router/ts/app/login.routes.ts | 11 - .../_examples/router/ts/app/login.routing.ts | 14 + public/docs/_examples/router/ts/app/main.1.ts | 25 - public/docs/_examples/router/ts/app/main.2.ts | 33 - public/docs/_examples/router/ts/app/main.3.ts | 11 - public/docs/_examples/router/ts/app/main.ts | 12 +- public/docs/_examples/router/ts/index.2.html | 36 -- public/docs/_examples/router/ts/plnkr.json | 3 +- .../security/ts/app/app.component.ts | 9 +- .../_examples/security/ts/app/app.module.ts | 18 + public/docs/_examples/security/ts/app/main.ts | 8 +- .../ts/app/app.component.ts | 19 +- .../ts/app/app.module.1.ts | 22 + .../server-communication/ts/app/app.module.ts | 41 ++ .../server-communication/ts/app/main.ts | 32 +- .../ts/app/wiki/wiki-smart.component.ts | 3 +- .../ts/app/wiki/wiki.component.ts | 3 +- .../ts/app/wiki/wikipedia.service.ts | 2 +- .../ts/app/app.module.ts | 18 + .../structural-directives/ts/app/main.ts | 7 +- .../ts/app/structural-directives.component.ts | 5 +- .../style-guide/ts/01-01/app/app.component.ts | 5 +- .../style-guide/ts/01-01/app/app.module.ts | 27 + .../01-01/app/heroes/hero.component.avoid.ts | 15 +- .../_examples/style-guide/ts/01-01/main.ts | 6 +- .../style-guide/ts/02-07/app/app.component.ts | 6 +- .../style-guide/ts/02-07/app/app.module.ts | 19 + .../style-guide/ts/02-08/app/app.component.ts | 5 +- .../style-guide/ts/02-08/app/app.module.ts | 17 + .../style-guide/ts/03-01/app/app.module.ts | 15 + .../style-guide/ts/03-02/app/app.module.ts | 17 + .../style-guide/ts/03-03/app/app.module.ts | 15 + .../style-guide/ts/03-04/app/app.module.ts | 17 + .../style-guide/ts/03-05/app/app.module.ts | 19 + .../style-guide/ts/03-06/app/app.module.ts | 17 + .../style-guide/ts/04-10/app/+heroes/index.ts | 1 - .../style-guide/ts/04-10/app/app.component.ts | 10 +- .../style-guide/ts/04-10/app/app.module.ts | 22 + .../heroes.component.avoid.ts | 1 + .../{+heroes => heroes}/heroes.component.html | 0 .../{+heroes => heroes}/heroes.component.ts | 4 - .../style-guide/ts/04-10/app/index.ts | 3 - .../ts/04-10/app/shared/shared.module.ts | 20 + .../style-guide/ts/04-14/app/app.component.ts | 5 +- .../style-guide/ts/04-14/app/app.module.ts | 19 + .../style-guide/ts/05-02/app/app.component.ts | 5 +- .../style-guide/ts/05-02/app/app.module.ts | 17 + .../style-guide/ts/05-03/app/app.component.ts | 5 +- .../style-guide/ts/05-03/app/app.module.ts | 17 + .../style-guide/ts/05-04/app/app.component.ts | 5 +- .../style-guide/ts/05-04/app/app.module.ts | 19 + .../style-guide/ts/05-12/app/app.component.ts | 5 +- .../style-guide/ts/05-12/app/app.module.ts | 17 + .../style-guide/ts/05-13/app/app.component.ts | 5 +- .../style-guide/ts/05-13/app/app.module.ts | 17 + .../style-guide/ts/05-14/app/app.component.ts | 5 +- .../style-guide/ts/05-14/app/app.module.ts | 17 + .../style-guide/ts/05-15/app/app.component.ts | 3 +- .../style-guide/ts/05-15/app/app.module.ts | 19 + .../style-guide/ts/05-16/app/app.component.ts | 5 +- .../style-guide/ts/05-16/app/app.module.ts | 19 + .../style-guide/ts/05-17/app/app.component.ts | 5 +- .../style-guide/ts/05-17/app/app.module.ts | 20 + .../heroes/hero-list/hero-list.component.ts | 4 +- .../05-17/app/heroes/hero/hero.component.ts | 13 + .../ts/05-17/app/heroes/hero/index.ts | 1 + .../style-guide/ts/05-17/app/heroes/index.ts | 1 + .../style-guide/ts/06-01/app/app.component.ts | 5 +- .../style-guide/ts/06-01/app/app.module.ts | 17 + .../style-guide/ts/06-03/app/app.component.ts | 5 +- .../style-guide/ts/06-03/app/app.module.ts | 17 + .../style-guide/ts/07-01/app/app.module.ts | 17 + .../style-guide/ts/07-03/app/app.component.ts | 3 +- .../style-guide/ts/07-03/app/app.module.ts | 19 + .../style-guide/ts/07-04/app/app.module.ts | 17 + .../style-guide/ts/09-01/app/app.component.ts | 5 +- .../style-guide/ts/09-01/app/app.module.ts | 17 + .../style-guide/ts/app/app.component.ts | 4 +- .../style-guide/ts/app/app.routes.ts | 107 ++-- .../docs/_examples/style-guide/ts/app/main.ts | 109 +++- .../style-guide/ts/systemjs.custom.js | 3 +- public/docs/_examples/styleguide/js/app.js | 14 +- .../_examples/styleguide/ts/app/app.module.ts | 11 + .../docs/_examples/styleguide/ts/app/main.ts | 8 +- public/docs/_examples/systemjs.config.js | 3 - .../systemjs.config.plunker.build.js | 105 +++ .../docs/_examples/systemjs.config.plunker.js | 19 +- .../template-syntax/ts/app/app.component.html | 3 +- .../template-syntax/ts/app/app.component.ts | 10 +- .../template-syntax/ts/app/app.module.1.ts | 18 + .../template-syntax/ts/app/app.module.ts | 23 + .../_examples/template-syntax/ts/app/main.ts | 6 +- .../docs/_examples/toh-1/ts/app/app.module.ts | 18 + public/docs/_examples/toh-1/ts/app/main.ts | 11 +- .../docs/_examples/toh-2/ts/app/app.module.ts | 18 + public/docs/_examples/toh-2/ts/app/main.ts | 9 +- .../_examples/toh-3/ts/app/app.component.ts | 8 +- .../docs/_examples/toh-3/ts/app/app.module.ts | 24 + public/docs/_examples/toh-3/ts/app/main.ts | 7 +- .../_examples/toh-4/ts/app/app.component.1.ts | 2 - .../_examples/toh-4/ts/app/app.component.ts | 2 - .../docs/_examples/toh-4/ts/app/app.module.ts | 19 + public/docs/_examples/toh-4/ts/app/main.1.ts | 7 +- public/docs/_examples/toh-4/ts/app/main.ts | 7 +- .../_examples/toh-5/ts/app/app.component.1.ts | 15 +- .../_examples/toh-5/ts/app/app.component.2.ts | 15 +- .../_examples/toh-5/ts/app/app.component.3.ts | 13 +- .../_examples/toh-5/ts/app/app.component.ts | 11 +- .../_examples/toh-5/ts/app/app.module.1.ts | 28 + .../_examples/toh-5/ts/app/app.module.2.ts | 30 + .../_examples/toh-5/ts/app/app.module.3.ts | 47 ++ .../docs/_examples/toh-5/ts/app/app.module.ts | 34 + .../_examples/toh-5/ts/app/app.routes.2.ts | 14 - .../_examples/toh-5/ts/app/app.routing.1.ts | 17 + .../app/{app.routes.1.ts => app.routing.2.ts} | 11 +- .../ts/app/{app.routes.ts => app.routing.ts} | 12 +- .../toh-5/ts/app/hero-detail.component.ts | 15 +- public/docs/_examples/toh-5/ts/app/main.ts | 11 +- .../_examples/toh-6/ts/app/app.component.ts | 12 +- .../_examples/toh-6/ts/app/app.module.1.ts | 44 ++ .../_examples/toh-6/ts/app/app.module.2.ts | 44 ++ .../docs/_examples/toh-6/ts/app/app.module.ts | 45 ++ .../ts/app/{app.routes.ts => app.routing.ts} | 12 +- .../toh-6/ts/app/dashboard.component.ts | 6 +- .../toh-6/ts/app/hero-detail.component.ts | 13 +- .../toh-6/ts/app/heroes.component.ts | 4 +- public/docs/_examples/toh-6/ts/app/main.ts | 37 +- public/docs/_examples/typings.json | 2 +- .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 ...pec.ts => checkmark.pipe.spec.ts.disabled} | 5 +- ...spec.ts => phone.service.spec.ts.disabled} | 0 ...> phone-detail.component.spec.ts.disabled} | 0 ... => phone-list.component.spec.ts.disabled} | 0 .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 ...pec.ts => checkmark.pipe.spec.ts.disabled} | 0 ...spec.ts => phone.service.spec.ts.disabled} | 0 .../upgrade-phonecat-3-final/ts/app/main.ts | 18 +- ...> phone-detail.component.spec.ts.disabled} | 0 ... => phone-list.component.spec.ts.disabled} | 0 .../user-input/ts/app/app.component.ts | 20 +- .../_examples/user-input/ts/app/app.module.ts | 37 ++ .../docs/_examples/user-input/ts/app/main.ts | 6 +- .../{e2e-spec.ts => e2e-spec.ts.disabled} | 0 .../_examples/webpack/ts/package.webpack.json | 16 +- .../webpack/ts/src/app/app.component.spec.ts | 13 +- .../webpack/ts/src/app/app.module.ts | 16 + public/docs/_examples/webpack/ts/src/main.ts | 6 +- .../docs/_examples/webpack/ts/typings.1.json | 2 +- public/docs/js/latest/_data.json | 2 +- public/docs/js/latest/guide/_data.json | 2 +- .../js/latest/guide/forms-deprecated.jade | 2 +- public/docs/js/latest/quickstart.jade | 96 ++- public/docs/ts/latest/_data.json | 2 +- public/docs/ts/latest/cookbook/_data.json | 8 +- .../cookbook/a1-a2-quick-reference.jade | 58 +- .../latest/cookbook/dependency-injection.jade | 33 +- .../cookbook/dynamic-form-deprecated.jade | 2 +- .../docs/ts/latest/cookbook/dynamic-form.jade | 31 +- .../latest/cookbook/set-document-title.jade | 21 +- public/docs/ts/latest/glossary.jade | 144 ++++- public/docs/ts/latest/guide/_data.json | 6 +- public/docs/ts/latest/guide/architecture.jade | 222 +++---- .../ts/latest/guide/attribute-directives.jade | 15 +- .../ts/latest/guide/dependency-injection.jade | 46 +- .../docs/ts/latest/guide/displaying-data.jade | 5 +- .../ts/latest/guide/forms-deprecated.jade | 2 +- public/docs/ts/latest/guide/forms.jade | 160 ++--- .../hierarchical-dependency-injection.jade | 8 +- public/docs/ts/latest/guide/pipes.jade | 8 +- public/docs/ts/latest/guide/router.jade | 600 ++++++++++++------ .../ts/latest/guide/server-communication.jade | 43 +- .../latest/guide/structural-directives.jade | 2 +- public/docs/ts/latest/guide/style-guide.jade | 7 + .../docs/ts/latest/guide/template-syntax.jade | 14 +- public/docs/ts/latest/guide/testing.jade | 5 + public/docs/ts/latest/guide/upgrade.jade | 5 + public/docs/ts/latest/guide/webpack.jade | 6 +- public/docs/ts/latest/quickstart.jade | 86 ++- public/docs/ts/latest/tutorial/toh-pt1.jade | 21 +- public/docs/ts/latest/tutorial/toh-pt2.jade | 1 + public/docs/ts/latest/tutorial/toh-pt3.jade | 47 +- public/docs/ts/latest/tutorial/toh-pt4.jade | 2 + public/docs/ts/latest/tutorial/toh-pt5.jade | 130 ++-- public/docs/ts/latest/tutorial/toh-pt6.jade | 80 +-- tools/plunker-builder/plunkerBuilder.js | 10 +- 374 files changed, 4612 insertions(+), 2294 deletions(-) create mode 100644 public/docs/_examples/animations/ts/app/app.module.ts create mode 100644 public/docs/_examples/architecture/ts/app/app.module.ts create mode 100644 public/docs/_examples/architecture/ts/app/mini-app.ts rename public/docs/_examples/{router/ts/index.3.html => architecture/ts/mini-app.html} (68%) create mode 100644 public/docs/_examples/attribute-directives/ts/app/app.module.ts create mode 100644 public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.1.ts create mode 100644 public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.ts delete mode 100644 public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts create mode 100644 public/docs/_examples/cb-component-communication/ts/app/app.module.ts create mode 100644 public/docs/_examples/cb-component-relative-paths/ts/app/app.module.ts create mode 100644 public/docs/_examples/cb-dependency-injection/ts/app/app.module.ts rename public/docs/_examples/cb-dynamic-form-deprecated/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) create mode 100644 public/docs/_examples/cb-dynamic-form/ts/app/app.module.ts create mode 100644 public/docs/_examples/cb-set-document-title/ts/app/app.module.ts rename public/docs/_examples/cli-quickstart/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) create mode 100644 public/docs/_examples/component-styles/ts/app/app.module.ts create mode 100644 public/docs/_examples/dependency-injection/ts/app/app.module.ts delete mode 100644 public/docs/_examples/dependency-injection/ts/app/main.1.ts create mode 100644 public/docs/_examples/displaying-data/ts/app/app.module.ts rename public/docs/_examples/forms-deprecated/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) create mode 100644 public/docs/_examples/forms/js/app/app.module.js create mode 100644 public/docs/_examples/forms/ts/app/app.module.ts create mode 100644 public/docs/_examples/hierarchical-dependency-injection/ts/app/app.module.ts create mode 100644 public/docs/_examples/homepage-hello-world/ts/app/app.module.ts create mode 100644 public/docs/_examples/homepage-todo/ts/app/app.module.ts create mode 100644 public/docs/_examples/lifecycle-hooks/ts/app/app.module.ts create mode 100644 public/docs/_examples/lifecycle-hooks/ts/app/do-check-parent.component.html create mode 100644 public/docs/_examples/pipes/ts/app/app.module.ts create mode 100644 public/docs/_examples/pipes/ts/app/flying-heroes-impure.component.html create mode 100644 public/docs/_examples/quickstart/js/app/app.module.js create mode 100644 public/docs/_examples/quickstart/ts/app/app.module.ts rename public/docs/_examples/router-deprecated/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) create mode 100644 public/docs/_examples/router/ts/app/app.module.1.ts create mode 100644 public/docs/_examples/router/ts/app/app.module.2.ts create mode 100644 public/docs/_examples/router/ts/app/app.module.3.ts create mode 100644 public/docs/_examples/router/ts/app/app.module.4.ts create mode 100644 public/docs/_examples/router/ts/app/app.module.ts delete mode 100644 public/docs/_examples/router/ts/app/app.routes.3.ts delete mode 100644 public/docs/_examples/router/ts/app/app.routes.4.ts delete mode 100644 public/docs/_examples/router/ts/app/app.routes.5.ts delete mode 100644 public/docs/_examples/router/ts/app/app.routes.ts rename public/docs/_examples/router/ts/app/{app.routes.1.ts => app.routing.1.ts} (73%) rename public/docs/_examples/router/ts/app/{app.routes.2.ts => app.routing.2.ts} (67%) create mode 100644 public/docs/_examples/router/ts/app/app.routing.3.ts create mode 100644 public/docs/_examples/router/ts/app/app.routing.4.ts create mode 100644 public/docs/_examples/router/ts/app/app.routing.5.ts create mode 100644 public/docs/_examples/router/ts/app/app.routing.ts create mode 100644 public/docs/_examples/router/ts/app/can-deactivate-guard.service.1.ts delete mode 100644 public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts create mode 100644 public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.1.ts create mode 100644 public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.ts rename public/docs/_examples/router/ts/app/crisis-center/{crisis-center.routes.1.ts => crisis-center.routing.1.ts} (72%) rename public/docs/_examples/router/ts/app/crisis-center/{crisis-center.routes.2.ts => crisis-center.routing.2.ts} (77%) rename public/docs/_examples/router/ts/app/crisis-center/{crisis-center.routes.3.ts => crisis-center.routing.3.ts} (86%) rename public/docs/_examples/router/ts/app/crisis-center/{crisis-center.routes.4.ts => crisis-center.routing.4.ts} (87%) rename public/docs/_examples/router/ts/app/crisis-center/{crisis-center.routes.ts => crisis-center.routing.5.ts} (63%) create mode 100644 public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.ts create mode 100644 public/docs/_examples/router/ts/app/crisis-center/crisis-detail-resolve.service.ts create mode 100644 public/docs/_examples/router/ts/app/heroes/heroes.module.1.ts create mode 100644 public/docs/_examples/router/ts/app/heroes/heroes.module.ts rename public/docs/_examples/router/ts/app/heroes/{heroes.routes.ts => heroes.routing.ts} (68%) create mode 100644 public/docs/_examples/router/ts/app/login.component.1.ts delete mode 100644 public/docs/_examples/router/ts/app/login.routes.ts create mode 100644 public/docs/_examples/router/ts/app/login.routing.ts delete mode 100644 public/docs/_examples/router/ts/app/main.1.ts delete mode 100644 public/docs/_examples/router/ts/app/main.2.ts delete mode 100644 public/docs/_examples/router/ts/app/main.3.ts delete mode 100644 public/docs/_examples/router/ts/index.2.html create mode 100644 public/docs/_examples/security/ts/app/app.module.ts create mode 100644 public/docs/_examples/server-communication/ts/app/app.module.1.ts create mode 100644 public/docs/_examples/server-communication/ts/app/app.module.ts create mode 100644 public/docs/_examples/structural-directives/ts/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/01-01/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/02-07/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/02-08/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/03-01/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/03-02/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/03-03/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/03-04/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/03-05/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/03-06/app/app.module.ts delete mode 100644 public/docs/_examples/style-guide/ts/04-10/app/+heroes/index.ts create mode 100644 public/docs/_examples/style-guide/ts/04-10/app/app.module.ts rename public/docs/_examples/style-guide/ts/04-10/app/{+heroes => heroes}/heroes.component.avoid.ts (94%) rename public/docs/_examples/style-guide/ts/04-10/app/{+heroes => heroes}/heroes.component.html (100%) rename public/docs/_examples/style-guide/ts/04-10/app/{+heroes => heroes}/heroes.component.ts (86%) delete mode 100644 public/docs/_examples/style-guide/ts/04-10/app/index.ts create mode 100644 public/docs/_examples/style-guide/ts/04-10/app/shared/shared.module.ts create mode 100644 public/docs/_examples/style-guide/ts/04-14/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-02/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-03/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-04/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-12/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-13/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-14/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-15/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-16/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-17/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/05-17/app/heroes/hero/hero.component.ts create mode 100644 public/docs/_examples/style-guide/ts/05-17/app/heroes/hero/index.ts create mode 100644 public/docs/_examples/style-guide/ts/06-01/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/06-03/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/07-01/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/07-03/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/07-04/app/app.module.ts create mode 100644 public/docs/_examples/style-guide/ts/09-01/app/app.module.ts create mode 100644 public/docs/_examples/styleguide/ts/app/app.module.ts create mode 100644 public/docs/_examples/systemjs.config.plunker.build.js create mode 100644 public/docs/_examples/template-syntax/ts/app/app.module.1.ts create mode 100644 public/docs/_examples/template-syntax/ts/app/app.module.ts create mode 100644 public/docs/_examples/toh-1/ts/app/app.module.ts create mode 100644 public/docs/_examples/toh-2/ts/app/app.module.ts create mode 100644 public/docs/_examples/toh-3/ts/app/app.module.ts create mode 100644 public/docs/_examples/toh-4/ts/app/app.module.ts create mode 100644 public/docs/_examples/toh-5/ts/app/app.module.1.ts create mode 100644 public/docs/_examples/toh-5/ts/app/app.module.2.ts create mode 100644 public/docs/_examples/toh-5/ts/app/app.module.3.ts create mode 100644 public/docs/_examples/toh-5/ts/app/app.module.ts delete mode 100644 public/docs/_examples/toh-5/ts/app/app.routes.2.ts create mode 100644 public/docs/_examples/toh-5/ts/app/app.routing.1.ts rename public/docs/_examples/toh-5/ts/app/{app.routes.1.ts => app.routing.2.ts} (76%) rename public/docs/_examples/toh-5/ts/app/{app.routes.ts => app.routing.ts} (60%) create mode 100644 public/docs/_examples/toh-6/ts/app/app.module.1.ts create mode 100644 public/docs/_examples/toh-6/ts/app/app.module.2.ts create mode 100644 public/docs/_examples/toh-6/ts/app/app.module.ts rename public/docs/_examples/toh-6/ts/app/{app.routes.ts => app.routing.ts} (56%) rename public/docs/_examples/upgrade-adapter/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-2-hybrid/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/{checkmark.pipe.spec.ts => checkmark.pipe.spec.ts.disabled} (92%) rename public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/{phone.service.spec.ts => phone.service.spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/{phone-detail.component.spec.ts => phone-detail.component.spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/{phone-list.component.spec.ts => phone-list.component.spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-3-final/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/checkmark/{checkmark.pipe.spec.ts => checkmark.pipe.spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/{phone.service.spec.ts => phone.service.spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/{phone-detail.component.spec.ts => phone-detail.component.spec.ts.disabled} (100%) rename public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/{phone-list.component.spec.ts => phone-list.component.spec.ts.disabled} (100%) create mode 100644 public/docs/_examples/user-input/ts/app/app.module.ts rename public/docs/_examples/webpack/{e2e-spec.ts => e2e-spec.ts.disabled} (100%) create mode 100644 public/docs/_examples/webpack/ts/src/app/app.module.ts diff --git a/gulpfile.js b/gulpfile.js index 6c5a3a4490..c6e15ae13f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -595,7 +595,7 @@ gulp.task('build-dart-api-docs', ['_shred-api-examples', 'dartdoc'], function() }); gulp.task('build-plunkers', ['_copy-example-boilerplate'], function() { - return plunkerBuilder.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log }); + return plunkerBuilder.buildPlunkers(EXAMPLES_PATH, LIVE_EXAMPLES_PATH, { errFn: gutil.log, build: argv.build }); }); gulp.task('build-dart-cheatsheet', [], function() { diff --git a/package.json b/package.json index d5283e3f43..2e848d239c 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "browser-sync": "^2.9.3", "canonical-path": "0.0.2", "cheerio": "^0.20.0", - "codelyzer": "0.0.22", + "codelyzer": "0.0.26", "cross-spawn": "^4.0.0", "del": "^2.2.0", "dgeni": "^0.4.0", diff --git a/public/docs/_examples/animations/ts/app/app.module.ts b/public/docs/_examples/animations/ts/app/app.module.ts new file mode 100644 index 0000000000..1550034236 --- /dev/null +++ b/public/docs/_examples/animations/ts/app/app.module.ts @@ -0,0 +1,33 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { HeroTeamBuilderComponent } from './hero-team-builder.component'; +import { HeroListBasicComponent } from './hero-list-basic.component'; +import { HeroListInlineStylesComponent } from './hero-list-inline-styles.component'; +import { HeroListEnterLeaveComponent } from './hero-list-enter-leave.component'; +import { HeroListEnterLeaveStatesComponent } from './hero-list-enter-leave-states.component'; +import { HeroListCombinedTransitionsComponent } from './hero-list-combined-transitions.component'; +import { HeroListTwowayComponent } from './hero-list-twoway.component'; +import { HeroListAutoComponent } from './hero-list-auto.component'; +import { HeroListGroupsComponent } from './hero-list-groups.component'; +import { HeroListMultistepComponent } from './hero-list-multistep.component'; +import { HeroListTimingsComponent } from './hero-list-timings.component'; + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ + HeroTeamBuilderComponent, + HeroListBasicComponent, + HeroListInlineStylesComponent, + HeroListCombinedTransitionsComponent, + HeroListTwowayComponent, + HeroListEnterLeaveComponent, + HeroListEnterLeaveStatesComponent, + HeroListAutoComponent, + HeroListTimingsComponent, + HeroListMultistepComponent, + HeroListGroupsComponent + ], + bootstrap: [ HeroTeamBuilderComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/animations/ts/app/hero-team-builder.component.ts b/public/docs/_examples/animations/ts/app/hero-team-builder.component.ts index 0ae7ec519e..e5413be50e 100644 --- a/public/docs/_examples/animations/ts/app/hero-team-builder.component.ts +++ b/public/docs/_examples/animations/ts/app/hero-team-builder.component.ts @@ -1,16 +1,6 @@ import { Component } from '@angular/core'; import { Heroes } from './hero.service'; -import { HeroListBasicComponent } from './hero-list-basic.component'; -import { HeroListInlineStylesComponent } from './hero-list-inline-styles.component'; -import { HeroListEnterLeaveComponent } from './hero-list-enter-leave.component'; -import { HeroListEnterLeaveStatesComponent } from './hero-list-enter-leave-states.component'; -import { HeroListCombinedTransitionsComponent } from './hero-list-combined-transitions.component'; -import { HeroListTwowayComponent } from './hero-list-twoway.component'; -import { HeroListAutoComponent } from './hero-list-auto.component'; -import { HeroListGroupsComponent } from './hero-list-groups.component'; -import { HeroListMultistepComponent } from './hero-list-multistep.component'; -import { HeroListTimingsComponent } from './hero-list-timings.component'; @Component({ selector: 'hero-team-builder', @@ -97,18 +87,6 @@ import { HeroListTimingsComponent } from './hero-list-timings.component'; min-height: 6em; } `], - directives: [ - HeroListBasicComponent, - HeroListInlineStylesComponent, - HeroListCombinedTransitionsComponent, - HeroListTwowayComponent, - HeroListEnterLeaveComponent, - HeroListEnterLeaveStatesComponent, - HeroListAutoComponent, - HeroListTimingsComponent, - HeroListMultistepComponent, - HeroListGroupsComponent - ], providers: [Heroes] }) export class HeroTeamBuilderComponent { diff --git a/public/docs/_examples/animations/ts/app/main.ts b/public/docs/_examples/animations/ts/app/main.ts index 3c8422e790..2470c9595e 100644 --- a/public/docs/_examples/animations/ts/app/main.ts +++ b/public/docs/_examples/animations/ts/app/main.ts @@ -1,5 +1,4 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { HeroTeamBuilderComponent } from './hero-team-builder.component'; - -bootstrap(HeroTeamBuilderComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/architecture/ts/app/app.component.ts b/public/docs/_examples/architecture/ts/app/app.component.ts index 930cf5f045..b987f17e44 100644 --- a/public/docs/_examples/architecture/ts/app/app.component.ts +++ b/public/docs/_examples/architecture/ts/app/app.component.ts @@ -1,16 +1,13 @@ // #docregion import import { Component } from '@angular/core'; // #enddocregion import -import { HeroListComponent } from './hero-list.component'; -import { SalesTaxComponent } from './sales-tax.component'; @Component({ selector: 'my-app', template: ` - <hero-list></hero-list> - <sales-tax></sales-tax> - `, - directives: [HeroListComponent, SalesTaxComponent] + <hero-list></hero-list> + <sales-tax></sales-tax> + ` }) // #docregion export export class AppComponent { } diff --git a/public/docs/_examples/architecture/ts/app/app.module.ts b/public/docs/_examples/architecture/ts/app/app.module.ts new file mode 100644 index 0000000000..f6e64beecd --- /dev/null +++ b/public/docs/_examples/architecture/ts/app/app.module.ts @@ -0,0 +1,36 @@ +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +// #docregion imports +import { NgModule } from '@angular/core'; +import { AppComponent } from './app.component'; +// #enddocregion imports +import { HeroDetailComponent } from './hero-detail.component'; +import { HeroListComponent } from './hero-list.component'; +import { SalesTaxComponent } from './sales-tax.component'; +import { HeroService } from './hero.service'; +import { BackendService } from './backend.service'; +import { Logger } from './logger.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + declarations: [ + AppComponent, + HeroDetailComponent, + HeroListComponent, + SalesTaxComponent + ], +// #docregion providers + providers: [ + BackendService, + HeroService, + Logger + ], +// #enddocregion providers + bootstrap: [ AppComponent ] +}) +// #docregion export +export class AppModule { } +// #enddocregion export diff --git a/public/docs/_examples/architecture/ts/app/hero-detail.component.ts b/public/docs/_examples/architecture/ts/app/hero-detail.component.ts index fc2604e53e..b1a93cf7a2 100644 --- a/public/docs/_examples/architecture/ts/app/hero-detail.component.ts +++ b/public/docs/_examples/architecture/ts/app/hero-detail.component.ts @@ -4,8 +4,7 @@ import { Hero } from './hero'; @Component({ selector: 'hero-detail', - templateUrl: 'app/hero-detail.component.html', - directives: [HeroDetailComponent] + templateUrl: 'app/hero-detail.component.html' }) export class HeroDetailComponent { @Input() hero: Hero; diff --git a/public/docs/_examples/architecture/ts/app/hero-list.component.ts b/public/docs/_examples/architecture/ts/app/hero-list.component.ts index eaa21b45fe..868b9251b5 100644 --- a/public/docs/_examples/architecture/ts/app/hero-list.component.ts +++ b/public/docs/_examples/architecture/ts/app/hero-list.component.ts @@ -1,18 +1,15 @@ import { Component, OnInit } from '@angular/core'; import { Hero } from './hero'; -import { HeroDetailComponent } from './hero-detail.component'; import { HeroService } from './hero.service'; -// #docregion metadata +// #docregion metadata, providers @Component({ selector: 'hero-list', templateUrl: 'app/hero-list.component.html', - directives: [HeroDetailComponent], - // #docregion providers - providers: [HeroService] - // #enddocregion providers + providers: [ HeroService ] }) +// #enddocregion providers // #docregion class export class HeroListComponent implements OnInit { // #enddocregion metadata diff --git a/public/docs/_examples/architecture/ts/app/main.ts b/public/docs/_examples/architecture/ts/app/main.ts index 4e8c107afd..4acf5de663 100644 --- a/public/docs/_examples/architecture/ts/app/main.ts +++ b/public/docs/_examples/architecture/ts/app/main.ts @@ -1,11 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; -// #docregion import -import { AppComponent } from './app.component'; -// #enddocregion import -import { HeroService } from './hero.service'; -import { BackendService } from './backend.service'; -import { Logger } from './logger.service'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -// #docregion bootstrap -bootstrap(AppComponent, [BackendService, HeroService, Logger]); -// #enddocregion bootstrap +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/architecture/ts/app/mini-app.ts b/public/docs/_examples/architecture/ts/app/mini-app.ts new file mode 100644 index 0000000000..8a35a607a2 --- /dev/null +++ b/public/docs/_examples/architecture/ts/app/mini-app.ts @@ -0,0 +1,45 @@ +// #docplaster +// A mini-application +import { Injectable } from '@angular/core'; + +@Injectable() +export class Logger { + log(message: string) { console.log(message); } +} + +// #docregion import-core-component +import { Component } from '@angular/core'; +// #enddocregion import-core-component + +@Component({ + selector: 'my-app', + template: 'Welcome to Angular 2' +}) +export class AppComponent { + constructor(logger: Logger) { + logger.log('Let the fun begin!'); + } +} + +// #docregion module +import { NgModule } from '@angular/core'; +// #docregion import-browser-module +import { BrowserModule } from '@angular/platform-browser'; +// #enddocregion import-browser-module +@NgModule({ +// #docregion ngmodule-imports + imports: [ BrowserModule ], +// #enddocregion ngmodule-imports + providers: [ Logger ], + declarations: [ AppComponent ], + exports: [ AppComponent ], + bootstrap: [ AppComponent ] +}) +// #docregion export +export class AppModule { } +// #enddocregion export +// #enddocregion module + +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/architecture/ts/app/sales-tax.component.ts b/public/docs/_examples/architecture/ts/app/sales-tax.component.ts index 983e731dc7..02201afe05 100644 --- a/public/docs/_examples/architecture/ts/app/sales-tax.component.ts +++ b/public/docs/_examples/architecture/ts/app/sales-tax.component.ts @@ -14,7 +14,7 @@ import { TaxRateService } from './tax-rate.service'; {{ getTax(amountBox.value) | currency:'USD':true:'1.2-2' }} </div> `, - providers: [SalesTaxService, TaxRateService] + providers: [SalesTaxService, TaxRateService] }) export class SalesTaxComponent { constructor(private salesTaxService: SalesTaxService) { } diff --git a/public/docs/_examples/router/ts/index.3.html b/public/docs/_examples/architecture/ts/mini-app.html similarity index 68% rename from public/docs/_examples/router/ts/index.3.html rename to public/docs/_examples/architecture/ts/mini-app.html index a142bb6fcc..a17a191e90 100644 --- a/public/docs/_examples/router/ts/index.3.html +++ b/public/docs/_examples/architecture/ts/mini-app.html @@ -1,9 +1,7 @@ <!DOCTYPE html> -<!-- #docregion --> <html> <head> - <base href="/"> - <title>Router Sample v.3 + Architecture of Angular 2 @@ -17,15 +15,12 @@ -

Milestone 3

- loading... + Loading... - diff --git a/public/docs/_examples/attribute-directives/ts/app/app.component.ts b/public/docs/_examples/attribute-directives/ts/app/app.component.ts index 711e98f1e2..b8b1fa6f08 100644 --- a/public/docs/_examples/attribute-directives/ts/app/app.component.ts +++ b/public/docs/_examples/attribute-directives/ts/app/app.component.ts @@ -1,14 +1,10 @@ // #docregion import { Component } from '@angular/core'; -import { HighlightDirective } from './highlight.directive'; - @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: [HighlightDirective] + templateUrl: 'app/app.component.html' }) export class AppComponent { } - // #enddocregion diff --git a/public/docs/_examples/attribute-directives/ts/app/app.module.ts b/public/docs/_examples/attribute-directives/ts/app/app.module.ts new file mode 100644 index 0000000000..ca35d560fb --- /dev/null +++ b/public/docs/_examples/attribute-directives/ts/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'; +import { HighlightDirective } from './highlight.directive'; + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ + AppComponent, + HighlightDirective + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/attribute-directives/ts/app/main.ts b/public/docs/_examples/attribute-directives/ts/app/main.ts index 4fc79adda1..4acf5de663 100644 --- a/public/docs/_examples/attribute-directives/ts/app/main.ts +++ b/public/docs/_examples/attribute-directives/ts/app/main.ts @@ -1,7 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; - -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts index cdb9d9fd81..c3eb0c86f4 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.component.ts @@ -1,17 +1,12 @@ import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; -import { MovieListComponent } from './movie-list.component'; import { MovieService } from './movie.service'; import { IMovie } from './movie'; -import { StringSafeDatePipe } from './date.pipe'; @Component({ selector: 'my-app', templateUrl: 'app/app.component.html', styleUrls: ['app/app.component.css'], - directives: [MovieListComponent, ROUTER_DIRECTIVES], - pipes: [StringSafeDatePipe], providers: [MovieService] }) export class AppComponent { diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.1.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.1.ts new file mode 100644 index 0000000000..5b24020186 --- /dev/null +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.1.ts @@ -0,0 +1,12 @@ +// #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/cb-a1-a2-quick-reference/ts/app/app.module.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.ts new file mode 100644 index 0000000000..08391524b9 --- /dev/null +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.module.ts @@ -0,0 +1,23 @@ +// #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 { MovieListComponent } from './movie-list.component'; +import { routes } from './app.routes'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + RouterModule.forRoot(routes, {}) + ], + declarations: [ + AppComponent, + MovieListComponent + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts index 1fd2627996..67aa75ed25 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/app.routes.ts @@ -1,13 +1,9 @@ // #docregion -import { provideRouter, RouterConfig } from '@angular/router'; +import { RouterConfig } from '@angular/router'; import { MovieListComponent } from './movie-list.component'; -const routes: RouterConfig = [ +export const routes: RouterConfig = [ { path: '', redirectTo: '/movies', pathMatch: 'full' }, { path: 'movies', component: MovieListComponent } ]; - -export const appRouterProviders = [ - provideRouter(routes) -]; diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts deleted file mode 100644 index 67de525937..0000000000 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.1.ts +++ /dev/null @@ -1,5 +0,0 @@ -// #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts index 7a1d58fad8..4acf5de663 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/main.ts @@ -1,8 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; -import { appRouterProviders } from './app.routes'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -bootstrap(AppComponent, [ - appRouterProviders -]); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts index 67b0c2873d..dc6fbd6b63 100644 --- a/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts +++ b/public/docs/_examples/cb-a1-a2-quick-reference/ts/app/movie-list.component.ts @@ -3,10 +3,8 @@ // #docregion import import { Component } from '@angular/core'; // #enddocregion import -import { MovieService } from './movie.service'; import { IMovie } from './movie'; -import { StringSafeDatePipe } from './date.pipe'; - +import { MovieService } from './movie.service'; // #docregion component @Component({ @@ -16,9 +14,6 @@ import { StringSafeDatePipe } from './date.pipe'; // #docregion style-url styleUrls: ['app/movie-list.component.css'], // #enddocregion style-url -// #docregion date-pipe - pipes: [StringSafeDatePipe] -// #enddocregion date-pipe }) // #enddocregion component // #docregion class diff --git a/public/docs/_examples/cb-component-communication/ts/app/app.component.ts b/public/docs/_examples/cb-component-communication/ts/app/app.component.ts index 891b5c86ac..0a3c992498 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/app.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/app.component.ts @@ -1,32 +1,7 @@ import { Component } from '@angular/core'; -import { HeroParentComponent } from './hero-parent.component'; -import { NameParentComponent } from './name-parent.component'; -import { VersionParentComponent } from './version-parent.component'; -import { VoteTakerComponent } from './votetaker.component'; -import { CountdownLocalVarParentComponent, - CountdownViewChildParentComponent } from './countdown-parent.component'; -import { MissionControlComponent } from './missioncontrol.component'; - -let directives: any[] = [ - HeroParentComponent, - NameParentComponent, - VersionParentComponent, - VoteTakerComponent, - MissionControlComponent, - ]; - -// Include Countdown examples -// unless in e2e tests which they break. -if (!/e2e/.test(location.search)) { - console.log('adding countdown timer examples'); - directives.push(CountdownLocalVarParentComponent); - directives.push(CountdownViewChildParentComponent); -} - @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: directives + templateUrl: 'app/app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/cb-component-communication/ts/app/app.module.ts b/public/docs/_examples/cb-component-communication/ts/app/app.module.ts new file mode 100644 index 0000000000..2314524aa7 --- /dev/null +++ b/public/docs/_examples/cb-component-communication/ts/app/app.module.ts @@ -0,0 +1,48 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; +import { AstronautComponent } from './astronaut.component'; +import { CountdownLocalVarParentComponent, CountdownViewChildParentComponent } from './countdown-parent.component'; +import { CountdownTimerComponent } from './countdown-timer.component'; +import { HeroChildComponent } from './hero-child.component'; +import { HeroParentComponent } from './hero-parent.component'; +import { MissionControlComponent } from './missioncontrol.component'; +import { NameChildComponent } from './name-child.component'; +import { NameParentComponent } from './name-parent.component'; +import { VersionChildComponent } from './version-child.component'; +import { VersionParentComponent } from './version-parent.component'; +import { VoterComponent } from './voter.component'; +import { VoteTakerComponent } from './votetaker.component'; + +let directives: any[] = [ + AppComponent, + AstronautComponent, + CountdownTimerComponent, + HeroChildComponent, + HeroParentComponent, + MissionControlComponent, + NameChildComponent, + NameParentComponent, + VersionChildComponent, + VersionParentComponent, + VoterComponent, + VoteTakerComponent + ]; + +// Include Countdown examples +// unless in e2e tests which they break. +if (!/e2e/.test(location.search)) { + console.log('adding countdown timer examples'); + directives.push(CountdownLocalVarParentComponent); + directives.push(CountdownViewChildParentComponent); +} + +@NgModule({ + imports: [ + BrowserModule + ], + declarations: directives, + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/cb-component-communication/ts/app/countdown-parent.component.ts b/public/docs/_examples/cb-component-communication/ts/app/countdown-parent.component.ts index a89dee52d9..5bcf0645c9 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/countdown-parent.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/countdown-parent.component.ts @@ -19,7 +19,6 @@ import { CountdownTimerComponent } from './countdown-timer.component';
{{timer.seconds}}
`, - directives: [CountdownTimerComponent], styleUrls: ['demo.css'] }) export class CountdownLocalVarParentComponent { } @@ -36,7 +35,6 @@ export class CountdownLocalVarParentComponent { }
{{ seconds() }}
`, - directives: [CountdownTimerComponent], styleUrls: ['demo.css'] }) export class CountdownViewChildParentComponent implements AfterViewInit { diff --git a/public/docs/_examples/cb-component-communication/ts/app/hero-parent.component.ts b/public/docs/_examples/cb-component-communication/ts/app/hero-parent.component.ts index 6d82f53a4d..bf3861c455 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/hero-parent.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/hero-parent.component.ts @@ -1,7 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { HeroChildComponent } from './hero-child.component'; import { HEROES } from './hero'; @Component({ @@ -12,8 +11,7 @@ import { HEROES } from './hero'; [hero]="hero" [master]="master"> - `, - directives: [HeroChildComponent] + ` }) export class HeroParentComponent { heroes = HEROES; diff --git a/public/docs/_examples/cb-component-communication/ts/app/main.ts b/public/docs/_examples/cb-component-communication/ts/app/main.ts index ad256f0823..4f325ca43d 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/main.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/main.ts @@ -1,5 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; +import { AppModule } from './app.module'; -bootstrap(AppComponent); +browserDynamicPlatform().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-component-communication/ts/app/missioncontrol.component.ts b/public/docs/_examples/cb-component-communication/ts/app/missioncontrol.component.ts index 1c7968bb88..a27e9b16b1 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/missioncontrol.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/missioncontrol.component.ts @@ -1,7 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { AstronautComponent } from './astronaut.component'; import { MissionService } from './mission.service'; @Component({ @@ -17,7 +16,6 @@ import { MissionService } from './mission.service';
  • {{event}}
  • `, - directives: [AstronautComponent], providers: [MissionService] }) export class MissionControlComponent { diff --git a/public/docs/_examples/cb-component-communication/ts/app/name-parent.component.ts b/public/docs/_examples/cb-component-communication/ts/app/name-parent.component.ts index aa7382503a..699690c067 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/name-parent.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/name-parent.component.ts @@ -1,8 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { NameChildComponent } from './name-child.component'; - @Component({ selector: 'name-parent', template: ` @@ -10,8 +8,7 @@ import { NameChildComponent } from './name-child.component'; - `, - directives: [NameChildComponent] + ` }) export class NameParentComponent { // Displays 'Mr. IQ', '', 'Bombasto' diff --git a/public/docs/_examples/cb-component-communication/ts/app/version-parent.component.ts b/public/docs/_examples/cb-component-communication/ts/app/version-parent.component.ts index cdb590b87a..bbc9101702 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/version-parent.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/version-parent.component.ts @@ -1,8 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { VersionChildComponent } from './version-child.component'; - @Component({ selector: 'version-parent', template: ` @@ -10,8 +8,7 @@ import { VersionChildComponent } from './version-child.component'; - `, - directives: [VersionChildComponent] + ` }) export class VersionParentComponent { major: number = 1; diff --git a/public/docs/_examples/cb-component-communication/ts/app/votetaker.component.ts b/public/docs/_examples/cb-component-communication/ts/app/votetaker.component.ts index 7267185ff1..87f06161f5 100644 --- a/public/docs/_examples/cb-component-communication/ts/app/votetaker.component.ts +++ b/public/docs/_examples/cb-component-communication/ts/app/votetaker.component.ts @@ -1,8 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { VoterComponent } from './voter.component'; - @Component({ selector: 'vote-taker', template: ` @@ -12,8 +10,7 @@ import { VoterComponent } from './voter.component'; [name]="voter" (onVoted)="onVoted($event)"> - `, - directives: [VoterComponent] + ` }) export class VoteTakerComponent { agreed = 0; diff --git a/public/docs/_examples/cb-component-relative-paths/ts/app/app.module.ts b/public/docs/_examples/cb-component-relative-paths/ts/app/app.module.ts new file mode 100644 index 0000000000..ab79a37ba7 --- /dev/null +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/app.module.ts @@ -0,0 +1,15 @@ +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/cb-component-relative-paths/ts/app/main.ts b/public/docs/_examples/cb-component-relative-paths/ts/app/main.ts index 42dbeb9f7d..6af7a5b2ae 100644 --- a/public/docs/_examples/cb-component-relative-paths/ts/app/main.ts +++ b/public/docs/_examples/cb-component-relative-paths/ts/app/main.ts @@ -1,5 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; +import { AppModule } from './app.module'; -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts index 578fb06730..ab0d9c445b 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/app.component.ts @@ -1,22 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { HeroBiosComponent, - HeroBiosAndContactsComponent } from './hero-bios.component'; -import { HeroOfTheMonthComponent } from './hero-of-the-month.component'; -import { HeroesBaseComponent, - SortedHeroesComponent } from './sorted-heroes.component'; -import { HighlightDirective } from './highlight.directive'; -import { ParentFinderComponent } from './parent-finder.component'; - -const DIRECTIVES = [ - HeroBiosComponent, HeroBiosAndContactsComponent, - HeroesBaseComponent, SortedHeroesComponent, - HeroOfTheMonthComponent, - HighlightDirective, - ParentFinderComponent -]; - // #docregion import-services import { LoggerService } from './logger.service'; import { UserContextService } from './user-context.service'; @@ -25,7 +9,6 @@ import { UserService } from './user.service'; @Component({ selector: 'my-app', templateUrl: 'app/app.component.html', - directives: DIRECTIVES, // #docregion providers providers: [LoggerService, UserContextService, UserService] // #enddocregion providers diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/app.module.ts b/public/docs/_examples/cb-dependency-injection/ts/app/app.module.ts new file mode 100644 index 0000000000..326ea4e945 --- /dev/null +++ b/public/docs/_examples/cb-dependency-injection/ts/app/app.module.ts @@ -0,0 +1,74 @@ +// #docregion +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { XHRBackend } from '@angular/http'; +// import { appRouterProviders } from './app.routes'; +import { LocationStrategy, + HashLocationStrategy } from '@angular/common'; +import { NgModule } from '@angular/core'; + +import { HeroData } from './hero-data'; +import { InMemoryBackendService, + SEED_DATA } from 'angular2-in-memory-web-api'; + +import { AppComponent } from './app.component'; +import { HeroBioComponent } from './hero-bio.component'; +import { HeroBiosComponent, + HeroBiosAndContactsComponent } from './hero-bios.component'; +import { HeroOfTheMonthComponent } from './hero-of-the-month.component'; +import { HeroContactComponent } from './hero-contact.component'; +import { HeroesBaseComponent, + SortedHeroesComponent } from './sorted-heroes.component'; +import { HighlightDirective } from './highlight.directive'; +import { ParentFinderComponent, + AlexComponent, + AliceComponent, + CarolComponent, + ChrisComponent, + CraigComponent, + CathyComponent, + BarryComponent, + BethComponent, + BobComponent } from './parent-finder.component'; + +const DIRECTIVES = [ + HeroBiosComponent, HeroBiosAndContactsComponent, HeroBioComponent, + HeroesBaseComponent, SortedHeroesComponent, + HeroOfTheMonthComponent, HeroContactComponent, + HighlightDirective, + ParentFinderComponent, + AppComponent +]; + +const B_DIRECTIVES = [ BarryComponent, BethComponent, BobComponent ]; + +// #docregion C_DIRECTIVES +const C_DIRECTIVES = [ + CarolComponent, ChrisComponent, CraigComponent, + CathyComponent +]; +// #enddocregion C_DIRECTIVES + +// #docregion bootstrap +@NgModule({ + imports: [ BrowserModule, FormsModule ], + declarations: [ ...DIRECTIVES, + ...B_DIRECTIVES, + ...C_DIRECTIVES, + AliceComponent, + AlexComponent ], + bootstrap: [ AppComponent ], + providers: [ + // appRouterProviders, TODO: add routes + { provide: LocationStrategy, useClass: HashLocationStrategy }, + + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: HeroData } // in-mem server data + ] +}) +export class AppModule { + constructor() { + } +} +// #enddocregion bootstraps + diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/hero-bios.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/hero-bios.component.ts index 8b738bf04f..217c5edcd0 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/hero-bios.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/hero-bios.component.ts @@ -2,8 +2,6 @@ // #docregion import { Component } from '@angular/core'; -import { HeroContactComponent } from './hero-contact.component'; -import { HeroBioComponent } from './hero-bio.component'; import { HeroService } from './hero.service'; import { LoggerService } from './logger.service'; @@ -15,7 +13,6 @@ import { LoggerService } from './logger.service'; `, - directives: [HeroBioComponent], providers: [HeroService] }) export class HeroBiosComponent { @@ -39,7 +36,6 @@ export class HeroBiosComponent { `, // #enddocregion template - directives: [HeroBioComponent, HeroContactComponent], // #docregion class-provider providers: [HeroService] // #enddocregion class-provider diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/main.ts b/public/docs/_examples/cb-dependency-injection/ts/app/main.ts index c83f329e91..4acf5de663 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/main.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/main.ts @@ -1,22 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { XHRBackend } from '@angular/http'; -import { appRouterProviders } from './app.routes'; -import { LocationStrategy, - HashLocationStrategy } from '@angular/common'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { HeroData } from './hero-data'; -import { InMemoryBackendService, - SEED_DATA } from 'angular2-in-memory-web-api'; - -import { AppComponent } from './app.component'; - -// #docregion bootstrap -bootstrap(AppComponent, [ - appRouterProviders, - { provide: LocationStrategy, useClass: HashLocationStrategy }, - - { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server - { provide: SEED_DATA, useClass: HeroData } // in-mem server data -]).catch((err: any) => console.error(err)); -// #enddocregion bootstrap +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts b/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts index a8e245376c..d5f87d51bd 100644 --- a/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts +++ b/public/docs/_examples/cb-dependency-injection/ts/app/parent-finder.component.ts @@ -85,13 +85,6 @@ export class CraigComponent { } // #enddocregion craig -// #docregion C_DIRECTIVES -const C_DIRECTIVES = [ - CarolComponent, ChrisComponent, CraigComponent, - forwardRef(() => CathyComponent) -]; -// #enddocregion C_DIRECTIVES - //////// B - Parent ///////// // #docregion barry const templateB = ` @@ -107,7 +100,6 @@ const templateB = ` @Component({ selector: 'barry', template: templateB, - directives: C_DIRECTIVES, providers: [{ provide: Parent, useExisting: forwardRef(() => BarryComponent) }] }) export class BarryComponent implements Parent { @@ -121,7 +113,6 @@ export class BarryComponent implements Parent { @Component({ selector: 'bob', template: templateB, - directives: C_DIRECTIVES, providers: [ provideParent(BobComponent) ] }) export class BobComponent implements Parent { @@ -132,7 +123,6 @@ export class BobComponent implements Parent { @Component({ selector: 'beth', template: templateB, - directives: C_DIRECTIVES, // #docregion beth-providers providers: [ provideParent(BethComponent, DifferentParent) ] // #enddocregion beth-providers @@ -142,8 +132,6 @@ export class BethComponent implements Parent { constructor( @SkipSelf() @Optional() public parent: Parent ) { } } -const B_DIRECTIVES = [ BarryComponent, BethComponent, BobComponent ]; - ///////// A - Grandparent ////// // #docregion alex, alex-1 @@ -161,7 +149,6 @@ const B_DIRECTIVES = [ BarryComponent, BethComponent, BobComponent ]; providers: [{ provide: Parent, useExisting: forwardRef(() => AlexComponent) }], // #enddocregion alex-providers // #docregion alex-1 - directives: C_DIRECTIVES }) // #enddocregion alex-1 // Todo: Add `... implements Parent` to class signature @@ -187,7 +174,6 @@ export class AlexComponent extends Base `, - directives: [ B_DIRECTIVES, C_DIRECTIVES ], // #docregion alice-providers providers: [ provideParent(AliceComponent) ] // #enddocregion alice-providers @@ -224,7 +210,6 @@ export class CathyComponent { template: `

    Parent Finder

    - `, - directives: [ AlexComponent, AliceComponent ] + ` }) export class ParentFinderComponent { } diff --git a/public/docs/_examples/cb-dynamic-form-deprecated/e2e-spec.ts b/public/docs/_examples/cb-dynamic-form-deprecated/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/cb-dynamic-form-deprecated/e2e-spec.ts rename to public/docs/_examples/cb-dynamic-form-deprecated/e2e-spec.ts.disabled diff --git a/public/docs/_examples/cb-dynamic-form-deprecated/ts/app/main.ts b/public/docs/_examples/cb-dynamic-form-deprecated/ts/app/main.ts index 5a8d9c5044..62ced34a31 100644 --- a/public/docs/_examples/cb-dynamic-form-deprecated/ts/app/main.ts +++ b/public/docs/_examples/cb-dynamic-form-deprecated/ts/app/main.ts @@ -2,5 +2,4 @@ import { bootstrap } from '@angular/platform-browser-dynamic'; import { AppComponent } from './app.component'; -bootstrap(AppComponent, []) - .catch((err: any) => console.error(err)); +bootstrap(AppComponent, []); diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts b/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts index e51561770e..582daced2e 100644 --- a/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts +++ b/public/docs/_examples/cb-dynamic-form/ts/app/app.component.ts @@ -1,7 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { DynamicFormComponent } from './dynamic-form.component'; import { QuestionService } from './question.service'; @Component({ @@ -12,7 +11,6 @@ import { QuestionService } from './question.service'; `, - directives: [DynamicFormComponent], providers: [QuestionService] }) export class AppComponent { diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/app.module.ts b/public/docs/_examples/cb-dynamic-form/ts/app/app.module.ts new file mode 100644 index 0000000000..7a68e45a92 --- /dev/null +++ b/public/docs/_examples/cb-dynamic-form/ts/app/app.module.ts @@ -0,0 +1,18 @@ +// #docregion +import { BrowserModule } from '@angular/platform-browser'; +import { ReactiveFormsModule } from '@angular/forms'; +import { NgModule } from '@angular/core'; + +import { AppComponent } from './app.component'; +import { DynamicFormComponent } from './dynamic-form.component'; +import { DynamicFormQuestionComponent } from './dynamic-form-question.component'; + +@NgModule({ + imports: [ BrowserModule, ReactiveFormsModule ], + declarations: [ AppComponent, DynamicFormComponent, DynamicFormQuestionComponent ], + bootstrap: [ AppComponent ] +}) +export class AppModule { + constructor() { + } +} diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts index 3381d1c444..412f2d8c55 100644 --- a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts +++ b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form-question.component.ts @@ -1,13 +1,12 @@ // #docregion import { Component, Input } from '@angular/core'; -import { FormGroup, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; +import { FormGroup } from '@angular/forms'; import { QuestionBase } from './question-base'; @Component({ selector: 'df-question', - templateUrl: 'app/dynamic-form-question.component.html', - directives: [REACTIVE_FORM_DIRECTIVES] + templateUrl: 'app/dynamic-form-question.component.html' }) export class DynamicFormQuestionComponent { @Input() question: QuestionBase; diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts index 15730806d1..68bd1f582b 100644 --- a/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts +++ b/public/docs/_examples/cb-dynamic-form/ts/app/dynamic-form.component.ts @@ -1,16 +1,14 @@ // #docregion import { Component, Input, OnInit } from '@angular/core'; -import { FormGroup, REACTIVE_FORM_DIRECTIVES } from '@angular/forms'; +import { FormGroup } from '@angular/forms'; -import { DynamicFormQuestionComponent } from './dynamic-form-question.component'; -import { QuestionBase } from './question-base'; -import { QuestionControlService } from './question-control.service'; +import { QuestionBase } from './question-base'; +import { QuestionControlService } from './question-control.service'; @Component({ selector: 'dynamic-form', templateUrl: 'app/dynamic-form.component.html', - directives: [DynamicFormQuestionComponent, REACTIVE_FORM_DIRECTIVES], - providers: [QuestionControlService] + providers: [ QuestionControlService ] }) export class DynamicFormComponent implements OnInit { diff --git a/public/docs/_examples/cb-dynamic-form/ts/app/main.ts b/public/docs/_examples/cb-dynamic-form/ts/app/main.ts index 461ce210c6..4acf5de663 100644 --- a/public/docs/_examples/cb-dynamic-form/ts/app/main.ts +++ b/public/docs/_examples/cb-dynamic-form/ts/app/main.ts @@ -1,11 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { disableDeprecatedForms, provideForms } from '@angular/forms'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent, [ - disableDeprecatedForms(), - provideForms() -]) -.catch((err: any) => console.error(err)); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-set-document-title/ts/app/app.module.ts b/public/docs/_examples/cb-set-document-title/ts/app/app.module.ts new file mode 100644 index 0000000000..81f13c244c --- /dev/null +++ b/public/docs/_examples/cb-set-document-title/ts/app/app.module.ts @@ -0,0 +1,19 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule, Title } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; + +@NgModule({ + imports: [ + BrowserModule + ], + declarations: [ + AppComponent + ], + providers: [ + Title + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/cb-set-document-title/ts/app/main.ts b/public/docs/_examples/cb-set-document-title/ts/app/main.ts index abee7afb87..ba9421d573 100644 --- a/public/docs/_examples/cb-set-document-title/ts/app/main.ts +++ b/public/docs/_examples/cb-set-document-title/ts/app/main.ts @@ -1,22 +1,6 @@ -/* tslint:disable */ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; +import { AppModule } from './app.module'; -// While Angular supplies a Title service for setting the HTML document title -// it doesn't include this service as part of the default Browser platform providers. -// As such, if we want to inject it into the components within our application, -// we have to explicitly provide the Angular service in our top component. -// #docregion bootstrap-title -import { Title } from '@angular/platform-browser'; - -bootstrap(AppComponent, [ Title ]) -// #enddocregion bootstrap-title - .then( - () => window.console.info( 'Angular finished bootstrapping your application!' ), - (error) => { - console.warn( 'Angular was not able to bootstrap your application.' ); - console.error( error ); - } - ); +browserDynamicPlatform().bootstrapModule(AppModule); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject-additional.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject-additional.component.js index 413ccfc8f3..153e83c06f 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject-additional.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject-additional.component.js @@ -36,12 +36,22 @@ selector: 'hero-di-inject-additional', template: '' + '' + - '', - directives: [TitleComponent] + '' }).Class({ constructor: function() { } }); - - app.HeroDIInjectAdditionalComponent = AppComponent; + + app.HeroesDIInjectAdditionalModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ + AppComponent, + TitleComponent + ], + bootstrap: [ AppComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject.component.js index 71bdbb57f9..dfda83e1f0 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inject.component.js @@ -15,7 +15,16 @@ ]; // #enddocregion parameters - app.HeroDIInjectComponent = HeroComponent; + app.HeroesDIInjectModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + providers: [ { provide: 'heroName', useValue: 'Windstorm' } ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); @@ -34,6 +43,15 @@ }); // #enddocregion ctor - app.HeroDIInjectComponent2 = HeroComponent; + app.HeroesDIInjectModule2 = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + providers: [ { provide: 'heroName', useValue: 'Bombasto' } ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inline.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inline.component.js index 90c3d69cba..1fe9c38cb1 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inline.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-di-inline.component.js @@ -12,5 +12,16 @@ }] }); // #enddocregion - app.HeroDIInlineComponent = HeroComponent; + + app.HeroDIInlineModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + providers: [ app.DataService ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); + })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-di.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-di.component.js index 2963258292..55c576b836 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-di.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-di.component.js @@ -17,4 +17,15 @@ ]; // #enddocregion + app.HeroesDIModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + providers: [ app.DataService ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); + })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-dsl.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-dsl.component.js index 2890e006a2..5dd84733f3 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-dsl.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-dsl.component.js @@ -17,7 +17,15 @@ }); // #enddocregion component - app.HeroComponentDsl = HeroComponent; + app.HeroesDslModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); // #enddocregion appexport diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-io.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-io.component.js index f00ce8065c..79aef12125 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-io.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-io.component.js @@ -42,8 +42,7 @@ '(cancel)="onCancel()">' + '' + 'OK clicked' + - 'Cancel clicked', - directives: [ConfirmComponent] + 'Cancel clicked' }) ]; AppComponent.prototype.onOk = function() { @@ -52,6 +51,18 @@ AppComponent.prototype.onCancel = function() { this.cancelClicked = true; } - app.HeroIOComponent = AppComponent; + + app.HeroesIOModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ + AppComponent, + ConfirmComponent + ], + bootstrap: [ AppComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero-lifecycle.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero-lifecycle.component.js index b8a2148a64..3e81c72e4e 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero-lifecycle.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero-lifecycle.component.js @@ -16,6 +16,14 @@ }; // #enddocregion - app.HeroLifecycleComponent = HeroComponent; + app.HeroesLifecycleModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/hero.component.js b/public/docs/_examples/cb-ts-to-js/js/app/hero.component.js index f741285617..c6a58acc56 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/hero.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/hero.component.js @@ -25,6 +25,16 @@ // #enddocregion constructorproto // #enddocregion metadata + app.HeroesModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] + }) + .Class({ + constructor: function() {} + }); + // #docregion appexport app.HeroComponent = HeroComponent; diff --git a/public/docs/_examples/cb-ts-to-js/js/app/heroes-bindings.component.js b/public/docs/_examples/cb-ts-to-js/js/app/heroes-bindings.component.js index 0c4ee7d7cf..3a562d6912 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/heroes-bindings.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/heroes-bindings.component.js @@ -25,6 +25,15 @@ } }); // #enddocregion - app.HeroesHostBindingsComponent = HeroesComponent; + + app.HeroesHostBindingsModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ HeroesComponent ], + bootstrap: [ HeroesComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/heroes-queries.component.js b/public/docs/_examples/cb-ts-to-js/js/app/heroes-queries.component.js index 17cb57093f..f1bda77cb1 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/heroes-queries.component.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/heroes-queries.component.js @@ -7,7 +7,7 @@ 'Active' + '' }).Class({ - constructor: function() { }, + constructor: [function() { }], activate: function() { this.active = true; } @@ -26,12 +26,13 @@ ActiveLabelComponent) } }).Class({ - constructor: function() { }, + constructor: [function() { }], activate: function() { this.active = true; this.label.activate(); } }); + app.HeroQueriesComponent = HeroComponent; // #enddocregion content // #docregion view @@ -44,11 +45,7 @@ '' + '', - directives: [ - HeroComponent, - ActiveLabelComponent - ], + '', queries: { heroCmps: new ng.core.ViewChildren( HeroComponent) @@ -68,6 +65,18 @@ }); // #enddocregion view - app.HeroesQueriesComponent = AppComponent; + app.HeroesQueriesModule = + ng.core.NgModule({ + imports: [ ng.platformBrowser.BrowserModule ], + declarations: [ + AppComponent, + HeroComponent, + ActiveLabelComponent + ], + bootstrap: [ AppComponent ] + }) + .Class({ + constructor: function() {} + }); })(window.app = window.app || {}); diff --git a/public/docs/_examples/cb-ts-to-js/js/app/main.js b/public/docs/_examples/cb-ts-to-js/js/app/main.js index f483a7c8cd..a1ec27c6ad 100644 --- a/public/docs/_examples/cb-ts-to-js/js/app/main.js +++ b/public/docs/_examples/cb-ts-to-js/js/app/main.js @@ -17,21 +17,20 @@ // #enddocregion appimport document.addEventListener('DOMContentLoaded', function() { - bootstrap(HeroComponent); - bootstrap(app.HeroComponentDsl); - bootstrap(app.HeroLifecycleComponent); - bootstrap(app.HeroDIComponent, [app.DataService]); - bootstrap(app.HeroDIInlineComponent, [app.DataService]); - bootstrap(app.HeroDIInjectComponent, [ - { provide: 'heroName', useValue: 'Windstorm' } - ]); - bootstrap(app.HeroDIInjectComponent2, [ - { provide: 'heroName', useValue: 'Bombasto' } - ]); - bootstrap(app.HeroDIInjectAdditionalComponent); - bootstrap(app.HeroIOComponent); - bootstrap(app.HeroesHostBindingsComponent); - bootstrap(app.HeroesQueriesComponent); + var platformBrowserDynamic = ng.platformBrowserDynamic.platformBrowserDynamic(); + + platformBrowserDynamic.bootstrapModule(app.HeroesModule); + platformBrowserDynamic.bootstrapModule(app.HeroesDslModule); + platformBrowserDynamic.bootstrapModule(app.HeroesLifecycleModule); + platformBrowserDynamic.bootstrapModule(app.HeroesDIModule); + platformBrowserDynamic.bootstrapModule(app.HeroDIInlineModule); + platformBrowserDynamic.bootstrapModule(app.HeroesDIInjectModule); + platformBrowserDynamic.bootstrapModule(app.HeroesDIInjectModule2); + platformBrowserDynamic.bootstrapModule(app.HeroesDIInjectAdditionalModule); + platformBrowserDynamic.bootstrapModule(app.HeroesIOModule); + platformBrowserDynamic.bootstrapModule(app.HeroesHostBindingsModule); + + platformBrowserDynamic.bootstrapModule(app.HeroesQueriesModule); }); // #docregion appimport diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject-additional.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject-additional.component.ts index da11e56b8a..bca1e88cb6 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject-additional.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject-additional.component.ts @@ -5,8 +5,10 @@ import { Inject, Optional, Query, - QueryList + QueryList, + NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; // #docregion @Component({ @@ -17,7 +19,7 @@ import { ` }) -export class TitleComponent { +class TitleComponent { constructor( @Inject('titlePrefix') @Optional() @@ -40,9 +42,16 @@ export class TitleComponent { selector: 'hero-di-inject-additional', template: ` - `, - directives: [TitleComponent] + ` }) -export class AppComponent { +class AppComponent { } -} +@NgModule({ + imports: [ BrowserModule ], + declarations: [ + AppComponent, + TitleComponent + ], + bootstrap: [ AppComponent ] +}) +export class HeroesDIInjectAdditionalModule { } diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject.component.ts index 4865f9eb22..7dc2bc84a2 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/hero-di-inject.component.ts @@ -1,14 +1,23 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; // #docregion @Component({ selector: 'hero-di-inject', template: `

    Hero: {{name}}

    ` }) -export class HeroComponent { +class HeroComponent { constructor( @Inject('heroName') private name: string) { } } // #enddocregion + +@NgModule({ + imports: [ BrowserModule ], + providers: [ { provide: 'heroName', useValue: 'Windstorm' } ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] +}) +export class HeroesDIInjectModule { } diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/hero-di.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/hero-di.component.ts index 2f3fa267ba..a20453ef0a 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/hero-di.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/hero-di.component.ts @@ -1,4 +1,5 @@ -import { Component } from '@angular/core'; +import { Component, NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; import { DataService } from './data.service'; @@ -7,10 +8,18 @@ import { DataService } from './data.service'; selector: 'hero-di', template: `

    Hero: {{name}}

    ` }) -export class HeroComponent { +class HeroComponent { name: string; constructor(dataService: DataService) { this.name = dataService.getHeroName(); } } // #enddocregion + +@NgModule({ + imports: [ BrowserModule ], + providers: [ DataService ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] +}) +export class HeroesDIModule { } diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/hero-io.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/hero-io.component.ts index a93b9ed08c..8cf430c000 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/hero-io.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/hero-io.component.ts @@ -1,4 +1,11 @@ -import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { + Component, + EventEmitter, + Input, + Output, + NgModule +} from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; // #docregion @Component({ @@ -12,7 +19,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; ` }) -export class ConfirmComponent { +class ConfirmComponent { @Input() okMsg: string; @Input('cancelMsg') notOkMsg: string; @Output() ok = @@ -40,10 +47,9 @@ export class ConfirmComponent { OK clicked Cancel clicked - `, - directives: [ConfirmComponent] + ` }) -export class AppComponent { +class AppComponent { okClicked: boolean; cancelClicked: boolean; @@ -54,3 +60,14 @@ export class AppComponent { this.cancelClicked = true; } } + + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ + AppComponent, + ConfirmComponent + ], + bootstrap: [ AppComponent ] +}) +export class HeroesIOModule { } diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/hero-lifecycle.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/hero-lifecycle.component.ts index c883658135..1181c71cc9 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/hero-lifecycle.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/hero-lifecycle.component.ts @@ -1,15 +1,16 @@ // #docplaster // #docregion -import { Component, OnInit } - from '@angular/core'; - // #enddocregion +import { Component, OnInit } from '@angular/core'; +// #enddocregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; @Component({ selector: 'hero-lifecycle', template: `

    Hero: {{name}}

    ` }) // #docregion -export class HeroComponent +class HeroComponent implements OnInit { name: string; ngOnInit() { @@ -17,3 +18,11 @@ export class HeroComponent } } // #enddocregion + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] +}) +export class HeroesLifecycleModule { } + diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/hero.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/hero.component.ts index a1628a1104..4b4ba8529d 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/hero.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/hero.component.ts @@ -16,3 +16,15 @@ export class HeroComponent { // #enddocregion class // #enddocregion appexport // #enddocregion metadata + +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ HeroComponent ], + bootstrap: [ HeroComponent ] +}) +export class HeroesModule { } + + diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/heroes-bindings.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/heroes-bindings.component.ts index 71c75bb5c8..407b8b0e29 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/heroes-bindings.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/heroes-bindings.component.ts @@ -1,13 +1,19 @@ -import { Component, HostBinding, HostListener } from '@angular/core'; +import { + Component, + HostBinding, + HostListener, + NgModule +} from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; // #docregion @Component({ selector: 'heroes-bindings', template: `

    - Tour ofHeroes + Tour of Heroes

    ` }) -export class HeroesComponent { +class HeroesComponent { @HostBinding() title = 'Tooltip content'; @HostBinding('class.heading') hClass = true; @@ -26,3 +32,10 @@ export class HeroesComponent { } } // #enddocregion + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ HeroesComponent ], + bootstrap: [ HeroesComponent ] +}) +export class HeroesHostBindingsModule { } diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/heroes-queries.component.ts b/public/docs/_examples/cb-ts-to-js/ts/app/heroes-queries.component.ts index b511f29f90..97003766e6 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/heroes-queries.component.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/heroes-queries.component.ts @@ -3,8 +3,10 @@ import { ViewChildren, ContentChild, QueryList, - Input + Input, + NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; @Component({ selector: 'active-label', @@ -55,13 +57,9 @@ class HeroComponent { - `, - directives: [ - HeroComponent, - ActiveLabelComponent - ] + ` }) -export class HeroesQueriesComponent { +class HeroesQueriesComponent { heroData = [ {id: 1, name: 'Windstorm'}, {id: 2, name: 'Superman'} @@ -77,3 +75,14 @@ export class HeroesQueriesComponent { } } // #enddocregion view + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ + HeroesQueriesComponent, + HeroComponent, + ActiveLabelComponent + ], + bootstrap: [ HeroesQueriesComponent ] +}) +export class HeroesQueriesModule { } diff --git a/public/docs/_examples/cb-ts-to-js/ts/app/main.ts b/public/docs/_examples/cb-ts-to-js/ts/app/main.ts index 9e1540abc2..c4549d7034 100644 --- a/public/docs/_examples/cb-ts-to-js/ts/app/main.ts +++ b/public/docs/_examples/cb-ts-to-js/ts/app/main.ts @@ -9,26 +9,25 @@ import { // #enddocregion ng2import // #docregion appimport -import { HeroComponent } - from './hero.component'; +import { HeroComponent } from './hero.component'; // #enddocregion appimport -import { HeroComponent as HeroLifecycleComponent } from './hero-lifecycle.component'; -import { HeroComponent as HeroDIComponent } from './hero-di.component'; -import { HeroComponent as HeroDIInjectComponent } from './hero-di-inject.component'; -import { AppComponent as AppDIInjectAdditionalComponent } from './hero-di-inject-additional.component'; -import { AppComponent as AppIOComponent } from './hero-io.component'; -import { HeroesComponent as HeroesHostBindingsComponent } from './heroes-bindings.component'; -import { HeroesQueriesComponent } from './heroes-queries.component'; -import { DataService } from './data.service'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -bootstrap(HeroComponent); -bootstrap(HeroLifecycleComponent); -bootstrap(HeroDIComponent, [DataService]); -bootstrap(HeroDIInjectComponent, [ - { provide: 'heroName', useValue: 'Windstorm' } -]); -bootstrap(AppDIInjectAdditionalComponent); -bootstrap(AppIOComponent); -bootstrap(HeroesHostBindingsComponent); -bootstrap(HeroesQueriesComponent); +import { HeroesModule } from './hero.component'; +import { HeroesLifecycleModule } from './hero-lifecycle.component'; +import { HeroesDIModule } from './hero-di.component'; +import { HeroesDIInjectModule } from './hero-di-inject.component'; +import { HeroesDIInjectAdditionalModule } from './hero-di-inject-additional.component'; +import { HeroesIOModule } from './hero-io.component'; +import { HeroesHostBindingsModule } from './heroes-bindings.component'; +import { HeroesQueriesModule } from './heroes-queries.component'; + +platformBrowserDynamic().bootstrapModule(HeroesModule); +platformBrowserDynamic().bootstrapModule(HeroesLifecycleModule); +platformBrowserDynamic().bootstrapModule(HeroesDIModule); +platformBrowserDynamic().bootstrapModule(HeroesDIInjectModule); +platformBrowserDynamic().bootstrapModule(HeroesDIInjectAdditionalModule); +platformBrowserDynamic().bootstrapModule(HeroesIOModule); +platformBrowserDynamic().bootstrapModule(HeroesHostBindingsModule); +platformBrowserDynamic().bootstrapModule(HeroesQueriesModule); diff --git a/public/docs/_examples/cli-quickstart/e2e-spec.ts b/public/docs/_examples/cli-quickstart/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/cli-quickstart/e2e-spec.ts rename to public/docs/_examples/cli-quickstart/e2e-spec.ts.disabled diff --git a/public/docs/_examples/component-styles/ts/app/app.module.ts b/public/docs/_examples/component-styles/ts/app/app.module.ts new file mode 100644 index 0000000000..31c72cbbf2 --- /dev/null +++ b/public/docs/_examples/component-styles/ts/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/app/hero-app-main.component.ts b/public/docs/_examples/component-styles/ts/app/hero-app-main.component.ts index fb5b5d9d98..aebb1f729e 100644 --- a/public/docs/_examples/component-styles/ts/app/hero-app-main.component.ts +++ b/public/docs/_examples/component-styles/ts/app/hero-app-main.component.ts @@ -1,9 +1,6 @@ import { Component, Input } from '@angular/core'; import { Hero } from './hero'; -import { HeroDetailsComponent } from './hero-details.component'; -import { HeroControlsComponent } from './hero-controls.component'; -import { QuestSummaryComponent } from './quest-summary.component'; @Component({ selector: 'hero-app-main', @@ -12,8 +9,7 @@ import { QuestSummaryComponent } from './quest-summary.component'; - `, - directives: [HeroDetailsComponent, HeroControlsComponent, QuestSummaryComponent] + ` }) export class HeroAppMainComponent { @Input() hero: Hero; diff --git a/public/docs/_examples/component-styles/ts/app/hero-app.component.ts b/public/docs/_examples/component-styles/ts/app/hero-app.component.ts index 7386226e71..5f1923e6f3 100644 --- a/public/docs/_examples/component-styles/ts/app/hero-app.component.ts +++ b/public/docs/_examples/component-styles/ts/app/hero-app.component.ts @@ -1,6 +1,5 @@ import { Component, HostBinding } from '@angular/core'; import { Hero } from './hero'; -import { HeroAppMainComponent } from './hero-app-main.component'; // #docregion @Component({ @@ -8,8 +7,7 @@ import { HeroAppMainComponent } from './hero-app-main.component'; template: `

    Tour of Heroes

    `, - styles: ['h1 { font-weight: normal; }'], - directives: [HeroAppMainComponent] + styles: ['h1 { font-weight: normal; }'] }) export class HeroAppComponent { // #enddocregion diff --git a/public/docs/_examples/component-styles/ts/app/hero-details.component.ts b/public/docs/_examples/component-styles/ts/app/hero-details.component.ts index f530ec0757..bd86a63e04 100644 --- a/public/docs/_examples/component-styles/ts/app/hero-details.component.ts +++ b/public/docs/_examples/component-styles/ts/app/hero-details.component.ts @@ -1,6 +1,5 @@ import { Component, Input } from '@angular/core'; import { Hero } from './hero'; -import { HeroTeamComponent } from './hero-team.component'; // #docregion styleurls @Component({ @@ -10,8 +9,7 @@ import { HeroTeamComponent } from './hero-team.component'; `, - styleUrls: ['app/hero-details.component.css'], - directives: [HeroTeamComponent] + styleUrls: ['app/hero-details.component.css'] }) export class HeroDetailsComponent { // #enddocregion styleurls diff --git a/public/docs/_examples/component-styles/ts/app/main.ts b/public/docs/_examples/component-styles/ts/app/main.ts index 1d1e75499c..4acf5de663 100644 --- a/public/docs/_examples/component-styles/ts/app/main.ts +++ b/public/docs/_examples/component-styles/ts/app/main.ts @@ -1,4 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { HeroAppComponent } from './hero-app.component'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -bootstrap(HeroAppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/dependency-injection/ts/app/app.component.1.ts b/public/docs/_examples/dependency-injection/ts/app/app.component.1.ts index a67946ac19..b398ebeb57 100644 --- a/public/docs/_examples/dependency-injection/ts/app/app.component.1.ts +++ b/public/docs/_examples/dependency-injection/ts/app/app.component.1.ts @@ -3,17 +3,13 @@ // #docregion import { Component } from '@angular/core'; -import { CarComponent } from './car/car.component'; -import { HeroesComponent } from './heroes/heroes.component.1'; - @Component({ selector: 'my-app', template: `

    {{title}}

    - `, - directives: [CarComponent, HeroesComponent] + ` }) export class AppComponent { diff --git a/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts b/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts index a6106cbc4b..d24df5568c 100644 --- a/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts +++ b/public/docs/_examples/dependency-injection/ts/app/app.component.2.ts @@ -1,13 +1,9 @@ // #docregion // #docregion imports import { Component } from '@angular/core'; -import { CarComponent } from './car/car.component'; -import { HeroesComponent } from './heroes/heroes.component.1'; - import { Inject } from '@angular/core'; -import { APP_CONFIG, AppConfig, - HERO_DI_CONFIG } from './app.config'; -import { Logger } from './logger.service'; + +import { APP_CONFIG, AppConfig } from './app.config'; // #enddocregion imports @Component({ @@ -16,14 +12,7 @@ import { Logger } from './logger.service';

    {{title}}

    - `, - directives: [CarComponent, HeroesComponent], - providers: [ - Logger, - // #docregion providers - { provide: APP_CONFIG, useValue: HERO_DI_CONFIG } - // #enddocregion providers - ] + ` }) export class AppComponent { title: string; diff --git a/public/docs/_examples/dependency-injection/ts/app/app.component.ts b/public/docs/_examples/dependency-injection/ts/app/app.component.ts index bb330c9441..9a50661a84 100644 --- a/public/docs/_examples/dependency-injection/ts/app/app.component.ts +++ b/public/docs/_examples/dependency-injection/ts/app/app.component.ts @@ -3,18 +3,10 @@ // #docregion imports import { Component, Inject } from '@angular/core'; -import { CarComponent } from './car/car.component'; -import { HeroesComponent } from './heroes/heroes.component'; - -import { APP_CONFIG, AppConfig, - HERO_DI_CONFIG } from './app.config'; -import { Logger } from './logger.service'; - +import { APP_CONFIG, AppConfig } from './app.config'; +import { Logger } from './logger.service'; import { UserService } from './user.service'; // #enddocregion imports -import { InjectorComponent } from './injector.component'; -import { TestComponent } from './test.component'; -import { ProvidersComponent } from './providers.component'; @Component({ selector: 'my-app', @@ -31,15 +23,7 @@ import { ProvidersComponent } from './providers.component'; `, - directives: [CarComponent, HeroesComponent, - InjectorComponent, TestComponent, ProvidersComponent], - // #docregion providers - providers: [ - Logger, - UserService, - { provide: APP_CONFIG, useValue: HERO_DI_CONFIG } - ] - // #enddocregion providers + providers: [Logger] }) export class AppComponent { title: string; diff --git a/public/docs/_examples/dependency-injection/ts/app/app.module.ts b/public/docs/_examples/dependency-injection/ts/app/app.module.ts new file mode 100644 index 0000000000..439c9ff276 --- /dev/null +++ b/public/docs/_examples/dependency-injection/ts/app/app.module.ts @@ -0,0 +1,36 @@ +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 { ProvidersComponent } from './providers.component'; +import { APP_CONFIG, HERO_DI_CONFIG } from './app.config'; +import { UserService } from './user.service'; + +// #docregion ngmodule +@NgModule({ + imports: [ + BrowserModule + ], + declarations: [ + AppComponent, + CarComponent, + HeroesComponent, + HeroListComponent, + InjectorComponent, + TestComponent + ], + // #docregion ngmodule-providers + providers: [ + UserService, + { provide: APP_CONFIG, useValue: HERO_DI_CONFIG } + ], + // #enddocregion ngmodule-providers + bootstrap: [ AppComponent, ProvidersComponent ] +}) +export class AppModule { } +// #enddocregion ngmodule diff --git a/public/docs/_examples/dependency-injection/ts/app/heroes/hero-list.component.2.ts b/public/docs/_examples/dependency-injection/ts/app/heroes/hero-list.component.2.ts index 6b55f78a6e..cb23d3257c 100644 --- a/public/docs/_examples/dependency-injection/ts/app/heroes/hero-list.component.2.ts +++ b/public/docs/_examples/dependency-injection/ts/app/heroes/hero-list.component.2.ts @@ -18,7 +18,7 @@ import { HeroService } from './hero.service';
    {{hero.id}} - {{hero.name}}
    - `, + ` }) export class HeroListComponent { heroes: Hero[]; diff --git a/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.1.ts b/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.1.ts index b089dba80f..e0e9deae08 100644 --- a/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.1.ts +++ b/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.1.ts @@ -1,28 +1,21 @@ // #docplaster // #docregion full, v1 import { Component } from '@angular/core'; - -// #enddocregion full, v1 -import { HeroListComponent } from './hero-list.component.2'; -import { HeroService } from './hero.service.1'; -/* -// #docregion full, v1 -import { HeroListComponent } from './hero-list.component'; // #enddocregion v1 + import { HeroService } from './hero.service'; // #enddocregion full -*/ + // #docregion full, v1 @Component({ selector: 'my-heroes', + // #enddocregion v1 + providers: [HeroService], + // #docregion v1 template: `

    Heroes

    - `, - // #enddocregion v1 - providers: [HeroService], - // #docregion v1 - directives: [HeroListComponent] + ` }) export class HeroesComponent { } diff --git a/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.ts b/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.ts index dd2346248c..5923f7590b 100644 --- a/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.ts +++ b/public/docs/_examples/dependency-injection/ts/app/heroes/heroes.component.ts @@ -1,7 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { HeroListComponent } from './hero-list.component'; import { heroServiceProvider } from './hero.service.provider'; @Component({ @@ -10,7 +9,6 @@ import { heroServiceProvider } from './hero.service.provider';

    Heroes

    `, - providers: [heroServiceProvider], - directives: [HeroListComponent] + providers: [heroServiceProvider] }) export class HeroesComponent { } diff --git a/public/docs/_examples/dependency-injection/ts/app/main.1.ts b/public/docs/_examples/dependency-injection/ts/app/main.1.ts deleted file mode 100644 index 0d83503360..0000000000 --- a/public/docs/_examples/dependency-injection/ts/app/main.1.ts +++ /dev/null @@ -1,13 +0,0 @@ -/* tslint:disable:no-unused-variable */ -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component.1'; -import { HeroService } from './heroes/hero.service.1'; - -bootstrap(AppComponent); - -function discouraged() { - // #docregion bootstrap-discouraged - bootstrap(AppComponent, - [HeroService]); // DISCOURAGED (but works) - // #enddocregion bootstrap-discouraged -} diff --git a/public/docs/_examples/dependency-injection/ts/app/main.ts b/public/docs/_examples/dependency-injection/ts/app/main.ts index 2f002de7d9..4acb58eab5 100644 --- a/public/docs/_examples/dependency-injection/ts/app/main.ts +++ b/public/docs/_examples/dependency-injection/ts/app/main.ts @@ -1,8 +1,6 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; -import { ProvidersComponent } from './providers.component'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; // #docregion bootstrap -bootstrap(AppComponent); +browserDynamicPlatform().bootstrapModule(AppModule); // #enddocregion bootstrap -bootstrap(ProvidersComponent); diff --git a/public/docs/_examples/displaying-data/ts/app/app.component.3.ts b/public/docs/_examples/displaying-data/ts/app/app.component.3.ts index 0705176292..06ab060557 100644 --- a/public/docs/_examples/displaying-data/ts/app/app.component.3.ts +++ b/public/docs/_examples/displaying-data/ts/app/app.component.3.ts @@ -1,5 +1,6 @@ // #docregion import { Component } from '@angular/core'; + // #docregion import import { Hero } from './hero'; // #enddocregion import diff --git a/public/docs/_examples/displaying-data/ts/app/app.component.ts b/public/docs/_examples/displaying-data/ts/app/app.component.ts index 40c90e2d62..7234959265 100644 --- a/public/docs/_examples/displaying-data/ts/app/app.component.ts +++ b/public/docs/_examples/displaying-data/ts/app/app.component.ts @@ -1,6 +1,7 @@ // #docplaster // #docregion final import { Component } from '@angular/core'; + import { Hero } from './hero'; @Component({ diff --git a/public/docs/_examples/displaying-data/ts/app/app.module.ts b/public/docs/_examples/displaying-data/ts/app/app.module.ts new file mode 100644 index 0000000000..362f3401fa --- /dev/null +++ b/public/docs/_examples/displaying-data/ts/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/app/main.ts b/public/docs/_examples/displaying-data/ts/app/main.ts index 52b47899ef..961a226688 100644 --- a/public/docs/_examples/displaying-data/ts/app/main.ts +++ b/public/docs/_examples/displaying-data/ts/app/main.ts @@ -1,6 +1,6 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; +import { AppModule } from './app.module'; -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/forms-deprecated/e2e-spec.ts b/public/docs/_examples/forms-deprecated/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/forms-deprecated/e2e-spec.ts rename to public/docs/_examples/forms-deprecated/e2e-spec.ts.disabled diff --git a/public/docs/_examples/forms/js/app/app.component.js b/public/docs/_examples/forms/js/app/app.component.js index bb6b789938..56bd982416 100644 --- a/public/docs/_examples/forms/js/app/app.component.js +++ b/public/docs/_examples/forms/js/app/app.component.js @@ -3,8 +3,7 @@ app.AppComponent = ng.core .Component({ selector: 'my-app', - template: '', - directives: [app.HeroFormComponent] + template: '' }) .Class({ constructor: function() {} diff --git a/public/docs/_examples/forms/js/app/app.module.js b/public/docs/_examples/forms/js/app/app.module.js new file mode 100644 index 0000000000..92c7f8b9e5 --- /dev/null +++ b/public/docs/_examples/forms/js/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/app/hero-form.component.js b/public/docs/_examples/forms/js/app/hero-form.component.js index 8988231189..428ff82d35 100644 --- a/public/docs/_examples/forms/js/app/hero-form.component.js +++ b/public/docs/_examples/forms/js/app/hero-form.component.js @@ -9,7 +9,7 @@ }) .Class({ // #docregion submitted - constructor: function() { + constructor: [function() { // #enddocregion submitted this.powers = ['Really Smart', 'Super Flexible', 'Super Hot', 'Weather Changer' @@ -20,7 +20,7 @@ // #docregion submitted this.submitted = false; - }, + }], onSubmit: function() { this.submitted = true; }, diff --git a/public/docs/_examples/forms/js/app/main.js b/public/docs/_examples/forms/js/app/main.js index 2b866b3685..785823fa84 100644 --- a/public/docs/_examples/forms/js/app/main.js +++ b/public/docs/_examples/forms/js/app/main.js @@ -1,9 +1,8 @@ // #docregion (function(app) { document.addEventListener('DOMContentLoaded', function() { - ng.platformBrowserDynamic.bootstrap(app.AppComponent,[ - ng.forms.disableDeprecatedForms(), - ng.forms.provideForms() - ]); + ng.platformBrowserDynamic + .platformBrowserDynamic() + .bootstrapModule(app.AppModule); }); })(window.app || (window.app = {})); diff --git a/public/docs/_examples/forms/js/index.html b/public/docs/_examples/forms/js/index.html index 8353dfb180..fe525d5549 100644 --- a/public/docs/_examples/forms/js/index.html +++ b/public/docs/_examples/forms/js/index.html @@ -35,6 +35,7 @@ + diff --git a/public/docs/_examples/forms/ts/app/app.component.ts b/public/docs/_examples/forms/ts/app/app.component.ts index 2dcbc8037a..454f7e03db 100644 --- a/public/docs/_examples/forms/ts/app/app.component.ts +++ b/public/docs/_examples/forms/ts/app/app.component.ts @@ -1,10 +1,8 @@ // #docregion -import { Component } from '@angular/core'; -import { HeroFormComponent } from './hero-form.component'; +import { Component } from '@angular/core'; @Component({ selector: 'my-app', - template: '', - directives: [HeroFormComponent] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/forms/ts/app/app.module.ts b/public/docs/_examples/forms/ts/app/app.module.ts new file mode 100644 index 0000000000..f214c02714 --- /dev/null +++ b/public/docs/_examples/forms/ts/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/app/hero-form.component.html b/public/docs/_examples/forms/ts/app/hero-form.component.html index 80401a1313..4b90a9464f 100644 --- a/public/docs/_examples/forms/ts/app/hero-form.component.html +++ b/public/docs/_examples/forms/ts/app/hero-form.component.html @@ -11,11 +11,13 @@
    - + -
    +
    Name is required
    @@ -24,16 +26,16 @@
    - +
    -
    @@ -111,19 +113,19 @@
    - +
    - +
    -
    @@ -147,20 +149,22 @@ {{diagnostic}}
    - +
    - +
    -
    @@ -175,15 +179,17 @@
    - + TODO: remove this: {{model.name}}
    - + TODO: remove this: {{model.name}}
    @@ -192,15 +198,16 @@ - +
    - +
    TODO: remove this: {{spy.className}}
    diff --git a/public/docs/_examples/forms/ts/app/hero-form.component.ts b/public/docs/_examples/forms/ts/app/hero-form.component.ts index e1a19c5cd9..fb08f7833b 100644 --- a/public/docs/_examples/forms/ts/app/hero-form.component.ts +++ b/public/docs/_examples/forms/ts/app/hero-form.component.ts @@ -2,7 +2,6 @@ // #docregion // #docregion first, final import { Component } from '@angular/core'; -import { NgForm } from '@angular/forms'; import { Hero } from './hero'; @@ -51,7 +50,7 @@ export class HeroFormComponent { // Reveal in html: // Name via form.controls = {{showFormControls(heroForm)}} - showFormControls(form: NgForm) { + showFormControls(form: any) { return form && form.controls['name'] && // #docregion form-controls diff --git a/public/docs/_examples/forms/ts/app/main.ts b/public/docs/_examples/forms/ts/app/main.ts index 9e0244c26a..0cfb0afb43 100644 --- a/public/docs/_examples/forms/ts/app/main.ts +++ b/public/docs/_examples/forms/ts/app/main.ts @@ -1,11 +1,8 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { disableDeprecatedForms, provideForms } from '@angular/forms'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent, [ - disableDeprecatedForms(), - provideForms() - ]) - .catch((err: any) => console.error(err)); +// Compiles the module (asynchronously) with the runtime compiler +// which generates a compiled module factory in memory. +// Then bootstraps with that factory, targeting the browser. +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/forms/ts/index.html b/public/docs/_examples/forms/ts/index.html index 4df2d32d46..7d6e7cda5d 100644 --- a/public/docs/_examples/forms/ts/index.html +++ b/public/docs/_examples/forms/ts/index.html @@ -7,7 +7,8 @@ - + diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/app/app.module.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/app/app.module.ts new file mode 100644 index 0000000000..80047b8e32 --- /dev/null +++ b/public/docs/_examples/hierarchical-dependency-injection/ts/app/app.module.ts @@ -0,0 +1,43 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +import { HeroesListComponent } from './heroes-list.component'; +import { HeroEditorComponent } from './hero-editor.component'; +import { HeroCardComponent } from './hero-card.component'; +import { HeroesService } from './heroes.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + providers: [ HeroesService ], + declarations: [ + HeroesListComponent, + HeroCardComponent, + HeroEditorComponent + ], + bootstrap: [ HeroesListComponent ] +}) +export class AppModule { } + +/* Documentation artifact below +// #docregion bad-alternative +// Don't do this! +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + providers: [ HeroesService, RestoreService ], + declarations: [ HeroesListComponent ], + bootstrap: [ + HeroesListComponent, + HeroCardComponent, + HeroEditorComponent + ] +}) +// #enddocregion bad-alternative +*/ diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/app/heroes-list.component.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/app/heroes-list.component.ts index 0fef36e205..af1ece3376 100644 --- a/public/docs/_examples/hierarchical-dependency-injection/ts/app/heroes-list.component.ts +++ b/public/docs/_examples/hierarchical-dependency-injection/ts/app/heroes-list.component.ts @@ -3,8 +3,6 @@ import { Component } from '@angular/core'; import { EditItem } from './edit-item'; import { HeroesService } from './heroes.service'; -import { HeroCardComponent } from './hero-card.component'; -import { HeroEditorComponent } from './hero-editor.component'; import { Hero } from './hero'; @Component({ @@ -30,8 +28,7 @@ import { Hero } from './hero'; -
    `, - directives: [HeroCardComponent, HeroEditorComponent] +
    ` }) export class HeroesListComponent { heroes: Array>; diff --git a/public/docs/_examples/hierarchical-dependency-injection/ts/app/main.ts b/public/docs/_examples/hierarchical-dependency-injection/ts/app/main.ts index d24e5a3274..4acf5de663 100644 --- a/public/docs/_examples/hierarchical-dependency-injection/ts/app/main.ts +++ b/public/docs/_examples/hierarchical-dependency-injection/ts/app/main.ts @@ -1,14 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { HeroesListComponent } from './heroes-list.component'; -import { HeroesService } from './heroes.service'; - -bootstrap(HeroesListComponent, [HeroesService]); - -/* Documentation artifact below -// #docregion bad-alternative -// Don't do this! -bootstrap(HeroesListComponent, [HeroesService, RestoreService]) -// #enddocregion bad-alternative -*/ +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/homepage-hello-world/ts/app/app.module.ts b/public/docs/_examples/homepage-hello-world/ts/app/app.module.ts new file mode 100644 index 0000000000..55a2ef3693 --- /dev/null +++ b/public/docs/_examples/homepage-hello-world/ts/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/app/main.ts b/public/docs/_examples/homepage-hello-world/ts/app/main.ts index c0b99d1b7b..4acf5de663 100644 --- a/public/docs/_examples/homepage-hello-world/ts/app/main.ts +++ b/public/docs/_examples/homepage-hello-world/ts/app/main.ts @@ -1,6 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { HelloWorldComponent } from './hello_world'; - -bootstrap(HelloWorldComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/homepage-todo/ts/app/app.module.ts b/public/docs/_examples/homepage-todo/ts/app/app.module.ts new file mode 100644 index 0000000000..344a98f417 --- /dev/null +++ b/public/docs/_examples/homepage-todo/ts/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 { TodoAppComponent } from './todo_app'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + declarations: [ TodoAppComponent ], + bootstrap: [ TodoAppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/homepage-todo/ts/app/main.ts b/public/docs/_examples/homepage-todo/ts/app/main.ts index 97ed5fa913..4acf5de663 100644 --- a/public/docs/_examples/homepage-todo/ts/app/main.ts +++ b/public/docs/_examples/homepage-todo/ts/app/main.ts @@ -1,6 +1,5 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { TodoAppComponent } from './todo_app'; - -bootstrap(TodoAppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/lifecycle-hooks/e2e-spec.ts b/public/docs/_examples/lifecycle-hooks/e2e-spec.ts index 535a3c1999..f86218e9a8 100644 --- a/public/docs/_examples/lifecycle-hooks/e2e-spec.ts +++ b/public/docs/_examples/lifecycle-hooks/e2e-spec.ts @@ -92,7 +92,7 @@ describe('Lifecycle hooks', function () { 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 childViewInputEle = parentEle.element(by.css('my-child-view input')); let logCount: number; expect(childViewInputEle.getAttribute('value')).toContain('Magneta'); diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/after-content.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/after-content.component.ts index 279a609ed3..dfd2b80d24 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/after-content.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/after-content.component.ts @@ -97,8 +97,7 @@ export class AfterContentComponent implements AfterContentChecked, AfterContentI
    `, styles: ['.parent {background: burlywood}'], - providers: [LoggerService], - directives: [AfterContentComponent, ChildComponent] + providers: [LoggerService] }) export class AfterContentParentComponent { logs: string[]; diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/after-view.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/after-view.component.ts index 000cf348eb..71310e4530 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/after-view.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/after-view.component.ts @@ -7,7 +7,7 @@ import { LoggerService } from './logger.service'; ////////////////// // #docregion child-view @Component({ - selector: 'my-child', + selector: 'my-child-view', template: '' }) export class ChildViewComponent { @@ -21,15 +21,14 @@ export class ChildViewComponent { // #docregion template template: `
    -- child view begins --
    - +
    -- child view ends --
    ` // #enddocregion template + `

    {{comment}}

    - `, - directives: [ChildViewComponent] + ` }) // #docregion hooks export class AfterViewComponent implements AfterViewChecked, AfterViewInit { @@ -100,8 +99,7 @@ export class AfterViewComponent implements AfterViewChecked, AfterViewInit {
    `, styles: ['.parent {background: burlywood}'], - providers: [LoggerService], - directives: [AfterViewComponent] + providers: [LoggerService] }) export class AfterViewParentComponent { logs: string[]; diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/app.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/app.component.ts index b3d5e45b76..9b785144ab 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/app.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/app.component.ts @@ -1,26 +1,7 @@ // #docregion import { Component } from '@angular/core'; - -import { AfterContentParentComponent } from './after-content.component'; -import { AfterViewParentComponent } from './after-view.component'; -import { CounterParentComponent } from './counter.component'; -import { DoCheckParentComponent } from './do-check.component'; -import { OnChangesParentComponent } from './on-changes.component'; -import { PeekABooParentComponent } from './peek-a-boo-parent.component'; -import { SpyParentComponent } from './spy.component'; - @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: [ - AfterContentParentComponent, - AfterViewParentComponent, - CounterParentComponent, - DoCheckParentComponent, - OnChangesParentComponent, - PeekABooParentComponent, - SpyParentComponent, - ] + templateUrl: 'app/app.component.html' }) -export class AppComponent { -} +export class AppComponent { } diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/app.module.ts b/public/docs/_examples/lifecycle-hooks/ts/app/app.module.ts new file mode 100644 index 0000000000..4d9cabf218 --- /dev/null +++ b/public/docs/_examples/lifecycle-hooks/ts/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/app/counter.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/counter.component.ts index 6514873232..71167ab791 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/counter.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/counter.component.ts @@ -4,7 +4,6 @@ import { OnChanges, SimpleChange, } from '@angular/core'; -import { SpyDirective } from './spy.directive'; import { LoggerService } from './logger.service'; @Component({ @@ -17,8 +16,7 @@ import { LoggerService } from './logger.service';
    {{chg}}
    `, - styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'], - directives: [SpyDirective] + styles: ['.counter {background: LightYellow; padding: 8px; margin-top: 8px}'] }) export class MyCounterComponent implements OnChanges { @Input() counter: number; @@ -59,7 +57,6 @@ export class MyCounterComponent implements OnChanges { `, styles: ['.parent {background: gold;}'], - directives: [MyCounterComponent], providers: [LoggerService] }) export class CounterParentComponent { diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/do-check-parent.component.html b/public/docs/_examples/lifecycle-hooks/ts/app/do-check-parent.component.html new file mode 100644 index 0000000000..cf7c2b91ce --- /dev/null +++ b/public/docs/_examples/lifecycle-hooks/ts/app/do-check-parent.component.html @@ -0,0 +1,13 @@ +
    +

    {{title}}

    + + + + +
    Power:
    Hero.name:
    +

    + + + + +
    diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/do-check.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/do-check.component.ts index 64b998c25a..1ffdd23ba7 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/do-check.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/do-check.component.ts @@ -86,9 +86,8 @@ export class DoCheckComponent implements DoCheck, OnChanges { @Component({ selector: 'do-check-parent', - templateUrl: 'app/on-changes-parent.component.html', - styles: ['.parent {background: Lavender}'], - directives: [DoCheckComponent] + templateUrl: 'app/do-check-parent.component.html', + styles: ['.parent {background: Lavender}'] }) export class DoCheckParentComponent { hero: Hero; diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/main.ts b/public/docs/_examples/lifecycle-hooks/ts/app/main.ts index 1e9be2601e..4acf5de663 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/main.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/main.ts @@ -1,5 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent).catch(err => console.error(err)); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/on-changes-parent.component.html b/public/docs/_examples/lifecycle-hooks/ts/app/on-changes-parent.component.html index 7889ce8e91..5c76a6056c 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/on-changes-parent.component.html +++ b/public/docs/_examples/lifecycle-hooks/ts/app/on-changes-parent.component.html @@ -10,5 +10,4 @@ - diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts index 81de85633a..993eb4040d 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/on-changes.component.ts @@ -5,7 +5,6 @@ import { SimpleChange, ViewChild } from '@angular/core'; - class Hero { constructor(public name: string) {} } @@ -52,8 +51,7 @@ export class OnChangesComponent implements OnChanges { @Component({ selector: 'on-changes-parent', templateUrl: 'app/on-changes-parent.component.html', - styles: ['.parent {background: Lavender;}'], - directives: [OnChangesComponent] + styles: ['.parent {background: Lavender;}'] }) export class OnChangesParentComponent { hero: Hero; diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/peek-a-boo-parent.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/peek-a-boo-parent.component.ts index e218dc3fc5..3f2bd8585d 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/peek-a-boo-parent.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/peek-a-boo-parent.component.ts @@ -1,7 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { PeekABooComponent } from './peek-a-boo.component'; import { LoggerService } from './logger.service'; @Component({ @@ -23,8 +22,7 @@ import { LoggerService } from './logger.service'; `, styles: ['.parent {background: moccasin}'], - directives: [PeekABooComponent], - providers: [LoggerService] + providers: [ LoggerService ] }) export class PeekABooParentComponent { diff --git a/public/docs/_examples/lifecycle-hooks/ts/app/spy.component.ts b/public/docs/_examples/lifecycle-hooks/ts/app/spy.component.ts index 4b8d742f4c..2d487f4037 100644 --- a/public/docs/_examples/lifecycle-hooks/ts/app/spy.component.ts +++ b/public/docs/_examples/lifecycle-hooks/ts/app/spy.component.ts @@ -2,7 +2,6 @@ import { Component } from '@angular/core'; import { LoggerService } from './logger.service'; -import { SpyDirective } from './spy.directive'; @Component({ selector: 'spy-parent', @@ -11,7 +10,6 @@ import { SpyDirective } from './spy.directive'; '.parent {background: khaki;}', '.heroes {background: LightYellow; padding: 0 8px}' ], - directives: [SpyDirective], providers: [LoggerService] }) export class SpyParentComponent { diff --git a/public/docs/_examples/package.json b/public/docs/_examples/package.json index 21a52a65e3..7015bd0767 100644 --- a/public/docs/_examples/package.json +++ b/public/docs/_examples/package.json @@ -25,17 +25,17 @@ "author": "", "license": "ISC", "dependencies": { - "@angular/common": "2.0.0-rc.4", - "@angular/compiler": "2.0.0-rc.4", - "@angular/core": "2.0.0-rc.4", - "@angular/forms": "0.2.0", - "@angular/http": "2.0.0-rc.4", - "@angular/platform-browser": "2.0.0-rc.4", - "@angular/platform-browser-dynamic": "2.0.0-rc.4", - "@angular/router": "3.0.0-beta.2", + "@angular/common": "2.0.0-rc.5", + "@angular/compiler": "2.0.0-rc.5", + "@angular/core": "2.0.0-rc.5", + "@angular/forms": "0.3.0", + "@angular/http": "2.0.0-rc.5", + "@angular/platform-browser": "2.0.0-rc.5", + "@angular/platform-browser-dynamic": "2.0.0-rc.5", + "@angular/router": "3.0.0-rc.1", "@angular/router-deprecated": "2.0.0-rc.2", - "@angular/upgrade": "2.0.0-rc.4", - "angular2-in-memory-web-api": "0.0.14", + "@angular/upgrade": "2.0.0-rc.5", + "angular2-in-memory-web-api": "0.0.15", "bootstrap": "^3.3.6", "core-js": "^2.4.0", "reflect-metadata": "^0.1.3", diff --git a/public/docs/_examples/pipes/ts/app/app.component.ts b/public/docs/_examples/pipes/ts/app/app.component.ts index dd4a9c3bad..1a53c144aa 100644 --- a/public/docs/_examples/pipes/ts/app/app.component.ts +++ b/public/docs/_examples/pipes/ts/app/app.component.ts @@ -1,28 +1,9 @@ // #docregion import { Component } from '@angular/core'; -import { HTTP_PROVIDERS } from '@angular/http'; - -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'; @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: [ - FlyingHeroesComponent, FlyingHeroesImpureComponent, - HeroAsyncMessageComponent, - HeroBirthdayComponent, - HeroBirthday2Component, - HeroListComponent, - PowerBoosterComponent, PowerBoostCalculatorComponent - ], - providers: [HTTP_PROVIDERS] + templateUrl: 'app/app.component.html' }) export class AppComponent { birthday = new Date(1988, 3, 15); // April 15, 1988 diff --git a/public/docs/_examples/pipes/ts/app/app.module.ts b/public/docs/_examples/pipes/ts/app/app.module.ts new file mode 100644 index 0000000000..89a3a29505 --- /dev/null +++ b/public/docs/_examples/pipes/ts/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/app/flying-heroes-impure.component.html b/public/docs/_examples/pipes/ts/app/flying-heroes-impure.component.html new file mode 100644 index 0000000000..66bd86f81c --- /dev/null +++ b/public/docs/_examples/pipes/ts/app/flying-heroes-impure.component.html @@ -0,0 +1,38 @@ + + +

    {{title}}

    +

    + +New hero: + + + can fly +

    +

    + Mutate array + + + +

    + +

    Heroes who fly (piped)

    +
    + +
    + {{hero.name}} +
    + +
    + +

    All Heroes (no pipe)

    +
    + + +
    + {{hero.name}} +
    + + +
    diff --git a/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts b/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts index d7bc01f94f..a6a8dd08b2 100644 --- a/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts +++ b/public/docs/_examples/pipes/ts/app/flying-heroes.component.ts @@ -2,15 +2,12 @@ // #docregion import { Component } from '@angular/core'; -import { FlyingHeroesPipe, - FlyingHeroesImpurePipe } from './flying-heroes.pipe'; import { HEROES } from './heroes'; @Component({ selector: 'flying-heroes', templateUrl: 'app/flying-heroes.component.html', - styles: ['#flyers, #all {font-style: italic}'], - pipes: [FlyingHeroesPipe] + styles: ['#flyers, #all {font-style: italic}'] }) // #docregion v1 export class FlyingHeroesComponent { @@ -53,11 +50,10 @@ export class FlyingHeroesComponent { // #docregion impure-component @Component({ selector: 'flying-heroes-impure', - templateUrl: 'app/flying-heroes.component.html', + templateUrl: 'app/flying-heroes-impure.component.html', // #enddocregion impure-component styles: ['.flyers, .all {font-style: italic}'], // #docregion impure-component - pipes: [FlyingHeroesImpurePipe] }) export class FlyingHeroesImpureComponent extends FlyingHeroesComponent { title = 'Flying Heroes (impure pipe)'; diff --git a/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts b/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts index 425c021931..87db9c277e 100644 --- a/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts +++ b/public/docs/_examples/pipes/ts/app/flying-heroes.pipe.ts @@ -19,7 +19,7 @@ export class FlyingHeroesPipe implements PipeTransform { // #docregion impure // #docregion pipe-decorator @Pipe({ - name: 'flyingHeroes', + name: 'flyingHeroesImpure', pure: false }) // #enddocregion pipe-decorator diff --git a/public/docs/_examples/pipes/ts/app/hero-list.component.ts b/public/docs/_examples/pipes/ts/app/hero-list.component.ts index 0b9d8df37e..dea501b6b5 100644 --- a/public/docs/_examples/pipes/ts/app/hero-list.component.ts +++ b/public/docs/_examples/pipes/ts/app/hero-list.component.ts @@ -1,8 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { FetchJsonPipe } from './fetch-json.pipe'; - @Component({ selector: 'hero-list', // #docregion template @@ -16,8 +14,7 @@ import { FetchJsonPipe } from './fetch-json.pipe';

    Heroes as JSON: {{'heroes.json' | fetch | json}}

    - `, + ` // #enddocregion template - pipes: [FetchJsonPipe] }) export class HeroListComponent { } diff --git a/public/docs/_examples/pipes/ts/app/main.ts b/public/docs/_examples/pipes/ts/app/main.ts index dd9994d2b6..3001ee1461 100644 --- a/public/docs/_examples/pipes/ts/app/main.ts +++ b/public/docs/_examples/pipes/ts/app/main.ts @@ -1,6 +1,6 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; import 'rxjs/Rx'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/pipes/ts/app/power-boost-calculator.component.ts b/public/docs/_examples/pipes/ts/app/power-boost-calculator.component.ts index 2c97e4a8f6..e65e29ad4c 100644 --- a/public/docs/_examples/pipes/ts/app/power-boost-calculator.component.ts +++ b/public/docs/_examples/pipes/ts/app/power-boost-calculator.component.ts @@ -1,8 +1,6 @@ // #docregion import { Component } from '@angular/core'; -import { ExponentialStrengthPipe } from './exponential-strength.pipe'; - @Component({ selector: 'power-boost-calculator', template: ` @@ -12,8 +10,7 @@ import { ExponentialStrengthPipe } from './exponential-strength.pipe';

    Super Hero Power: {{power | exponentialStrength: factor}}

    - `, - pipes: [ExponentialStrengthPipe] + ` }) export class PowerBoostCalculatorComponent { power = 5; diff --git a/public/docs/_examples/pipes/ts/app/power-booster.component.ts b/public/docs/_examples/pipes/ts/app/power-booster.component.ts index b62e678e90..08e3e24c7b 100644 --- a/public/docs/_examples/pipes/ts/app/power-booster.component.ts +++ b/public/docs/_examples/pipes/ts/app/power-booster.component.ts @@ -1,14 +1,11 @@ // #docregion import { Component } from '@angular/core'; -import { ExponentialStrengthPipe } from './exponential-strength.pipe'; - @Component({ selector: 'power-booster', template: `

    Power Booster

    Super power boost: {{2 | exponentialStrength: 10}}

    - `, - pipes: [ExponentialStrengthPipe] + ` }) export class PowerBoosterComponent { } diff --git a/public/docs/_examples/quickstart/js/app/app.module.js b/public/docs/_examples/quickstart/js/app/app.module.js new file mode 100644 index 0000000000..0f3d5f82cf --- /dev/null +++ b/public/docs/_examples/quickstart/js/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/app/main.js b/public/docs/_examples/quickstart/js/app/main.js index 55a77b9ddd..785823fa84 100644 --- a/public/docs/_examples/quickstart/js/app/main.js +++ b/public/docs/_examples/quickstart/js/app/main.js @@ -1,8 +1,8 @@ // #docregion (function(app) { document.addEventListener('DOMContentLoaded', function() { - // #docregion import - ng.platformBrowserDynamic.bootstrap(app.AppComponent); - // #enddocregion import + ng.platformBrowserDynamic + .platformBrowserDynamic() + .bootstrapModule(app.AppModule); }); })(window.app || (window.app = {})); diff --git a/public/docs/_examples/quickstart/js/index.html b/public/docs/_examples/quickstart/js/index.html index a1e88e0461..b127bdce13 100644 --- a/public/docs/_examples/quickstart/js/index.html +++ b/public/docs/_examples/quickstart/js/index.html @@ -27,6 +27,7 @@ + diff --git a/public/docs/_examples/quickstart/js/package.1.json b/public/docs/_examples/quickstart/js/package.1.json index 358034f15e..5b3ecbc847 100644 --- a/public/docs/_examples/quickstart/js/package.1.json +++ b/public/docs/_examples/quickstart/js/package.1.json @@ -7,23 +7,23 @@ }, "license": "ISC", "dependencies": { - "@angular/common": "2.0.0-rc.4", - "@angular/compiler": "2.0.0-rc.4", - "@angular/core": "2.0.0-rc.4", - "@angular/forms": "0.2.0", - "@angular/http": "2.0.0-rc.4", - "@angular/platform-browser": "2.0.0-rc.4", - "@angular/platform-browser-dynamic": "2.0.0-rc.4", - "@angular/router": "3.0.0-beta.1", + "@angular/common": "2.0.0-rc.5", + "@angular/compiler": "2.0.0-rc.5", + "@angular/core": "2.0.0-rc.5", + "@angular/forms": "0.3.0", + "@angular/http": "2.0.0-rc.5", + "@angular/platform-browser": "2.0.0-rc.5", + "@angular/platform-browser-dynamic": "2.0.0-rc.5", + "@angular/router": "3.0.0-rc.1", "@angular/router-deprecated": "2.0.0-rc.2", - "@angular/upgrade": "2.0.0-rc.4", + "@angular/upgrade": "2.0.0-rc.5", "core-js": "^2.4.0", "reflect-metadata": "0.1.3", "rxjs": "5.0.0-beta.6", "zone.js": "0.6.12", - "angular2-in-memory-web-api": "0.0.14", + "angular2-in-memory-web-api": "0.0.15", "bootstrap": "^3.3.6" }, "devDependencies": { diff --git a/public/docs/_examples/quickstart/ts/app/app.module.ts b/public/docs/_examples/quickstart/ts/app/app.module.ts new file mode 100644 index 0000000000..b4fc185c24 --- /dev/null +++ b/public/docs/_examples/quickstart/ts/app/app.module.ts @@ -0,0 +1,12 @@ +// #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/app/main.ts b/public/docs/_examples/quickstart/ts/app/main.ts index 2aede345e9..4ad787ebfd 100644 --- a/public/docs/_examples/quickstart/ts/app/main.ts +++ b/public/docs/_examples/quickstart/ts/app/main.ts @@ -1,8 +1,8 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; - // #docregion import -import { AppComponent } from './app.component'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app.module'; // #enddocregion import -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/quickstart/ts/package.1.json b/public/docs/_examples/quickstart/ts/package.1.json index ef5d180619..4f0bc098fd 100644 --- a/public/docs/_examples/quickstart/ts/package.1.json +++ b/public/docs/_examples/quickstart/ts/package.1.json @@ -11,16 +11,16 @@ }, "license": "ISC", "dependencies": { - "@angular/common": "2.0.0-rc.4", - "@angular/compiler": "2.0.0-rc.4", - "@angular/core": "2.0.0-rc.4", - "@angular/forms": "0.2.0", - "@angular/http": "2.0.0-rc.4", - "@angular/platform-browser": "2.0.0-rc.4", - "@angular/platform-browser-dynamic": "2.0.0-rc.4", - "@angular/router": "3.0.0-beta.1", + "@angular/common": "2.0.0-rc.5", + "@angular/compiler": "2.0.0-rc.5", + "@angular/core": "2.0.0-rc.5", + "@angular/forms": "0.3.0", + "@angular/http": "2.0.0-rc.5", + "@angular/platform-browser": "2.0.0-rc.5", + "@angular/platform-browser-dynamic": "2.0.0-rc.5", + "@angular/router": "3.0.0-rc.1", "@angular/router-deprecated": "2.0.0-rc.2", - "@angular/upgrade": "2.0.0-rc.4", + "@angular/upgrade": "2.0.0-rc.5", "systemjs": "0.19.27", "core-js": "^2.4.0", @@ -28,7 +28,7 @@ "rxjs": "5.0.0-beta.6", "zone.js": "^0.6.12", - "angular2-in-memory-web-api": "0.0.14", + "angular2-in-memory-web-api": "0.0.15", "bootstrap": "^3.3.6" }, "devDependencies": { diff --git a/public/docs/_examples/quickstart/ts/typings.1.json b/public/docs/_examples/quickstart/ts/typings.1.json index 3385926d1f..3d826df25a 100644 --- a/public/docs/_examples/quickstart/ts/typings.1.json +++ b/public/docs/_examples/quickstart/ts/typings.1.json @@ -2,6 +2,6 @@ "globalDependencies": { "core-js": "registry:dt/core-js#0.0.0+20160602141332", "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", - "node": "registry:dt/node#6.0.0+20160621231320" + "node": "registry:dt/node#6.0.0+20160807145350" } } diff --git a/public/docs/_examples/router-deprecated/e2e-spec.ts b/public/docs/_examples/router-deprecated/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/router-deprecated/e2e-spec.ts rename to public/docs/_examples/router-deprecated/e2e-spec.ts.disabled diff --git a/public/docs/_examples/router/e2e-spec.ts b/public/docs/_examples/router/e2e-spec.ts index ac3e36f4ea..bf672c3f01 100644 --- a/public/docs/_examples/router/e2e-spec.ts +++ b/public/docs/_examples/router/e2e-spec.ts @@ -11,18 +11,18 @@ describe('Router', function () { return { hrefs: hrefEles, - routerParent: element(by.css('my-app > undefined')), - routerTitle: element(by.css('my-app > undefined > h2')), + routerParent: element(by.css('my-app > ng-component')), + routerTitle: element(by.css('my-app > ng-component > h2')), crisisHref: hrefEles.get(0), - crisisList: element.all(by.css('my-app > undefined > undefined li')), - crisisDetail: element(by.css('my-app > undefined > undefined > div')), - crisisDetailTitle: element(by.css('my-app > undefined > undefined > div > h3')), + crisisList: element.all(by.css('my-app > ng-component > ng-component li')), + crisisDetail: element(by.css('my-app > ng-component > ng-component > div')), + crisisDetailTitle: element(by.css('my-app > ng-component > ng-component > div > h3')), heroesHref: hrefEles.get(1), - heroesList: element.all(by.css('my-app > undefined li')), - heroDetail: element(by.css('my-app > undefined > div')), - heroDetailTitle: element(by.css('my-app > undefined > div > h3')), + heroesList: element.all(by.css('my-app > ng-component li')), + heroDetail: element(by.css('my-app > ng-component > div')), + heroDetailTitle: element(by.css('my-app > ng-component > div > h3')), adminHref: hrefEles.get(2), loginHref: hrefEles.get(3) @@ -31,7 +31,7 @@ describe('Router', function () { it('should be able to see the start screen', function () { let page = getPageStruct(); - expect(page.hrefs.count()).toEqual(4, 'should be two dashboard choices'); + expect(page.hrefs.count()).toEqual(4, 'should be 4 dashboard choices'); expect(page.crisisHref.getText()).toEqual('Crisis Center'); expect(page.heroesHref.getText()).toEqual('Heroes'); expect(page.adminHref.getText()).toEqual('Crisis Admin'); @@ -40,7 +40,9 @@ describe('Router', function () { it('should be able to see crises center items', function () { let page = getPageStruct(); - expect(page.crisisList.count()).toBe(4, 'should be 4 crisis center entries at start'); + page.crisisHref.click().then(function() { + expect(page.crisisList.count()).toBe(4, 'should be 4 crisis center entries at start'); + }); }); it('should be able to see hero items', function () { @@ -62,11 +64,17 @@ describe('Router', function () { }); it('should be able to edit and save details from the crisis center view', function () { - crisisCenterEdit(2, true); + let page = getPageStruct(); + page.crisisHref.click().then(function() { + crisisCenterEdit(2, true); + }); }); xit('should be able to edit and cancel details from the crisis center view', function () { - crisisCenterEdit(3, false); + let page = getPageStruct(); + page.crisisHref.click().then(function() { + crisisCenterEdit(3, false); + }); }); it('should be able to edit and save details from the heroes view', function () { diff --git a/public/docs/_examples/router/ts/app/app.component.1.ts b/public/docs/_examples/router/ts/app/app.component.1.ts index e124b6771a..1b0aba0044 100644 --- a/public/docs/_examples/router/ts/app/app.component.1.ts +++ b/public/docs/_examples/router/ts/app/app.component.1.ts @@ -1,10 +1,6 @@ /* First version */ -// #docplaster // #docregion import { Component } from '@angular/core'; -// #docregion import-router -import { ROUTER_DIRECTIVES } from '@angular/router'; -// #enddocregion import-router @Component({ selector: 'my-app', @@ -16,11 +12,7 @@ import { ROUTER_DIRECTIVES } from '@angular/router'; Heroes - `, + ` // #enddocregion template - // #docregion directives - directives: [ROUTER_DIRECTIVES] - // #enddocregion directives }) - export class AppComponent { } diff --git a/public/docs/_examples/router/ts/app/app.component.2.ts b/public/docs/_examples/router/ts/app/app.component.2.ts index fcbde30967..7593aa1608 100644 --- a/public/docs/_examples/router/ts/app/app.component.2.ts +++ b/public/docs/_examples/router/ts/app/app.component.2.ts @@ -2,23 +2,7 @@ // #docplaster // #docregion -import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -// #enddocregion -/* - // Apparent Milestone 2 imports - // #docregion - // #docregion hero-import - import { HeroDetailComponent } from './heroes/hero-detail.component'; - import { HeroListComponent } from './heroes/hero-list.component'; - import { HeroService } from './heroes/hero.service'; - // #enddocregion hero-import - // #enddocregion - */ -// Actual Milestone 2 imports -import { HeroService } from './heroes/hero.service'; -// #docregion +import { Component } from '@angular/core'; @Component({ selector: 'my-app', @@ -29,12 +13,7 @@ import { HeroService } from './heroes/hero.service'; Heroes - `, - providers: [HeroService], - directives: [ROUTER_DIRECTIVES] + ` }) -// #enddocregion export class AppComponent { } -// #enddocregion route-config -// #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.component.3.ts b/public/docs/_examples/router/ts/app/app.component.3.ts index d8bb30b1e2..1150a01814 100644 --- a/public/docs/_examples/router/ts/app/app.component.3.ts +++ b/public/docs/_examples/router/ts/app/app.component.3.ts @@ -1,9 +1,7 @@ /* tslint:disable:no-unused-variable */ // #docplaster import { Component } from '@angular/core'; -import { Router, ROUTER_DIRECTIVES } from '@angular/router'; - -import { HeroService } from './heroes/hero.service'; +import { Router } from '@angular/router'; @Component({ selector: 'my-app', @@ -45,10 +43,8 @@ import { HeroService } from './heroes/hero.service'; Shark Crisis - `, + ` // #enddocregion template - providers: [HeroService], - directives: [ROUTER_DIRECTIVES] }) export class AppComponent { } diff --git a/public/docs/_examples/router/ts/app/app.component.4.ts b/public/docs/_examples/router/ts/app/app.component.4.ts index 80cf4b0d17..e609850a42 100644 --- a/public/docs/_examples/router/ts/app/app.component.4.ts +++ b/public/docs/_examples/router/ts/app/app.component.4.ts @@ -1,9 +1,5 @@ // #docregion import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -import { DialogService } from './dialog.service'; -import { HeroService } from './heroes/hero.service'; @Component({ selector: 'my-app', @@ -17,13 +13,8 @@ import { HeroService } from './heroes/hero.service'; Crisis Admin - `, + ` // #enddocregion template - providers: [ - HeroService, - DialogService - ], - directives: [ROUTER_DIRECTIVES] }) export class AppComponent { } diff --git a/public/docs/_examples/router/ts/app/app.component.ts b/public/docs/_examples/router/ts/app/app.component.ts index a3ad280a45..2dcb0a062b 100644 --- a/public/docs/_examples/router/ts/app/app.component.ts +++ b/public/docs/_examples/router/ts/app/app.component.ts @@ -1,10 +1,6 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -import { DialogService } from './dialog.service'; -import { HeroService } from './heroes/hero.service'; @Component({ selector: 'my-app', @@ -19,13 +15,8 @@ import { HeroService } from './heroes/hero.service'; Login - `, + ` // #enddocregion template - providers: [ - HeroService, - DialogService - ], - directives: [ROUTER_DIRECTIVES] }) export class AppComponent { } diff --git a/public/docs/_examples/router/ts/app/app.module.1.ts b/public/docs/_examples/router/ts/app/app.module.1.ts new file mode 100644 index 0000000000..c6d5648bbf --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.module.1.ts @@ -0,0 +1,35 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + + +// #docregion router-basics +import { AppComponent } from './app.component'; +import { routing, + appRoutingProviders } from './app.routing'; + +import { HeroListComponent } from './hero-list.component'; +import { CrisisListComponent } from './crisis-list.component'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing + ], + declarations: [ + AppComponent, + HeroListComponent, + CrisisListComponent + ], + providers: [ + appRoutingProviders + ], + bootstrap: [ AppComponent ] +}) +// #enddocregion router-basics +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.module.2.ts b/public/docs/_examples/router/ts/app/app.module.2.ts new file mode 100644 index 0000000000..35913ae6a2 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.module.2.ts @@ -0,0 +1,35 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { routing, + appRoutingProviders } from './app.routing'; + +// #docregion hero-import +import { HeroesModule } from './heroes/heroes.module'; + +import { CrisisListComponent } from './crisis-list.component'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing, + HeroesModule + ], + declarations: [ + AppComponent, + CrisisListComponent + ], + providers: [ + appRoutingProviders + ], + bootstrap: [ AppComponent ] +}) +// #enddocregion hero-import +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.module.3.ts b/public/docs/_examples/router/ts/app/app.module.3.ts new file mode 100644 index 0000000000..d1969a860a --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.module.3.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 { routing, + appRoutingProviders } from './app.routing'; + +import { HeroesModule } from './heroes/heroes.module'; +import { CrisisCenterModule } from './crisis-center/crisis-center.module'; + +import { DialogService } from './dialog.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing, + HeroesModule, + CrisisCenterModule + ], + declarations: [ + AppComponent + ], + providers: [ + appRoutingProviders, + DialogService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} diff --git a/public/docs/_examples/router/ts/app/app.module.4.ts b/public/docs/_examples/router/ts/app/app.module.4.ts new file mode 100644 index 0000000000..96a8eb61d9 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.module.4.ts @@ -0,0 +1,28 @@ +// #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'; + +const routes: Routes = [ + +]; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + RouterModule.forRoot(routes, { useHash: true }) // .../#/crisis-center/ + ], + declarations: [ + AppComponent + ], + providers: [ + + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} diff --git a/public/docs/_examples/router/ts/app/app.module.ts b/public/docs/_examples/router/ts/app/app.module.ts new file mode 100644 index 0000000000..f273052d83 --- /dev/null +++ b/public/docs/_examples/router/ts/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 { routing, + appRoutingProviders } from './app.routing'; + +import { HeroesModule } from './heroes/heroes.module'; + +import { LoginComponent } from './login.component'; + +import { DialogService } from './dialog.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing, + HeroesModule + ], + declarations: [ + AppComponent, + LoginComponent + ], + providers: [ + appRoutingProviders, + DialogService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.routes.3.ts b/public/docs/_examples/router/ts/app/app.routes.3.ts deleted file mode 100644 index e49d055672..0000000000 --- a/public/docs/_examples/router/ts/app/app.routes.3.ts +++ /dev/null @@ -1,15 +0,0 @@ -// #docplaster -// #docregion -import { provideRouter, RouterConfig } from '@angular/router'; - -import { CrisisListComponent } from './crisis-center/crisis-list.component'; -import { heroesRoutes } from './heroes/heroes.routes'; - -export const routes: RouterConfig = [ - ...heroesRoutes, - { path: 'crisis-center', component: CrisisListComponent } -]; - -export const appRouterProviders = [ - provideRouter(routes) -]; diff --git a/public/docs/_examples/router/ts/app/app.routes.4.ts b/public/docs/_examples/router/ts/app/app.routes.4.ts deleted file mode 100644 index d51635a676..0000000000 --- a/public/docs/_examples/router/ts/app/app.routes.4.ts +++ /dev/null @@ -1,14 +0,0 @@ -// #docregion -import { provideRouter, RouterConfig } from '@angular/router'; - -import { crisisCenterRoutes } from './crisis-center/crisis-center.routes'; -import { heroesRoutes } from './heroes/heroes.routes'; - -export const routes: RouterConfig = [ - ...heroesRoutes, - ...crisisCenterRoutes -]; - -export const appRouterProviders = [ - provideRouter(routes) -]; diff --git a/public/docs/_examples/router/ts/app/app.routes.5.ts b/public/docs/_examples/router/ts/app/app.routes.5.ts deleted file mode 100644 index 87e64af66e..0000000000 --- a/public/docs/_examples/router/ts/app/app.routes.5.ts +++ /dev/null @@ -1,19 +0,0 @@ -// #docregion -import { provideRouter, RouterConfig } from '@angular/router'; - -import { crisisCenterRoutes } from './crisis-center/crisis-center.routes'; -import { heroesRoutes } from './heroes/heroes.routes'; - -import { loginRoutes, - authProviders } from './login.routes'; - -export const routes: RouterConfig = [ - ...heroesRoutes, - ...loginRoutes, - ...crisisCenterRoutes -]; - -export const appRouterProviders = [ - provideRouter(routes), - authProviders -]; diff --git a/public/docs/_examples/router/ts/app/app.routes.ts b/public/docs/_examples/router/ts/app/app.routes.ts deleted file mode 100644 index eb238680da..0000000000 --- a/public/docs/_examples/router/ts/app/app.routes.ts +++ /dev/null @@ -1,22 +0,0 @@ -// #docregion -import { provideRouter, RouterConfig } from '@angular/router'; - -import { crisisCenterRoutes } from './crisis-center/crisis-center.routes'; -import { heroesRoutes } from './heroes/heroes.routes'; - -import { loginRoutes, - authProviders } from './login.routes'; - -import { CanDeactivateGuard } from './can-deactivate-guard.service'; - -export const routes: RouterConfig = [ - ...heroesRoutes, - ...loginRoutes, - ...crisisCenterRoutes -]; - -export const appRouterProviders = [ - provideRouter(routes), - authProviders, - CanDeactivateGuard -]; diff --git a/public/docs/_examples/router/ts/app/app.routes.1.ts b/public/docs/_examples/router/ts/app/app.routing.1.ts similarity index 73% rename from public/docs/_examples/router/ts/app/app.routes.1.ts rename to public/docs/_examples/router/ts/app/app.routing.1.ts index 7919f34aba..ccdff404a4 100644 --- a/public/docs/_examples/router/ts/app/app.routes.1.ts +++ b/public/docs/_examples/router/ts/app/app.routing.1.ts @@ -1,7 +1,7 @@ // #docplaster // #docregion // #docregion route-config -import { provideRouter, RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; // #enddocregion route-config // #enddocregion @@ -15,10 +15,16 @@ import { PageNotFoundComponent } from './not-found.component'; // #docregion // #docregion route-config -const routes: RouterConfig = [ +const appRoutes: Routes = [ // #docregion route-defs { path: 'crisis-center', component: CrisisCenterComponent }, - { path: 'heroes', component: HeroListComponent }, + { + path: 'heroes', + component: HeroListComponent, + data: { + title: 'Heroes List' + } + }, // #enddocregion route-defs // #docregion hero-detail-route { path: 'hero/:id', component: HeroDetailComponent }, @@ -26,8 +32,10 @@ const routes: RouterConfig = [ { path: '**', component: PageNotFoundComponent } ]; -export const appRouterProviders = [ - provideRouter(routes) +export const appRoutingProviders: any[] = [ + ]; + +export const routing = RouterModule.forRoot(appRoutes); // #enddocregion route-config // #enddocregion diff --git a/public/docs/_examples/router/ts/app/app.routes.2.ts b/public/docs/_examples/router/ts/app/app.routing.2.ts similarity index 67% rename from public/docs/_examples/router/ts/app/app.routes.2.ts rename to public/docs/_examples/router/ts/app/app.routing.2.ts index e2e3d1f9fc..81c04c86da 100644 --- a/public/docs/_examples/router/ts/app/app.routes.2.ts +++ b/public/docs/_examples/router/ts/app/app.routing.2.ts @@ -1,19 +1,21 @@ // #docplaster // #docregion // #docregion route-config -import { provideRouter, RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; // #enddocregion route-config import { CrisisListComponent } from './crisis-list.component'; import { HeroListComponent } from './hero-list.component'; // #docregion route-config -const routes: RouterConfig = [ +const appRoutes: Routes = [ { path: 'crisis-center', component: CrisisListComponent }, { path: 'heroes', component: HeroListComponent } ]; -export const appRouterProviders = [ - provideRouter(routes) +export const appRoutingProviders: any[] = [ + ]; + +export const routing = RouterModule.forRoot(appRoutes); // #enddocregion route-config diff --git a/public/docs/_examples/router/ts/app/app.routing.3.ts b/public/docs/_examples/router/ts/app/app.routing.3.ts new file mode 100644 index 0000000000..2eab693570 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.routing.3.ts @@ -0,0 +1,15 @@ +// #docplaster +// #docregion +import { Routes, RouterModule } from '@angular/router'; + +import { CrisisListComponent } from './crisis-center/crisis-list.component'; + +const appRoutes: Routes = [ + { path: 'crisis-center', component: CrisisListComponent } +]; + +export const appRoutingProviders: any[] = [ + +]; + +export const routing = RouterModule.forRoot(appRoutes); diff --git a/public/docs/_examples/router/ts/app/app.routing.4.ts b/public/docs/_examples/router/ts/app/app.routing.4.ts new file mode 100644 index 0000000000..59153e9e53 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.routing.4.ts @@ -0,0 +1,12 @@ +// #docregion +import { Routes, RouterModule } from '@angular/router'; + +const appRoutes: Routes = [ + +]; + +export const appRoutingProviders: any[] = [ + +]; + +export const routing = RouterModule.forRoot(appRoutes); diff --git a/public/docs/_examples/router/ts/app/app.routing.5.ts b/public/docs/_examples/router/ts/app/app.routing.5.ts new file mode 100644 index 0000000000..8334e865a5 --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.routing.5.ts @@ -0,0 +1,15 @@ +// #docregion +import { Routes, RouterModule } from '@angular/router'; + +import { loginRoutes, + authProviders } from './login.routing'; + +const appRoutes: Routes = [ + ...loginRoutes +]; + +export const appRoutingProviders: any[] = [ + authProviders +]; + +export const routing = RouterModule.forRoot(appRoutes); diff --git a/public/docs/_examples/router/ts/app/app.routing.ts b/public/docs/_examples/router/ts/app/app.routing.ts new file mode 100644 index 0000000000..46f497e45e --- /dev/null +++ b/public/docs/_examples/router/ts/app/app.routing.ts @@ -0,0 +1,45 @@ +// #docregion +// #docregion import-router +import { Routes, RouterModule } from '@angular/router'; +// #enddocregion import-router + +import { loginRoutes, + authProviders } from './login.routing'; + +import { CanDeactivateGuard } from './can-deactivate-guard.service'; + +// #docregion lazy-load-crisis-center +const crisisCenterRoutes: Routes = [ + { + path: '', + redirectTo: '/heroes', + pathMatch: 'full' + }, + { + path: 'crisis-center', + loadChildren: 'app/crisis-center/crisis-center.module#CrisisCenterModule' + } +]; + +const appRoutes: Routes = [ + ...loginRoutes, + ...crisisCenterRoutes +]; +// #enddocregion lazy-load-crisis-center + +export const appRoutingProviders: any[] = [ + authProviders, + CanDeactivateGuard +]; + +export const routing = RouterModule.forRoot(appRoutes); + +/* A link parameters array +// #docregion heroes-redirect +{ + path: '', + redirectTo: '/heroes', + pathMatch: 'full' +}, +// #enddocregion heroes-redirect +*/ diff --git a/public/docs/_examples/router/ts/app/auth-guard.service.ts b/public/docs/_examples/router/ts/app/auth-guard.service.ts index a70fadb5cf..63aee1deb5 100755 --- a/public/docs/_examples/router/ts/app/auth-guard.service.ts +++ b/public/docs/_examples/router/ts/app/auth-guard.service.ts @@ -2,7 +2,8 @@ import { Injectable } from '@angular/core'; import { CanActivate, Router, ActivatedRouteSnapshot, - RouterStateSnapshot } from '@angular/router'; + RouterStateSnapshot, + NavigationExtras } from '@angular/router'; import { AuthService } from './auth.service'; @Injectable() @@ -20,7 +21,7 @@ export class AuthGuard implements CanActivate { // Set our navigation extras object // that contains our global query params and fragment - let navigationExtras = { + let navigationExtras: NavigationExtras = { queryParams: { 'session_id': sessionId }, fragment: 'anchor' }; diff --git a/public/docs/_examples/router/ts/app/can-deactivate-guard.service.1.ts b/public/docs/_examples/router/ts/app/can-deactivate-guard.service.1.ts new file mode 100644 index 0000000000..b6e16bfa4c --- /dev/null +++ b/public/docs/_examples/router/ts/app/can-deactivate-guard.service.1.ts @@ -0,0 +1,32 @@ +// #docregion +import { Injectable } from '@angular/core'; +import { CanDeactivate, + ActivatedRouteSnapshot, + RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +import { CrisisDetailComponent } from './crisis-center/crisis-detail.component'; + +@Injectable() +export class CanDeactivateGuard implements CanDeactivate { + + canDeactivate( + component: CrisisDetailComponent, + route: ActivatedRouteSnapshot, + state: RouterStateSnapshot + ): Observable | 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/app/can-deactivate-guard.service.ts b/public/docs/_examples/router/ts/app/can-deactivate-guard.service.ts index e7774533bc..db8ecef5bf 100644 --- a/public/docs/_examples/router/ts/app/can-deactivate-guard.service.ts +++ b/public/docs/_examples/router/ts/app/can-deactivate-guard.service.ts @@ -9,7 +9,7 @@ export interface CanComponentDeactivate { @Injectable() export class CanDeactivateGuard implements CanDeactivate { - canDeactivate(component: CanComponentDeactivate): Observable | boolean { + canDeactivate(component: CanComponentDeactivate): Observable | Promise | boolean { return component.canDeactivate ? component.canDeactivate() : true; } } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.1.ts index 4f4173a4e5..a8dacc8549 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.1.ts @@ -5,7 +5,6 @@ import { Component } from '@angular/core'; template: `

    CRISIS ADMINISTRATION

    Manage your crises here

    - `, - directives: [] + ` }) export class CrisisAdminComponent { } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.ts index 9783466d4c..38fe14a38e 100755 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-admin.component.ts @@ -1,7 +1,7 @@ // #docregion -import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; import 'rxjs/add/operator/map'; @Component({ @@ -12,25 +12,22 @@ import 'rxjs/add/operator/map';

    Session ID: {{ sessionId | async }}

    Token: {{ token | async }}

    - `, - directives: [] + ` }) export class CrisisAdminComponent implements OnInit { sessionId: Observable; token: Observable; - constructor(private router: Router) {} + constructor(private route: ActivatedRoute) {} ngOnInit() { // Capture the session ID if available - this.sessionId = this.router - .routerState + this.sessionId = this.route .queryParams .map(params => params['session_id'] || 'None'); // Capture the fragment if available - this.token = this.router - .routerState + this.token = this.route .fragment .map(fragment => fragment || 'None'); } diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts deleted file mode 100644 index d5a1112c7b..0000000000 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.1.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -import { CrisisService } from './crisis.service'; - -// #docregion minus-imports -@Component({ - template: ` -

    CRISIS CENTER

    - - `, - directives: [ROUTER_DIRECTIVES], -// #docregion providers - providers: [CrisisService] -// #enddocregion providers -}) -export class CrisisCenterComponent { } -// #enddocregion minus-imports diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts index 9cc268b6bf..31d1790f45 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.component.ts @@ -1,16 +1,14 @@ // #docregion -import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -import { CrisisService } from './crisis.service'; +// #docplaster +import { Component } from '@angular/core'; +// #docregion minus-imports @Component({ template: `

    CRISIS CENTER

    - `, - directives: [ROUTER_DIRECTIVES], - providers: [CrisisService] + ` }) export class CrisisCenterComponent { } +// #enddocregion minus-imports // #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.1.ts new file mode 100644 index 0000000000..a392ccfe04 --- /dev/null +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.1.ts @@ -0,0 +1,32 @@ +// #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 { CrisisDetailComponent } from './crisis-detail.component'; + +import { crisisCenterRouting } from './crisis-center.routing'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + crisisCenterRouting + ], + declarations: [ + CrisisCenterComponent, + CrisisListComponent, + CrisisDetailComponent + ], +// #docregion providers + providers: [ + CrisisService + ] +// #enddocregion providers +}) +export class CrisisCenterModule {} +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.ts new file mode 100644 index 0000000000..41b31d6aa5 --- /dev/null +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.module.ts @@ -0,0 +1,42 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { FormsModule } from '@angular/forms'; +import { CommonModule } from '@angular/common'; + +import { CrisisService } from './crisis.service'; +// #docregion crisis-detail-resolve +import { CrisisDetailResolve } from './crisis-detail-resolve.service'; +// #enddocregion crisis-detail-resolve + +import { CrisisCenterComponent } from './crisis-center.component'; +import { CrisisListComponent } from './crisis-list.component'; +import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisAdminComponent } from './crisis-admin.component'; + +import { crisisCenterRouting } from './crisis-center.routing'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + crisisCenterRouting + ], + declarations: [ + CrisisCenterComponent, + CrisisListComponent, + CrisisDetailComponent, + CrisisAdminComponent + ], + // #docregion crisis-detail-resolve + + providers: [ + CrisisService, + CrisisDetailResolve + ] + // #enddocregion crisis-detail-resolve +}) +// #docregion crisis-center-module-export +export class CrisisCenterModule {} +// #enddocregion crisis-center-module-export +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.1.ts similarity index 72% rename from public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts rename to public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.1.ts index 7f28dabdcf..0d53587aa4 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.1.ts @@ -1,11 +1,12 @@ // #docregion -import { RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + import { CrisisDetailComponent } from './crisis-detail.component'; import { CrisisListComponent } from './crisis-list.component'; import { CrisisCenterComponent } from './crisis-center.component'; // #docregion routes -export const crisisCenterRoutes: RouterConfig = [ +const crisisCenterRoutes: Routes = [ { path: 'crisis-center', component: CrisisCenterComponent, @@ -15,4 +16,6 @@ export const crisisCenterRoutes: RouterConfig = [ ] } ]; + +export const crisisCenterRouting = RouterModule.forChild(crisisCenterRoutes); // #enddocregion routes diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.2.ts similarity index 77% rename from public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts rename to public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.2.ts index 3e8d2f391e..a95ab751f3 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.2.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.2.ts @@ -1,11 +1,12 @@ // #docregion -import { RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + import { CrisisDetailComponent } from './crisis-detail.component'; import { CrisisListComponent } from './crisis-list.component'; import { CrisisCenterComponent } from './crisis-center.component'; // #docregion routes -export const crisisCenterRoutes: RouterConfig = [ +const crisisCenterRoutes: Routes = [ // #docregion redirect { path: '', @@ -22,4 +23,6 @@ export const crisisCenterRoutes: RouterConfig = [ ] } ]; + +export const crisisCenterRouting = RouterModule.forChild(crisisCenterRoutes); // #enddocregion routes diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.3.ts similarity index 86% rename from public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts rename to public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.3.ts index b0d0fc9fca..8d92662132 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.3.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.3.ts @@ -1,6 +1,7 @@ // #docplaster // #docregion -import { RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + import { CrisisDetailComponent } from './crisis-detail.component'; import { CrisisListComponent } from './crisis-list.component'; import { CrisisCenterComponent } from './crisis-center.component'; @@ -8,7 +9,7 @@ import { CrisisAdminComponent } from './crisis-admin.component'; import { CanDeactivateGuard } from '../can-deactivate-guard.service'; -export const crisisCenterRoutes: RouterConfig = [ +const crisisCenterRoutes: Routes = [ { path: '', redirectTo: '/crisis-center', @@ -36,6 +37,8 @@ export const crisisCenterRoutes: RouterConfig = [ ] } ]; + +export const crisisCenterRouting = RouterModule.forChild(crisisCenterRoutes); // #enddocregion /* diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.4.ts similarity index 87% rename from public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts rename to public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.4.ts index a7e1612297..fa5d1e887c 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.4.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.4.ts @@ -1,6 +1,7 @@ // #docplaster // #docregion -import { RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + import { CrisisDetailComponent } from './crisis-detail.component'; import { CrisisListComponent } from './crisis-list.component'; import { CrisisCenterComponent } from './crisis-center.component'; @@ -9,7 +10,7 @@ import { CrisisAdminComponent } from './crisis-admin.component'; import { CanDeactivateGuard } from '../can-deactivate-guard.service'; import { AuthGuard } from '../auth-guard.service'; -export const crisisCenterRoutes: RouterConfig = [ +const crisisCenterRoutes: Routes = [ { path: '', redirectTo: '/crisis-center', @@ -38,6 +39,8 @@ export const crisisCenterRoutes: RouterConfig = [ ] } ]; + +export const crisisCenterRouting = RouterModule.forChild(crisisCenterRoutes); // #enddocregion /* diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.5.ts similarity index 63% rename from public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts rename to public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.5.ts index 6712b35ced..cb7cda5d31 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routes.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.5.ts @@ -1,14 +1,20 @@ +// #docplaster // #docregion -import { RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + +import { CrisisCenterComponent } from './crisis-center.component'; import { CrisisDetailComponent } from './crisis-detail.component'; import { CrisisListComponent } from './crisis-list.component'; -import { CrisisCenterComponent } from './crisis-center.component'; import { CrisisAdminComponent } from './crisis-admin.component'; import { CanDeactivateGuard } from '../can-deactivate-guard.service'; import { AuthGuard } from '../auth-guard.service'; +// #docregion crisis-detail-resolve +import { CrisisDetailResolve } from './crisis-detail-resolve.service'; -export const crisisCenterRoutes: RouterConfig = [ +// #enddocregion crisis-detail-resolve + +const crisisCenterRoutes: Routes = [ { path: '', redirectTo: '/crisis-center', @@ -25,11 +31,16 @@ export const crisisCenterRoutes: RouterConfig = [ canActivate: [AuthGuard] }, // #enddocregion admin-route + // #docregion crisis-detail-resolve { path: ':id', component: CrisisDetailComponent, - canDeactivate: [CanDeactivateGuard] + canDeactivate: [CanDeactivateGuard], + resolve: { + crisis: CrisisDetailResolve + } }, + // #enddocregion crisis-detail-resolve { path: '', component: CrisisListComponent @@ -37,3 +48,5 @@ export const crisisCenterRoutes: RouterConfig = [ ] } ]; + +export const crisisCenterRouting = RouterModule.forChild(crisisCenterRoutes); diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.ts new file mode 100644 index 0000000000..2d044f9de9 --- /dev/null +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-center.routing.ts @@ -0,0 +1,50 @@ +// #docplaster +// #docregion +import { Routes, RouterModule } from '@angular/router'; + +import { CrisisCenterComponent } from './crisis-center.component'; +import { CrisisDetailComponent } from './crisis-detail.component'; +import { CrisisListComponent } from './crisis-list.component'; +import { CrisisAdminComponent } from './crisis-admin.component'; + +import { CanDeactivateGuard } from '../can-deactivate-guard.service'; +import { AuthGuard } from '../auth-guard.service'; +// #docregion crisis-detail-resolve +import { CrisisDetailResolve } from './crisis-detail-resolve.service'; + +// #enddocregion crisis-detail-resolve + +// #docregion lazy-load-crisis-center +const crisisCenterRoutes: Routes = [ + { + path: '', + component: CrisisCenterComponent, + children: [ + // #docregion admin-route + { + path: 'admin', + component: CrisisAdminComponent, + canActivate: [AuthGuard] + }, + // #enddocregion admin-route + // #docregion crisis-detail-resolve + { + path: ':id', + component: CrisisDetailComponent, + canDeactivate: [CanDeactivateGuard], + resolve: { + crisis: CrisisDetailResolve + } + // #enddocregion crisis-detail-resolve + }, + { + path: '', + component: CrisisListComponent + } + ] + } +]; + +export const crisisCenterRouting = RouterModule.forChild(crisisCenterRoutes); +// #enddocregion lazy-load-crisis-center +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail-resolve.service.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail-resolve.service.ts new file mode 100644 index 0000000000..d0d6d1f192 --- /dev/null +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail-resolve.service.ts @@ -0,0 +1,25 @@ +// #docregion +import { Injectable } from '@angular/core'; +import { Router, Resolve, + ActivatedRouteSnapshot } from '@angular/router'; +import { Observable } from 'rxjs/Observable'; + +import { Crisis, CrisisService } from './crisis.service'; + +@Injectable() +export class CrisisDetailResolve implements Resolve { + constructor(private cs: CrisisService, private router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable | Promise | any { + 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 false; + } + }); + } +} diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts index fdb331dec7..9d37453cc1 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.1.ts @@ -2,13 +2,11 @@ // #docregion import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/observable/fromPromise'; import { Crisis, CrisisService } from './crisis.service'; import { DialogService } from '../dialog.service'; - - +import { Observable } from 'rxjs/Observable'; +import { Subscription } from 'rxjs/Subscription'; @Component({ // #docregion template @@ -36,7 +34,7 @@ export class CrisisDetailComponent implements OnInit, OnDestroy { crisis: Crisis; editName: string; // #enddocregion ngOnDestroy - private sub: any; + private sub: Subscription; // #enddocregion ngOnDestroy // #enddocregion cancel-save @@ -44,7 +42,7 @@ export class CrisisDetailComponent implements OnInit, OnDestroy { private service: CrisisService, private router: Router, private route: ActivatedRoute, - private dialogService: DialogService + public dialogService: DialogService ) { } // #docregion ngOnInit @@ -86,16 +84,14 @@ export class CrisisDetailComponent implements OnInit, OnDestroy { // #enddocregion cancel-save // #docregion cancel-save-only - canDeactivate(): Observable | boolean { + canDeactivate(): Observable | 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 - let p = this.dialogService.confirm('Discard changes?'); - let o = Observable.fromPromise(p); - return o; + return this.dialogService.confirm('Discard changes?'); } // #enddocregion cancel-save-only @@ -105,7 +101,7 @@ export class CrisisDetailComponent implements OnInit, OnDestroy { // Pass along the hero id if available // so that the CrisisListComponent can select that hero. // Add a totally useless `foo` parameter for kicks. - this.router.navigate(['/crisis-center', { id: crisisId, foo: 'foo' }], { relativeTo: this.route }); + this.router.navigate(['/crisis-center', { id: crisisId, foo: 'foo' }]); } // #enddocregion gotoCrises // #docregion cancel-save diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts index 95df7d567f..303f8c6d43 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-detail.component.ts @@ -1,12 +1,11 @@ // #docplaster // #docregion -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { Router, ActivatedRoute } from '@angular/router'; -import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/observable/fromPromise'; +import { Component, OnInit } from '@angular/core'; +import { Router, ActivatedRoute } from '@angular/router'; -import { Crisis, CrisisService } from './crisis.service'; -import { DialogService } from '../dialog.service'; +import { Crisis } from './crisis.service'; +import { DialogService } from '../dialog.service'; +import { Observable } from 'rxjs/Observable'; @Component({ template: ` @@ -27,40 +26,24 @@ import { DialogService } from '../dialog.service'; styles: ['input {width: 20em}'] }) -export class CrisisDetailComponent implements OnInit, OnDestroy { +export class CrisisDetailComponent implements OnInit { crisis: Crisis; editName: string; - private sub: any; constructor( - private service: CrisisService, private route: ActivatedRoute, private router: Router, - private dialogService: DialogService + public dialogService: DialogService ) { } +// #docregion crisis-detail-resolve ngOnInit() { - this.sub = this.route - .params - .subscribe(params => { - let id = +params['id']; - this.service.getCrisis(id) - .then(crisis => { - if (crisis) { - this.editName = crisis.name; - this.crisis = crisis; - } else { // id not found - this.gotoCrises(); - } - }); - }); - } - - ngOnDestroy() { - if (this.sub) { - this.sub.unsubscribe(); - } + this.route.data.forEach((data: { crisis: Crisis }) => { + this.editName = data.crisis.name; + this.crisis = data.crisis; + }); } +// #enddocregion crisis-detail-resolve cancel() { this.gotoCrises(); @@ -71,16 +54,14 @@ export class CrisisDetailComponent implements OnInit, OnDestroy { this.gotoCrises(); } - canDeactivate(): Observable | boolean { + canDeactivate(): Observable | 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 - let p = this.dialogService.confirm('Discard changes?'); - let o = Observable.fromPromise(p); - return o; + return this.dialogService.confirm('Discard changes?'); } // #docregion gotoCrises @@ -91,7 +72,7 @@ export class CrisisDetailComponent implements OnInit, OnDestroy { // Add a totally useless `foo` parameter for kicks. // #docregion gotoCrises-navigate // Absolute link - this.router.navigate(['/crisis-center', {id: crisisId, foo: 'foo'}]); + this.router.navigate(['/crisis-center', { id: crisisId, foo: 'foo' }]); // #enddocregion gotoCrises-navigate } // #enddocregion gotoCrises diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts index c962ee0134..2d1df3df33 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.1.ts @@ -4,6 +4,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Crisis, CrisisService } from './crisis.service'; +import { Subscription } from 'rxjs/Subscription'; @Component({ // #docregion template @@ -20,7 +21,7 @@ import { Crisis, CrisisService } from './crisis.service'; export class CrisisListComponent implements OnInit, OnDestroy { crises: Crisis[]; selectedId: number; - private sub: any; + private sub: Subscription; constructor( private service: CrisisService, diff --git a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts index df51b14ade..7a6dcb2e9e 100644 --- a/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts +++ b/public/docs/_examples/router/ts/app/crisis-center/crisis-list.component.ts @@ -4,6 +4,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { Crisis, CrisisService } from './crisis.service'; +import { Subscription } from 'rxjs/Subscription'; @Component({ template: ` @@ -14,12 +15,12 @@ import { Crisis, CrisisService } from './crisis.service'; {{crisis.id}} {{crisis.name}} - `, + ` }) export class CrisisListComponent implements OnInit, OnDestroy { crises: Crisis[]; private selectedId: number; - private sub: any; + private sub: Subscription; constructor( private service: CrisisService, diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts index e84e4ad3e1..338b9ce3c4 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.1.ts @@ -4,6 +4,7 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { Hero, HeroService } from './hero.service'; +import { Subscription } from 'rxjs/Subscription'; @Component({ template: ` @@ -20,12 +21,12 @@ import { Hero, HeroService } from './hero.service';

    - `, + ` }) export class HeroDetailComponent implements OnInit, OnDestroy { hero: Hero; // #docregion ngOnInit - private sub: any; + private sub: Subscription; // #enddocregion ngOnInit // #docregion ctor diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.2.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.2.ts index 628a6e2dd1..07b876c79f 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.2.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.2.ts @@ -20,7 +20,7 @@ import { Hero, HeroService } from './hero.service';

    - `, + ` }) export class HeroDetailComponent implements OnInit { hero: Hero; diff --git a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts index 7d5fb1cf96..20b851ae6e 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-detail.component.ts @@ -3,7 +3,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; -import { Hero, HeroService } from './hero.service'; +import { Hero, HeroService } from './hero.service'; +import { Subscription } from 'rxjs/Subscription'; @Component({ template: ` @@ -20,13 +21,13 @@ import { Hero, HeroService } from './hero.service';

    - `, + ` }) export class HeroDetailComponent implements OnInit, OnDestroy { hero: Hero; // #docregion ngOnInit - private sub: any; + private sub: Subscription; // #enddocregion ngOnInit // #docregion ctor diff --git a/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts b/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts index 72dd3e0b25..831f24adc4 100644 --- a/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts +++ b/public/docs/_examples/router/ts/app/heroes/hero-list.component.ts @@ -6,7 +6,8 @@ import { Component, OnInit, OnDestroy } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; // #enddocregion import-router -import { Hero, HeroService } from './hero.service'; +import { Hero, HeroService } from './hero.service'; +import { Subscription } from 'rxjs/Subscription'; @Component({ // #docregion template @@ -27,7 +28,7 @@ export class HeroListComponent implements OnInit, OnDestroy { // #docregion ctor private selectedId: number; - private sub: any; + private sub: Subscription; constructor( private service: HeroService, diff --git a/public/docs/_examples/router/ts/app/heroes/heroes.module.1.ts b/public/docs/_examples/router/ts/app/heroes/heroes.module.1.ts new file mode 100644 index 0000000000..973102f83b --- /dev/null +++ b/public/docs/_examples/router/ts/app/heroes/heroes.module.1.ts @@ -0,0 +1,25 @@ +// #docregion +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'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule + ], + declarations: [ + HeroListComponent, + HeroDetailComponent + ], + providers: [ + HeroService + ] +}) +export class HeroesModule {} +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/heroes/heroes.module.ts b/public/docs/_examples/router/ts/app/heroes/heroes.module.ts new file mode 100644 index 0000000000..1816ef1e56 --- /dev/null +++ b/public/docs/_examples/router/ts/app/heroes/heroes.module.ts @@ -0,0 +1,30 @@ +// #docregion +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'; + +// #docregion heroes-routes +import { heroesRouting } from './heroes.routing'; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + heroesRouting + ], + declarations: [ + HeroListComponent, + HeroDetailComponent + ], + providers: [ + HeroService + ] +}) +// #enddocregion heroes-routes +export class HeroesModule {} +// #enddocregion diff --git a/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts b/public/docs/_examples/router/ts/app/heroes/heroes.routing.ts similarity index 68% rename from public/docs/_examples/router/ts/app/heroes/heroes.routes.ts rename to public/docs/_examples/router/ts/app/heroes/heroes.routing.ts index f6c2b19c75..89270ec492 100644 --- a/public/docs/_examples/router/ts/app/heroes/heroes.routes.ts +++ b/public/docs/_examples/router/ts/app/heroes/heroes.routing.ts @@ -1,12 +1,15 @@ // #docregion -import { RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + import { HeroListComponent } from './hero-list.component'; import { HeroDetailComponent } from './hero-detail.component'; -export const heroesRoutes: RouterConfig = [ +const heroesRoutes: Routes = [ { path: 'heroes', component: HeroListComponent }, // #docregion hero-detail-route { path: 'hero/:id', component: HeroDetailComponent } // #enddocregion hero-detail-route ]; + +export const heroesRouting = RouterModule.forChild(heroesRoutes); // #enddocregion diff --git a/public/docs/_examples/router/ts/app/login.component.1.ts b/public/docs/_examples/router/ts/app/login.component.1.ts new file mode 100644 index 0000000000..ddee339011 --- /dev/null +++ b/public/docs/_examples/router/ts/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}}

    +

    + + +

    ` +}) +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/app/login.component.ts b/public/docs/_examples/router/ts/app/login.component.ts index ddee339011..6dd5605fb1 100755 --- a/public/docs/_examples/router/ts/app/login.component.ts +++ b/public/docs/_examples/router/ts/app/login.component.ts @@ -1,7 +1,8 @@ // #docregion -import { Component } from '@angular/core'; -import { Router } from '@angular/router'; -import { AuthService } from './auth.service'; +import { Component } from '@angular/core'; +import { Router, + NavigationExtras } from '@angular/router'; +import { AuthService } from './auth.service'; @Component({ template: ` @@ -33,8 +34,17 @@ export class LoginComponent { // If no redirect has been set, use the default let redirect = this.authService.redirectUrl ? this.authService.redirectUrl : '/crisis-center/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]); + this.router.navigate([redirect], navigationExtras); + // #enddocregion preserve } }); } diff --git a/public/docs/_examples/router/ts/app/login.routes.ts b/public/docs/_examples/router/ts/app/login.routes.ts deleted file mode 100644 index 8bfc1ff10d..0000000000 --- a/public/docs/_examples/router/ts/app/login.routes.ts +++ /dev/null @@ -1,11 +0,0 @@ -// #docregion -import { RouterConfig } from '@angular/router'; -import { AuthGuard } from './auth-guard.service'; -import { AuthService } from './auth.service'; -import { LoginComponent } from './login.component'; - -export const loginRoutes: RouterConfig = [ - { path: 'login', component: LoginComponent } -]; - -export const authProviders = [AuthGuard, AuthService]; diff --git a/public/docs/_examples/router/ts/app/login.routing.ts b/public/docs/_examples/router/ts/app/login.routing.ts new file mode 100644 index 0000000000..9e3eaf2350 --- /dev/null +++ b/public/docs/_examples/router/ts/app/login.routing.ts @@ -0,0 +1,14 @@ +// #docregion +import { Routes } from '@angular/router'; +import { AuthGuard } from './auth-guard.service'; +import { AuthService } from './auth.service'; +import { LoginComponent } from './login.component'; + +export const loginRoutes: Routes = [ + { path: 'login', component: LoginComponent } +]; + +export const authProviders = [ + AuthGuard, + AuthService +]; diff --git a/public/docs/_examples/router/ts/app/main.1.ts b/public/docs/_examples/router/ts/app/main.1.ts deleted file mode 100644 index deff5b9ac5..0000000000 --- a/public/docs/_examples/router/ts/app/main.1.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* First version */ -// #docplaster -// #docregion all -// main entry point -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; -import { appRouterProviders } from './app.routes'; - -// #enddocregion - -/* Can't use AppComponent ... but display as if we can -// #docregion all - -bootstrap(AppComponent, [ -// #enddocregion all -*/ -// Actually use the v.1 component -import { AppComponent as ac } from './app.component.ts'; // './app.component.1'; - -bootstrap(ac, [ -// #docregion all - appRouterProviders -]) -.catch(err => console.error(err)); -// #enddocregion all diff --git a/public/docs/_examples/router/ts/app/main.2.ts b/public/docs/_examples/router/ts/app/main.2.ts deleted file mode 100644 index 10e4e4b18a..0000000000 --- a/public/docs/_examples/router/ts/app/main.2.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* Second version */ -// For Milestone #2 -// Also includes digression on HashPathStrategy (not used in the final app) -// #docplaster -// #docregion -// main entry point -import { bootstrap } from '@angular/platform-browser-dynamic'; - -// Add these symbols to override the `LocationStrategy` -import { LocationStrategy, - HashLocationStrategy } from '@angular/common'; - -import { AppComponent } from './app.component'; -import { appRouterProviders } from './app.routes'; - -// #enddocregion - -/* Can't use AppComponent ... but display as if we can -// #docregion -bootstrap(AppComponent, [ -// #enddocregion -*/ -// Actually use the v.2 component -import { AppComponent as ac } from './app.component.ts'; // './app.component.2'; - -bootstrap(ac, [ -// #docregion - appRouterProviders, - { provide: LocationStrategy, useClass: HashLocationStrategy } // .../#/crisis-center/ - -]) -.catch(err => console.error(err)); -// #enddocregion diff --git a/public/docs/_examples/router/ts/app/main.3.ts b/public/docs/_examples/router/ts/app/main.3.ts deleted file mode 100644 index e1be742421..0000000000 --- a/public/docs/_examples/router/ts/app/main.3.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* third version */ -// #docregion -// main entry point -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component.3'; -import { appRouterProviders } from './app.routes'; - -bootstrap(AppComponent, [ - appRouterProviders -]) -.catch(err => console.error(err)); diff --git a/public/docs/_examples/router/ts/app/main.ts b/public/docs/_examples/router/ts/app/main.ts index 6142cad5ee..ba9421d573 100644 --- a/public/docs/_examples/router/ts/app/main.ts +++ b/public/docs/_examples/router/ts/app/main.ts @@ -1,10 +1,6 @@ // #docregion -// main entry point -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; -import { appRouterProviders } from './app.routes'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; -bootstrap(AppComponent, [ - appRouterProviders -]) -.catch(err => console.error(err)); +import { AppModule } from './app.module'; + +browserDynamicPlatform().bootstrapModule(AppModule); diff --git a/public/docs/_examples/router/ts/index.2.html b/public/docs/_examples/router/ts/index.2.html deleted file mode 100644 index 2330a69e75..0000000000 --- a/public/docs/_examples/router/ts/index.2.html +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - Router Sample v.2 - - - - - - - - - - - - - - - - -

    Milestone 2

    - loading... - - - -

    Milestone 2

    - loading... - - - - diff --git a/public/docs/_examples/router/ts/plnkr.json b/public/docs/_examples/router/ts/plnkr.json index 73d83adaf6..97c725fd7d 100644 --- a/public/docs/_examples/router/ts/plnkr.json +++ b/public/docs/_examples/router/ts/plnkr.json @@ -6,7 +6,8 @@ "!**/*.[1,2,3,4,5].*", "!app/crisis-list.component.ts", "!app/hero-list.component.ts", - "!app/crisis-center/add-crisis.component.ts" + "!app/crisis-center/add-crisis.component.ts", + "!app/not-found.component.ts" ], "tags": ["router"] } diff --git a/public/docs/_examples/security/ts/app/app.component.ts b/public/docs/_examples/security/ts/app/app.component.ts index a2fc7e7320..c30235e8e7 100644 --- a/public/docs/_examples/security/ts/app/app.component.ts +++ b/public/docs/_examples/security/ts/app/app.component.ts @@ -1,20 +1,13 @@ // #docregion import { Component } from '@angular/core'; -import { BypassSecurityComponent } from './bypass-security.component'; -import { InnerHtmlBindingComponent } from './inner-html-binding.component'; - @Component({ selector: 'my-app', template: `

    Security

    - `, - directives: [ - BypassSecurityComponent, - InnerHtmlBindingComponent, - ] + ` }) export class AppComponent { } diff --git a/public/docs/_examples/security/ts/app/app.module.ts b/public/docs/_examples/security/ts/app/app.module.ts new file mode 100644 index 0000000000..21d880be3b --- /dev/null +++ b/public/docs/_examples/security/ts/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/app/main.ts b/public/docs/_examples/security/ts/app/main.ts index 3e1476beac..fc0ac5294a 100644 --- a/public/docs/_examples/security/ts/app/main.ts +++ b/public/docs/_examples/security/ts/app/main.ts @@ -1,8 +1,6 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -// #docregion import -import { AppComponent } from './app.component'; -// #enddocregion import +platformBrowserDynamic().bootstrapModule(AppModule); -bootstrap(AppComponent); diff --git a/public/docs/_examples/server-communication/ts/app/app.component.ts b/public/docs/_examples/server-communication/ts/app/app.component.ts index 144475d0e4..3a532b6524 100644 --- a/public/docs/_examples/server-communication/ts/app/app.component.ts +++ b/public/docs/_examples/server-communication/ts/app/app.component.ts @@ -7,12 +7,6 @@ import { Component } from '@angular/core'; import './rxjs-operators'; // #enddocregion import-rxjs -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'; - @Component({ selector: 'my-app', template: ` @@ -20,18 +14,7 @@ import { WikiSmartComponent } from './wiki/wiki-smart.component'; - `, -// #enddocregion -/* -// #docregion http-providers - providers: [ HTTP_PROVIDERS ] -// #enddocregion http-providers -*/ -// #docregion - directives: [ - HeroListComponent, HeroListPromiseComponent, - WikiComponent, WikiSmartComponent - ] + ` }) export class AppComponent { } // #enddocregion diff --git a/public/docs/_examples/server-communication/ts/app/app.module.1.ts b/public/docs/_examples/server-communication/ts/app/app.module.1.ts new file mode 100644 index 0000000000..51e52573cb --- /dev/null +++ b/public/docs/_examples/server-communication/ts/app/app.module.1.ts @@ -0,0 +1,22 @@ +// #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/app/app.module.ts b/public/docs/_examples/server-communication/ts/app/app.module.ts new file mode 100644 index 0000000000..528110661b --- /dev/null +++ b/public/docs/_examples/server-communication/ts/app/app.module.ts @@ -0,0 +1,41 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; +import { HttpModule, JsonpModule, XHRBackend } from '@angular/http'; + +import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; +import { HeroData } from './hero-data'; +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 + ], + providers: [ + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: HeroData } // in-mem server data + ], + declarations: [ + AppComponent, + HeroListComponent, + HeroListPromiseComponent, + WikiComponent, + WikiSmartComponent + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } + + + + diff --git a/public/docs/_examples/server-communication/ts/app/main.ts b/public/docs/_examples/server-communication/ts/app/main.ts index 194ab5c5fb..4acf5de663 100644 --- a/public/docs/_examples/server-communication/ts/app/main.ts +++ b/public/docs/_examples/server-communication/ts/app/main.ts @@ -1,29 +1,5 @@ -// #docplaster -// #docregion final -// Imports for loading & configuring the in-memory web api -import { XHRBackend } from '@angular/http'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { InMemoryBackendService, - SEED_DATA } from 'angular2-in-memory-web-api'; -import { HeroData } from './hero-data'; - -// The usual bootstrapping imports -// #docregion v1 -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { HTTP_PROVIDERS } from '@angular/http'; - -import { AppComponent } from './app.component'; - -// #enddocregion v1, final -/* -// #docregion v1 -bootstrap(AppComponent, [ HTTP_PROVIDERS ]); -// #enddocregion v1 - */ -// #docregion final -bootstrap(AppComponent, [ - HTTP_PROVIDERS, - { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server - { provide: SEED_DATA, useClass: HeroData } // in-mem server data -]); -// #enddocregion final +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/server-communication/ts/app/wiki/wiki-smart.component.ts b/public/docs/_examples/server-communication/ts/app/wiki/wiki-smart.component.ts index 608ee996b0..e6bbce139b 100644 --- a/public/docs/_examples/server-communication/ts/app/wiki/wiki-smart.component.ts +++ b/public/docs/_examples/server-communication/ts/app/wiki/wiki-smart.component.ts @@ -1,6 +1,5 @@ // #docregion import { Component } from '@angular/core'; -import { JSONP_PROVIDERS } from '@angular/http'; import { Observable } from 'rxjs/Observable'; // #docregion import-subject import { Subject } from 'rxjs/Subject'; @@ -20,7 +19,7 @@ import { WikipediaService } from './wikipedia.service';
  • {{item}}
  • `, - providers: [JSONP_PROVIDERS, WikipediaService] + providers: [WikipediaService] }) export class WikiSmartComponent { diff --git a/public/docs/_examples/server-communication/ts/app/wiki/wiki.component.ts b/public/docs/_examples/server-communication/ts/app/wiki/wiki.component.ts index 6253e0933b..92c31afb80 100644 --- a/public/docs/_examples/server-communication/ts/app/wiki/wiki.component.ts +++ b/public/docs/_examples/server-communication/ts/app/wiki/wiki.component.ts @@ -1,6 +1,5 @@ // #docregion import { Component } from '@angular/core'; -import { JSONP_PROVIDERS } from '@angular/http'; import { Observable } from 'rxjs/Observable'; import { WikipediaService } from './wikipedia.service'; @@ -17,7 +16,7 @@ import { WikipediaService } from './wikipedia.service';
  • {{item}}
  • `, - providers: [JSONP_PROVIDERS, WikipediaService] + providers: [WikipediaService] }) export class WikiComponent { items: Observable; diff --git a/public/docs/_examples/server-communication/ts/app/wiki/wikipedia.service.ts b/public/docs/_examples/server-communication/ts/app/wiki/wikipedia.service.ts index 8bc5747e43..96eca70af1 100644 --- a/public/docs/_examples/server-communication/ts/app/wiki/wikipedia.service.ts +++ b/public/docs/_examples/server-communication/ts/app/wiki/wikipedia.service.ts @@ -12,7 +12,7 @@ export class WikipediaService { // #docregion search-parameters let params = new URLSearchParams(); -params.set('search', term); // the user's search value + params.set('search', term); // the user's search value params.set('action', 'opensearch'); params.set('format', 'json'); params.set('callback', 'JSONP_CALLBACK'); diff --git a/public/docs/_examples/structural-directives/ts/app/app.module.ts b/public/docs/_examples/structural-directives/ts/app/app.module.ts new file mode 100644 index 0000000000..0712db6d4b --- /dev/null +++ b/public/docs/_examples/structural-directives/ts/app/app.module.ts @@ -0,0 +1,18 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { StructuralDirectivesComponent } from './structural-directives.component'; +import { UnlessDirective } from './unless.directive'; +import { HeavyLoaderComponent } from './heavy-loader.component'; + +@NgModule({ + imports: [ BrowserModule ], + declarations: [ + StructuralDirectivesComponent, + UnlessDirective, + HeavyLoaderComponent + ], + bootstrap: [ StructuralDirectivesComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/structural-directives/ts/app/main.ts b/public/docs/_examples/structural-directives/ts/app/main.ts index 5f5b5aeb19..fc0ac5294a 100644 --- a/public/docs/_examples/structural-directives/ts/app/main.ts +++ b/public/docs/_examples/structural-directives/ts/app/main.ts @@ -1,5 +1,6 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { StructuralDirectivesComponent } from './structural-directives.component'; +platformBrowserDynamic().bootstrapModule(AppModule); -bootstrap(StructuralDirectivesComponent); diff --git a/public/docs/_examples/structural-directives/ts/app/structural-directives.component.ts b/public/docs/_examples/structural-directives/ts/app/structural-directives.component.ts index 1e5a9ef939..8e36da3462 100644 --- a/public/docs/_examples/structural-directives/ts/app/structural-directives.component.ts +++ b/public/docs/_examples/structural-directives/ts/app/structural-directives.component.ts @@ -1,14 +1,11 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -import { UnlessDirective } from './unless.directive'; -import { HeavyLoaderComponent } from './heavy-loader.component'; @Component({ selector: 'structural-directives', templateUrl: 'app/structural-directives.component.html', - styles: ['button { min-width: 100px; }'], - directives: [UnlessDirective, HeavyLoaderComponent] + styles: ['button { min-width: 100px; }'] }) export class StructuralDirectivesComponent { heroes = ['Mr. Nice', 'Narco', 'Bombasto']; diff --git a/public/docs/_examples/style-guide/ts/01-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/01-01/app/app.component.ts index 67ae0213be..a9f29647a0 100644 --- a/public/docs/_examples/style-guide/ts/01-01/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/01-01/app/app.component.ts @@ -1,7 +1,7 @@ // #docregion import { Component } from '@angular/core'; -import { HeroesComponent, HeroService } from './heroes'; +import { HeroService } from './heroes'; @Component({ moduleId: module.id, @@ -10,7 +10,6 @@ import { HeroesComponent, HeroService } from './heroes'; `, styleUrls: ['app.component.css'], - directives: [HeroesComponent], - providers: [HeroService] + providers: [ HeroService ] }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/01-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/01-01/app/app.module.ts new file mode 100644 index 0000000000..53f29ea8cb --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/01-01/app/heroes/hero.component.avoid.ts b/public/docs/_examples/style-guide/ts/01-01/app/heroes/hero.component.avoid.ts index e19934c4d2..853e6ab64e 100644 --- a/public/docs/_examples/style-guide/ts/01-01/app/heroes/hero.component.avoid.ts +++ b/public/docs/_examples/style-guide/ts/01-01/app/heroes/hero.component.avoid.ts @@ -1,8 +1,9 @@ // #docregion /* avoid */ -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { Component, OnInit } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule, Component, OnInit } from '@angular/core'; class Hero { id: number; @@ -27,7 +28,15 @@ class AppComponent implements OnInit { } } -bootstrap(AppComponent, []); +@NgModule({ + imports: [ BrowserModule ], + declarations: [ AppComponent ], + exports: [ AppComponent ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } + +platformBrowserDynamic().bootstrapModule(AppModule); const HEROES: Hero[] = [ {id: 1, name: 'Bombasto'}, diff --git a/public/docs/_examples/style-guide/ts/01-01/main.ts b/public/docs/_examples/style-guide/ts/01-01/main.ts index 6b97e81a59..7e8269bd65 100644 --- a/public/docs/_examples/style-guide/ts/01-01/main.ts +++ b/public/docs/_examples/style-guide/ts/01-01/main.ts @@ -1,6 +1,6 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app/app.component'; +import { AppModule } from './app/app.module'; -bootstrap(AppComponent, []); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/style-guide/ts/02-07/app/app.component.ts b/public/docs/_examples/style-guide/ts/02-07/app/app.component.ts index 9e4bb0b8c5..c82e12624d 100644 --- a/public/docs/_examples/style-guide/ts/02-07/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/02-07/app/app.component.ts @@ -1,14 +1,10 @@ import { Component } from '@angular/core'; -import { HeroComponent } from './heroes'; -import { UsersComponent } from './users'; - @Component({ selector: 'sg-app', template: ` - `, - directives: [HeroComponent, UsersComponent] + ` }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/02-07/app/app.module.ts b/public/docs/_examples/style-guide/ts/02-07/app/app.module.ts new file mode 100644 index 0000000000..4320fe67d5 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/02-08/app/app.component.ts b/public/docs/_examples/style-guide/ts/02-08/app/app.component.ts index 29b3ed7062..bf27aeaf8a 100644 --- a/public/docs/_examples/style-guide/ts/02-08/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/02-08/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { ValidateDirective } from './shared'; - @Component({ selector: 'sg-app', - template: '', - directives: [ValidateDirective] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/02-08/app/app.module.ts b/public/docs/_examples/style-guide/ts/02-08/app/app.module.ts new file mode 100644 index 0000000000..a86c713d4f --- /dev/null +++ b/public/docs/_examples/style-guide/ts/02-08/app/app.module.ts @@ -0,0 +1,17 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { AppComponent } from './app.component'; +import { ValidateDirective } from './shared'; + +@NgModule({ + imports: [ + RouterModule.forChild([{ path: '02-08', component: AppComponent }]) + ], + declarations: [ + AppComponent, + ValidateDirective + ], + exports: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/03-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/03-01/app/app.module.ts new file mode 100644 index 0000000000..48079f21c7 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/03-02/app/app.module.ts b/public/docs/_examples/style-guide/ts/03-02/app/app.module.ts new file mode 100644 index 0000000000..2db4012ebf --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/03-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/03-03/app/app.module.ts new file mode 100644 index 0000000000..29b3d2e765 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/03-04/app/app.module.ts b/public/docs/_examples/style-guide/ts/03-04/app/app.module.ts new file mode 100644 index 0000000000..a5a8d5bb4e --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/03-05/app/app.module.ts b/public/docs/_examples/style-guide/ts/03-05/app/app.module.ts new file mode 100644 index 0000000000..b70c8416c7 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/03-05/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'; + +export const route = { path: '03-05', component: AppComponent }; + +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forChild([{ path: '03-05', component: AppComponent }]) + ], + declarations: [ + AppComponent + ], + exports: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/03-06/app/app.module.ts b/public/docs/_examples/style-guide/ts/03-06/app/app.module.ts new file mode 100644 index 0000000000..f259ce23a2 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/04-10/app/+heroes/index.ts b/public/docs/_examples/style-guide/ts/04-10/app/+heroes/index.ts deleted file mode 100644 index dbf3079697..0000000000 --- a/public/docs/_examples/style-guide/ts/04-10/app/+heroes/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './heroes.component'; diff --git a/public/docs/_examples/style-guide/ts/04-10/app/app.component.ts b/public/docs/_examples/style-guide/ts/04-10/app/app.component.ts index b7554b8d06..aef486d9a2 100644 --- a/public/docs/_examples/style-guide/ts/04-10/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/04-10/app/app.component.ts @@ -1,13 +1,9 @@ // #docregion import { Component } from '@angular/core'; -// #docregion example -import { HeroesComponent } from './+heroes'; -// #enddocregion example - @Component({ selector: 'sg-app', - template: '', - directives: [HeroesComponent] + template: '' }) -export class AppComponent { } +export class AppComponent { + } diff --git a/public/docs/_examples/style-guide/ts/04-10/app/app.module.ts b/public/docs/_examples/style-guide/ts/04-10/app/app.module.ts new file mode 100644 index 0000000000..dec8d2c6f2 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/04-10/app/app.module.ts @@ -0,0 +1,22 @@ +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'; +import { SharedModule } from './shared/shared.module'; + +@NgModule({ + imports: [ + BrowserModule, + SharedModule, + RouterModule.forChild([{ path: '04-10', component: AppComponent }]) + ], + declarations: [ + AppComponent, + HeroesComponent + ], + exports: [ AppComponent ], + entryComponents: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.avoid.ts b/public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.avoid.ts similarity index 94% rename from public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.avoid.ts rename to public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.avoid.ts index b61e3ab782..2d10e7b3c3 100644 --- a/public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.avoid.ts +++ b/public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.avoid.ts @@ -1,3 +1,4 @@ +/* tslint:disable:no-unused-variable */ // #docregion // #docregion example /* avoid */ diff --git a/public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.html b/public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.html similarity index 100% rename from public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.html rename to public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.html diff --git a/public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.ts b/public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.ts similarity index 86% rename from public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.ts rename to public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.ts index 9ca611ee5c..1443a5b445 100644 --- a/public/docs/_examples/style-guide/ts/04-10/app/+heroes/heroes.component.ts +++ b/public/docs/_examples/style-guide/ts/04-10/app/heroes/heroes.component.ts @@ -7,8 +7,6 @@ import { CONFIG, EntityService, ExceptionService, - FilterTextComponent, - InitCapsPipe, SpinnerService, ToastService } from '../shared'; @@ -17,8 +15,6 @@ import { // #enddocregion example moduleId: module.id, providers: [EntityService, ExceptionService, SpinnerService, ToastService], - directives: [FilterTextComponent], - pipes: [InitCapsPipe], // #docregion example selector: 'toh-heroes', templateUrl: 'heroes.component.html' diff --git a/public/docs/_examples/style-guide/ts/04-10/app/index.ts b/public/docs/_examples/style-guide/ts/04-10/app/index.ts deleted file mode 100644 index aa9e5b0b7c..0000000000 --- a/public/docs/_examples/style-guide/ts/04-10/app/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from './+heroes'; -export * from './shared'; -export * from './app.component'; diff --git a/public/docs/_examples/style-guide/ts/04-10/app/shared/shared.module.ts b/public/docs/_examples/style-guide/ts/04-10/app/shared/shared.module.ts new file mode 100644 index 0000000000..6500b28330 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/04-10/app/shared/shared.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { FilterTextComponent, + InitCapsPipe, + ModalComponent, + NavComponent, + SpinnerComponent } from './'; + +const declarations = [ + FilterTextComponent, InitCapsPipe, ModalComponent, + NavComponent, SpinnerComponent, +]; + +@NgModule({ + imports: [ BrowserModule ], + declarations: declarations, + exports: declarations +}) +export class SharedModule { } diff --git a/public/docs/_examples/style-guide/ts/04-14/app/app.component.ts b/public/docs/_examples/style-guide/ts/04-14/app/app.component.ts index ebf8146349..0e43893f7f 100644 --- a/public/docs/_examples/style-guide/ts/04-14/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/04-14/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { HeroesComponent } from './+heroes'; - @Component({ selector: 'sg-app', - template: '', - directives: [HeroesComponent] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/04-14/app/app.module.ts b/public/docs/_examples/style-guide/ts/04-14/app/app.module.ts new file mode 100644 index 0000000000..be01c06624 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/04-14/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 { HeroesComponent } from './+heroes'; + +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forChild([{ path: '04-14', component: AppComponent }]) + ], + declarations: [ + AppComponent, + HeroesComponent + ], + exports: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/05-02/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-02/app/app.component.ts index 76bf4eafd5..03062c57fd 100644 --- a/public/docs/_examples/style-guide/ts/05-02/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-02/app/app.component.ts @@ -1,11 +1,8 @@ import { Component } from '@angular/core'; -import { HeroButtonComponent } from './heroes'; - @Component({ moduleId: module.id, selector: 'sg-app', - templateUrl: 'app.component.html', - directives: [HeroButtonComponent] + templateUrl: 'app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-02/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-02/app/app.module.ts new file mode 100644 index 0000000000..1c458e2ca1 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-03/app/app.component.ts index 76bf4eafd5..03062c57fd 100644 --- a/public/docs/_examples/style-guide/ts/05-03/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-03/app/app.component.ts @@ -1,11 +1,8 @@ import { Component } from '@angular/core'; -import { HeroButtonComponent } from './heroes'; - @Component({ moduleId: module.id, selector: 'sg-app', - templateUrl: 'app.component.html', - directives: [HeroButtonComponent] + templateUrl: 'app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-03/app/app.module.ts new file mode 100644 index 0000000000..1b754e3ee5 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-04/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-04/app/app.component.ts index c8e62772b4..0e43893f7f 100644 --- a/public/docs/_examples/style-guide/ts/05-04/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-04/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { HeroesComponent } from './heroes'; - @Component({ selector: 'sg-app', - template: '', - directives: [HeroesComponent] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-04/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-04/app/app.module.ts new file mode 100644 index 0000000000..9cae85085a --- /dev/null +++ b/public/docs/_examples/style-guide/ts/05-04/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 { HeroesComponent } from './heroes'; + +@NgModule({ + imports: [ + BrowserModule, + RouterModule.forChild([{ path: '05-04', component: AppComponent }]) + ], + declarations: [ + AppComponent, + HeroesComponent + ], + exports: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/05-12/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-12/app/app.component.ts index c6bf721e40..dac40205c9 100644 --- a/public/docs/_examples/style-guide/ts/05-12/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-12/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { HeroButtonComponent } from './heroes'; - @Component({ selector: 'sg-app', - template: '', - directives: [HeroButtonComponent] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-12/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-12/app/app.module.ts new file mode 100644 index 0000000000..5177b2cc64 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-13/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-13/app/app.component.ts index 76bf4eafd5..03062c57fd 100644 --- a/public/docs/_examples/style-guide/ts/05-13/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-13/app/app.component.ts @@ -1,11 +1,8 @@ import { Component } from '@angular/core'; -import { HeroButtonComponent } from './heroes'; - @Component({ moduleId: module.id, selector: 'sg-app', - templateUrl: 'app.component.html', - directives: [HeroButtonComponent] + templateUrl: 'app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-13/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-13/app/app.module.ts new file mode 100644 index 0000000000..102ed0f617 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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 } from './heroes'; + +@NgModule({ + imports: [ + RouterModule.forChild([{ path: '05-13', component: AppComponent }]) + ], + declarations: [ + AppComponent, + HeroButtonComponent + ], + exports: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/05-14/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-14/app/app.component.ts index 05e38bf35c..8ed6da4c82 100644 --- a/public/docs/_examples/style-guide/ts/05-14/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-14/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { ToastComponent } from './shared'; - @Component({ selector: 'sg-app', - template: ``, - directives: [ToastComponent] + template: `` }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-14/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-14/app/app.module.ts new file mode 100644 index 0000000000..0b294573d2 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-15/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-15/app/app.component.ts index 7fd78842b8..91b569b1e7 100644 --- a/public/docs/_examples/style-guide/ts/05-15/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-15/app/app.component.ts @@ -1,11 +1,10 @@ import { Component } from '@angular/core'; -import { HeroListComponent, HeroService } from './heroes'; +import { HeroService } from './heroes'; @Component({ selector: 'sg-app', template: '', - directives: [HeroListComponent], providers: [HeroService] }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-15/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-15/app/app.module.ts new file mode 100644 index 0000000000..9bd4b8c9a2 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-16/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-16/app/app.component.ts index 39fab1a724..03062c57fd 100644 --- a/public/docs/_examples/style-guide/ts/05-16/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-16/app/app.component.ts @@ -1,11 +1,8 @@ import { Component } from '@angular/core'; -import { HeroComponent } from './heroes'; - @Component({ moduleId: module.id, selector: 'sg-app', - templateUrl: 'app.component.html', - directives: [HeroComponent] + templateUrl: 'app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-16/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-16/app/app.module.ts new file mode 100644 index 0000000000..c3fb36f8ac --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-17/app/app.component.ts b/public/docs/_examples/style-guide/ts/05-17/app/app.component.ts index 8e2e7e727c..86728b8b80 100644 --- a/public/docs/_examples/style-guide/ts/05-17/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/05-17/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { HeroListComponent } from './heroes'; - @Component({ selector: 'sg-app', - template: '', - directives: [HeroListComponent] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/05-17/app/app.module.ts b/public/docs/_examples/style-guide/ts/05-17/app/app.module.ts new file mode 100644 index 0000000000..e850d80ae3 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-17/app/heroes/hero-list/hero-list.component.ts b/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero-list/hero-list.component.ts index 8570544a36..b8ebd4cc46 100644 --- a/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero-list/hero-list.component.ts +++ b/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero-list/hero-list.component.ts @@ -10,8 +10,8 @@ import { Hero } from '../shared/hero.model.ts'; template: `
    Our list of heroes: - - + + Total powers: {{totalPowers}}
    Average power: {{avgPower}}
    diff --git a/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero/hero.component.ts b/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero/hero.component.ts new file mode 100644 index 0000000000..334f836a7d --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/05-17/app/heroes/hero/index.ts b/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero/index.ts new file mode 100644 index 0000000000..084f36d703 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/05-17/app/heroes/hero/index.ts @@ -0,0 +1 @@ +export * from './hero.component'; diff --git a/public/docs/_examples/style-guide/ts/05-17/app/heroes/index.ts b/public/docs/_examples/style-guide/ts/05-17/app/heroes/index.ts index f1112f1c7c..dcf3e79bd3 100644 --- a/public/docs/_examples/style-guide/ts/05-17/app/heroes/index.ts +++ b/public/docs/_examples/style-guide/ts/05-17/app/heroes/index.ts @@ -1,2 +1,3 @@ +export * from './hero'; export * from './hero-list'; export * from './shared'; diff --git a/public/docs/_examples/style-guide/ts/06-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/06-01/app/app.component.ts index 97a52c0c0c..03062c57fd 100644 --- a/public/docs/_examples/style-guide/ts/06-01/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/06-01/app/app.component.ts @@ -1,11 +1,8 @@ import { Component } from '@angular/core'; -import { HighlightDirective } from './shared'; - @Component({ moduleId: module.id, selector: 'sg-app', - templateUrl: 'app.component.html', - directives: [HighlightDirective] + templateUrl: 'app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/06-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/06-01/app/app.module.ts new file mode 100644 index 0000000000..318cd306d7 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/06-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts index c40b5c0406..5f4ea8dce5 100644 --- a/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/06-03/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { ValidatorDirective } from './shared'; - @Component({ selector: 'sg-app', - template: '', - directives: [ValidatorDirective] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/06-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/06-03/app/app.module.ts new file mode 100644 index 0000000000..8677138eef --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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 } from './shared'; + +@NgModule({ + imports: [ + RouterModule.forChild([{ path: '06-03', component: AppComponent }]) + ], + declarations: [ + AppComponent, + ValidatorDirective + ], + exports: [ AppComponent ] +}) +export class AppModule {} diff --git a/public/docs/_examples/style-guide/ts/07-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/07-01/app/app.module.ts new file mode 100644 index 0000000000..0077500dea --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/07-03/app/app.component.ts b/public/docs/_examples/style-guide/ts/07-03/app/app.component.ts index 276321e2f1..f4d25e1ab6 100644 --- a/public/docs/_examples/style-guide/ts/07-03/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/07-03/app/app.component.ts @@ -1,14 +1,13 @@ // #docregion import { Component } from '@angular/core'; -import { HeroListComponent, HeroService } from './heroes'; +import { HeroService } from './heroes'; @Component({ selector: 'toh-app', template: ` `, - directives: [HeroListComponent], providers: [HeroService] }) export class AppComponent {} diff --git a/public/docs/_examples/style-guide/ts/07-03/app/app.module.ts b/public/docs/_examples/style-guide/ts/07-03/app/app.module.ts new file mode 100644 index 0000000000..8ba06d22be --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/07-04/app/app.module.ts b/public/docs/_examples/style-guide/ts/07-04/app/app.module.ts new file mode 100644 index 0000000000..71c515c9c9 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/09-01/app/app.component.ts b/public/docs/_examples/style-guide/ts/09-01/app/app.component.ts index 50ddf5fe1e..ebc904f722 100644 --- a/public/docs/_examples/style-guide/ts/09-01/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/09-01/app/app.component.ts @@ -1,10 +1,7 @@ import { Component } from '@angular/core'; -import { HeroButtonComponent } from './heroes'; - @Component({ selector: 'sg-app', - template: '', - directives: [HeroButtonComponent] + template: '' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/09-01/app/app.module.ts b/public/docs/_examples/style-guide/ts/09-01/app/app.module.ts new file mode 100644 index 0000000000..5872e801d6 --- /dev/null +++ b/public/docs/_examples/style-guide/ts/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/app/app.component.ts b/public/docs/_examples/style-guide/ts/app/app.component.ts index d90494c212..0a3c992498 100644 --- a/public/docs/_examples/style-guide/ts/app/app.component.ts +++ b/public/docs/_examples/style-guide/ts/app/app.component.ts @@ -1,9 +1,7 @@ import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: [ROUTER_DIRECTIVES] + templateUrl: 'app/app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/style-guide/ts/app/app.routes.ts b/public/docs/_examples/style-guide/ts/app/app.routes.ts index 644e86bffd..3d5d6b3c15 100644 --- a/public/docs/_examples/style-guide/ts/app/app.routes.ts +++ b/public/docs/_examples/style-guide/ts/app/app.routes.ts @@ -1,62 +1,59 @@ -import { provideRouter, RouterConfig } from '@angular/router'; +import { RouterConfig } from '@angular/router'; import { AppComponent as S0101 } from '../01-01/app'; -import { AppComponent as S0207 } from '../02-07/app'; -import { AppComponent as S0208 } from '../02-08/app'; -import { AppComponent as S0301 } from '../03-01/app'; -import { AppComponent as S0302 } from '../03-02/app'; -import { AppComponent as S0303 } from '../03-03/app'; -import { AppComponent as S0304 } from '../03-04/app'; -import { AppComponent as S0305 } from '../03-05/app'; -import { AppComponent as S0306 } from '../03-06/app'; -import { AppComponent as S0410 } from '../04-10/app'; -import { AppComponent as S0414 } from '../04-14/app'; -import { AppComponent as S0502 } from '../05-02/app'; -import { AppComponent as S0503 } from '../05-03/app'; -import { AppComponent as S0504 } from '../05-04/app'; -import { AppComponent as S0512 } from '../05-12/app'; -import { AppComponent as S0513 } from '../05-13/app'; -import { AppComponent as S0514 } from '../05-14/app'; -import { AppComponent as S0515 } from '../05-15/app'; -import { AppComponent as S0516 } from '../05-16/app'; -import { AppComponent as S0517 } from '../05-17/app'; -import { AppComponent as S0601 } from '../06-01/app'; -import { AppComponent as S0603 } from '../06-03/app'; -import { AppComponent as S0701 } from '../07-01/app'; -import { AppComponent as S0703 } from '../07-03/app'; -import { AppComponent as S0704 } from '../07-04/app'; -import { AppComponent as S0901 } from '../09-01/app'; +// import { AppComponent as S0207 } from '../02-07/app'; +// import { AppComponent as S0208 } from '../02-08/app'; +// import { AppComponent as S0301 } from '../03-01/app'; +// import { AppComponent as S0302 } from '../03-02/app'; +// import { AppComponent as S0303 } from '../03-03/app'; +// import { AppComponent as S0304 } from '../03-04/app'; +// import { AppComponent as S0305 } from '../03-05/app'; +// import { AppComponent as S0306 } from '../03-06/app'; +// import { AppComponent as S0410 } from '../04-10/app'; +// import { AppComponent as S0414 } from '../04-14/app'; +// import { AppComponent as S0502 } from '../05-02/app'; +// import { AppComponent as S0503 } from '../05-03/app'; +// import { AppComponent as S0504 } from '../05-04/app'; +// import { AppComponent as S0512 } from '../05-12/app'; +// import { AppComponent as S0513 } from '../05-13/app'; +// import { AppComponent as S0514 } from '../05-14/app'; +// import { AppComponent as S0515 } from '../05-15/app'; +// import { AppComponent as S0516 } from '../05-16/app'; +// import { AppComponent as S0517 } from '../05-17/app'; +// import { AppComponent as S0601 } from '../06-01/app'; +// import { AppComponent as S0603 } from '../06-03/app'; +// import { AppComponent as S0701 } from '../07-01/app'; +// import { AppComponent as S0703 } from '../07-03/app'; +// import { AppComponent as S0704 } from '../07-04/app'; +// import { AppComponent as S0901 } from '../09-01/app'; -const routes: RouterConfig = [ +export const routes: RouterConfig = [ { path: '', redirectTo: '/01-01', pathMatch: 'full' }, { path: '01-01', component: S0101 }, - { path: '02-07', component: S0207 }, - { path: '02-08', component: S0208 }, - { path: '03-01', component: S0301 }, - { path: '03-02', component: S0302 }, - { path: '03-03', component: S0303 }, - { path: '03-04', component: S0304 }, - { path: '03-05', component: S0305 }, - { path: '03-06', component: S0306 }, - { path: '04-10', component: S0410 }, - { path: '04-14', component: S0414 }, - { path: '05-02', component: S0502 }, - { path: '05-03', component: S0503 }, - { path: '05-04', component: S0504 }, - { path: '05-12', component: S0512 }, - { path: '05-13', component: S0513 }, - { path: '05-14', component: S0514 }, - { path: '05-15', component: S0515 }, - { path: '05-16', component: S0516 }, - { path: '05-17', component: S0517 }, - { path: '06-01', component: S0601 }, - { path: '06-03', component: S0603 }, - { path: '07-01', component: S0701 }, - { path: '07-03', component: S0703 }, - { path: '07-04', component: S0704 }, - { path: '09-01', component: S0901 }, + // { path: '02-07', component: S0207 }, + // { path: '02-08', component: S0208 }, + // { path: '03-01', component: S0301 }, + // { path: '03-02', component: S0302 }, + // { path: '03-03', component: S0303 }, + // { path: '03-04', component: S0304 }, + // { path: '03-05', component: S0305 }, + // { path: '03-06', component: S0306 }, + ///////////////////{ path: '04-10', component: S0410 }, + // { path: '04-14', component: S0414 }, + // { path: '05-02', component: S0502 }, + // { path: '05-03', component: S0503 }, + // { path: '05-04', component: S0504 }, + // { path: '05-12', component: S0512 }, + // { path: '05-13', component: S0513 }, + // { path: '05-14', component: S0514 }, + // { path: '05-15', component: S0515 }, + // { path: '05-16', component: S0516 }, + // { path: '05-17', component: S0517 }, + // { path: '06-01', component: S0601 }, + // { path: '06-03', component: S0603 }, + // { path: '07-01', component: S0701 }, + // { path: '07-03', component: S0703 }, + // { path: '07-04', component: S0704 }, + // { path: '09-01', component: S0901 }, ]; -export const appRouterProviders = [ - provideRouter(routes) -]; diff --git a/public/docs/_examples/style-guide/ts/app/main.ts b/public/docs/_examples/style-guide/ts/app/main.ts index 759f9e261b..77e1c29934 100644 --- a/public/docs/_examples/style-guide/ts/app/main.ts +++ b/public/docs/_examples/style-guide/ts/app/main.ts @@ -1,17 +1,98 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { XHRBackend, HTTP_PROVIDERS } from '@angular/http'; -import { HashLocationStrategy, LocationStrategy } from '@angular/common'; -import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; +import { NgModule } from '@angular/core'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; +import { BrowserModule } from '@angular/platform-browser'; + +import { HttpModule, + XHRBackend } from '@angular/http'; +import { RouterModule } from '@angular/router'; + +import { HashLocationStrategy, + LocationStrategy } from '@angular/common'; + +import { InMemoryBackendService, + SEED_DATA } from 'angular2-in-memory-web-api'; + import 'rxjs/add/operator/map'; -import { appRouterProviders } from './app.routes'; -import { HeroData } from './hero-data'; -import { AppComponent } from './app.component'; +import { HeroData } from './hero-data'; +import { AppComponent } from './app.component'; + +import * as s0101 from '../01-01/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 s0305 from '../03-05/app/app.module'; +import * as s0306 from '../03-06/app/app.module'; +import * as s0410 from '../04-10/app/app.module'; +import * as s0414 from '../04-14/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, + + s0101.AppModule, + s0207.AppModule, + s0208.AppModule, + s0301.AppModule, + s0302.AppModule, + s0303.AppModule, + s0304.AppModule, + s0305.AppModule, + s0306.AppModule, + s0410.AppModule, + s0414.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 */}), + ], + declarations: [AppComponent], + providers: [ + { provide: LocationStrategy, useClass: HashLocationStrategy }, + { provide: XHRBackend, useClass: InMemoryBackendService }, + { provide: SEED_DATA, useClass: HeroData } + ], + bootstrap: [ AppComponent ] +}; + +@NgModule(moduleMetadata) +class MainModule { } + +browserDynamicPlatform().bootstrapModule(MainModule); + -bootstrap(AppComponent, [ - appRouterProviders, - HTTP_PROVIDERS, - { provide: LocationStrategy, useClass: HashLocationStrategy }, - { provide: XHRBackend, useClass: InMemoryBackendService }, - { provide: SEED_DATA, useClass: HeroData } - ]); diff --git a/public/docs/_examples/style-guide/ts/systemjs.custom.js b/public/docs/_examples/style-guide/ts/systemjs.custom.js index 10f13fe801..1ffb1b863a 100644 --- a/public/docs/_examples/style-guide/ts/systemjs.custom.js +++ b/public/docs/_examples/style-guide/ts/systemjs.custom.js @@ -23,7 +23,8 @@ '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-list', '05-17/app/heroes/shared', + '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', diff --git a/public/docs/_examples/styleguide/js/app.js b/public/docs/_examples/styleguide/js/app.js index 51a17a2429..61a0f2ae31 100644 --- a/public/docs/_examples/styleguide/js/app.js +++ b/public/docs/_examples/styleguide/js/app.js @@ -19,8 +19,20 @@ app.AppComponent = // #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.bootstrap(app.AppComponent); + ng.platformBrowserDynamic + .platformBrowserDynamic() + .bootstrapModule(app.AppModule); }); // #enddocregion // #enddocregion diff --git a/public/docs/_examples/styleguide/ts/app/app.module.ts b/public/docs/_examples/styleguide/ts/app/app.module.ts new file mode 100644 index 0000000000..0a9ee6adf7 --- /dev/null +++ b/public/docs/_examples/styleguide/ts/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/app/main.ts b/public/docs/_examples/styleguide/ts/app/main.ts index ad256f0823..4acf5de663 100644 --- a/public/docs/_examples/styleguide/ts/app/main.ts +++ b/public/docs/_examples/styleguide/ts/app/main.ts @@ -1,5 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/systemjs.config.js b/public/docs/_examples/systemjs.config.js index 22b0c210cc..1921a431c0 100644 --- a/public/docs/_examples/systemjs.config.js +++ b/public/docs/_examples/systemjs.config.js @@ -49,9 +49,6 @@ // Add package entries for angular packages ngPackageNames.forEach(setPackageConfig); - // No umd for router yet - packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' }; - var config = { map: map, packages: packages diff --git a/public/docs/_examples/systemjs.config.plunker.build.js b/public/docs/_examples/systemjs.config.plunker.build.js new file mode 100644 index 0000000000..3ccf77808c --- /dev/null +++ b/public/docs/_examples/systemjs.config.plunker.build.js @@ -0,0 +1,105 @@ +/** + * PLUNKER CURRENT BUILD VERSION + * (based on systemjs.config.js in angular.io) + * System configuration for Angular 2 samples + * Adjust as necessary for your application needs. + */ +(function(global) { + + var ngVer = '@2.0.0-rc.5'; // lock in the angular package version; do not let it float to current! + var routerVer = '@3.0.0-rc.1'; // lock router version + var formsVer = '@0.3.0'; // lock forms version + var routerDeprecatedVer = '@2.0.0-rc.2'; // temporarily until we update all the guides + + //map tells the System loader where to look for things + var map = { + 'app': 'app', + + + '@angular/core': 'https://cdn.rawgit.com/angular/core-builds/master', + '@angular/common': 'https://cdn.rawgit.com/angular/common-builds/master', + '@angular/compiler':'https://cdn.rawgit.com/angular/compiler-builds/master', + '@angular/forms':'https://cdn.rawgit.com/angular/forms-builds/master', + '@angular/http':'https://cdn.rawgit.com/angular/http-builds/master', + '@angular/platform-browser':'https://cdn.rawgit.com/angular/platform-browser-builds/master', + '@angular/platform-browser-dynamic': 'https://cdn.rawgit.com/angular/platform-browser-dynamic-builds/master', + '@angular/router': 'https://cdn.rawgit.com/angular/router-builds/master', + + 'rxjs': 'https://npmcdn.com/rxjs@5.0.0-beta.6', + 'ts': 'https://npmcdn.com/plugin-typescript@4.0.10/lib/plugin.js', + 'typescript': 'https://npmcdn.com/typescript@1.9.0-dev.20160409/lib/typescript.js', + + 'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', + }; + + //packages tells the System loader how to load when no filename and/or no extension + var packages = { + app: { + main: 'main.ts', + defaultExtension: 'ts' + }, + '@angular/core': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/compiler': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/common': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/forms': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/http': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/platform-browser-dynamic': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/platform-browser': { + main: 'index.js', + defaultExtension: 'js' + }, + '@angular/router': { + main: 'index.js', + defaultExtension: 'js' + }, + 'angular2-in-memory-web-api': { + main: 'index.js', + defaultExtension: 'js' + }, + rxjs: { + defaultExtension: 'js' + } + } + var config = { + // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER + transpiler: 'ts', + typescriptOptions: { + tsconfig: true + }, + meta: { + 'typescript': { + "exports": "ts" + } + }, + map: map, + packages: packages + }; + + System.config(config); + +})(this); + + +/* +Copyright 2016 Google Inc. All Rights Reserved. +Use of this source code is governed by an MIT-style license that +can be found in the LICENSE file at http://angular.io/license +*/ diff --git a/public/docs/_examples/systemjs.config.plunker.js b/public/docs/_examples/systemjs.config.plunker.js index 5ff363b229..d55c4db274 100644 --- a/public/docs/_examples/systemjs.config.plunker.js +++ b/public/docs/_examples/systemjs.config.plunker.js @@ -5,13 +5,13 @@ */ (function(global) { - var ngVer = '@2.0.0-rc.4'; // lock in the angular package version; do not let it float to current! - var routerVer = '@3.0.0-beta.2'; // lock router version - var formsVer = '@0.2.0'; // lock forms version + var ngVer = '@2.0.0-rc.5'; // lock in the angular package version; do not let it float to current! + var routerVer = '@3.0.0-rc.1'; // lock router version + var formsVer = '@0.3.0'; // lock forms version var routerDeprecatedVer = '@2.0.0-rc.2'; // temporarily until we update all the guides //map tells the System loader where to look for things - var map = { + var map = { 'app': 'app', '@angular': 'https://npmcdn.com/@angular', // sufficient if we didn't pin the version @@ -48,7 +48,7 @@ }); // Add package entries for angular packages - ngPackageNames.forEach(function(pkgName) { + ngPackageNames.concat(['forms', 'router', 'router-deprecated']).forEach(function(pkgName) { // Bundled (~40 requests): packages['@angular/'+pkgName] = { main: '/bundles/' + pkgName + '.umd.js', defaultExtension: 'js' }; @@ -57,15 +57,6 @@ //packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' }; }); - // No umd for router yet - packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' }; - - // Forms not on rc yet - packages['@angular/forms'] = { main: 'index.js', defaultExtension: 'js' }; - - // Temporarily until we update the guides - packages['@angular/router-deprecated'] = { main: '/bundles/router-deprecated' + '.umd.js', defaultExtension: 'js' }; - var config = { // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER transpiler: 'ts', diff --git a/public/docs/_examples/template-syntax/ts/app/app.component.html b/public/docs/_examples/template-syntax/ts/app/app.component.html index ebd9981e1a..50c07a1fc8 100644 --- a/public/docs/_examples/template-syntax/ts/app/app.component.html +++ b/public/docs/_examples/template-syntax/ts/app/app.component.html @@ -664,8 +664,7 @@ bindon-ngModel
    - +
    diff --git a/public/docs/_examples/template-syntax/ts/app/app.component.ts b/public/docs/_examples/template-syntax/ts/app/app.component.ts index 087feccdb6..9f64d3c98f 100644 --- a/public/docs/_examples/template-syntax/ts/app/app.component.ts +++ b/public/docs/_examples/template-syntax/ts/app/app.component.ts @@ -2,11 +2,9 @@ // #docplaster import { AfterViewInit, Component, ElementRef, OnInit, QueryList, ViewChildren } from '@angular/core'; -import { NgForm } from '@angular/common'; +import { NgForm } from '@angular/forms'; import { Hero } from './hero'; -import { HeroDetailComponent, BigHeroDetailComponent } from './hero-detail.component'; -import { MyClickDirective, MyClickDirective2 } from './my-click.directive'; // Alerter fn: monkey patch during test export function alerter(msg?: string) { @@ -20,11 +18,7 @@ export enum Color {Red, Green, Blue}; */ @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: [ - HeroDetailComponent, BigHeroDetailComponent, - MyClickDirective, MyClickDirective2 - ] + templateUrl: 'app/app.component.html' }) export class AppComponent implements AfterViewInit, OnInit { diff --git a/public/docs/_examples/template-syntax/ts/app/app.module.1.ts b/public/docs/_examples/template-syntax/ts/app/app.module.1.ts new file mode 100644 index 0000000000..47007ecae4 --- /dev/null +++ b/public/docs/_examples/template-syntax/ts/app/app.module.1.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/template-syntax/ts/app/app.module.ts b/public/docs/_examples/template-syntax/ts/app/app.module.ts new file mode 100644 index 0000000000..2a99d8b395 --- /dev/null +++ b/public/docs/_examples/template-syntax/ts/app/app.module.ts @@ -0,0 +1,23 @@ +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 { MyClickDirective, MyClickDirective2 } from './my-click.directive'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + declarations: [ + AppComponent, + BigHeroDetailComponent, + HeroDetailComponent, + MyClickDirective, + MyClickDirective2 + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/template-syntax/ts/app/main.ts b/public/docs/_examples/template-syntax/ts/app/main.ts index 42dbeb9f7d..458fd21eb1 100644 --- a/public/docs/_examples/template-syntax/ts/app/main.ts +++ b/public/docs/_examples/template-syntax/ts/app/main.ts @@ -1,5 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; +import { AppModule } from './app.module'; -bootstrap(AppComponent); +browserDynamicPlatform().bootstrapModule(AppModule); diff --git a/public/docs/_examples/toh-1/ts/app/app.module.ts b/public/docs/_examples/toh-1/ts/app/app.module.ts new file mode 100644 index 0000000000..4c0b77ea48 --- /dev/null +++ b/public/docs/_examples/toh-1/ts/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-1/ts/app/main.ts b/public/docs/_examples/toh-1/ts/app/main.ts index dae4ddf676..62da4340b8 100644 --- a/public/docs/_examples/toh-1/ts/app/main.ts +++ b/public/docs/_examples/toh-1/ts/app/main.ts @@ -1,7 +1,6 @@ -// #docregion pt1 -import { bootstrap } from '@angular/platform-browser-dynamic'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); -// #enddocregion pt1 +platformBrowserDynamic().bootstrapModule(AppModule); +// #enddocregion diff --git a/public/docs/_examples/toh-2/ts/app/app.module.ts b/public/docs/_examples/toh-2/ts/app/app.module.ts new file mode 100644 index 0000000000..4c0b77ea48 --- /dev/null +++ b/public/docs/_examples/toh-2/ts/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/app/main.ts b/public/docs/_examples/toh-2/ts/app/main.ts index 42dbeb9f7d..62da4340b8 100644 --- a/public/docs/_examples/toh-2/ts/app/main.ts +++ b/public/docs/_examples/toh-2/ts/app/main.ts @@ -1,5 +1,6 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +// #docregion +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); +// #enddocregion diff --git a/public/docs/_examples/toh-3/ts/app/app.component.ts b/public/docs/_examples/toh-3/ts/app/app.component.ts index bbe047a629..fe5a5b98ad 100644 --- a/public/docs/_examples/toh-3/ts/app/app.component.ts +++ b/public/docs/_examples/toh-3/ts/app/app.component.ts @@ -4,9 +4,6 @@ import { Component } from '@angular/core'; // #docregion hero-import import { Hero } from './hero'; // #enddocregion hero-import -// #docregion hero-detail-import -import { HeroDetailComponent } from './hero-detail.component'; -// #enddocregion hero-detail-import const HEROES: Hero[] = [ { id: 11, name: 'Mr. Nice' }, @@ -85,10 +82,7 @@ const HEROES: Hero[] = [ margin-right: .8em; border-radius: 4px 0 0 4px; } - `], -// #docregion directives - directives: [HeroDetailComponent] -// #enddocregion directives + `] }) export class AppComponent { title = 'Tour of Heroes'; diff --git a/public/docs/_examples/toh-3/ts/app/app.module.ts b/public/docs/_examples/toh-3/ts/app/app.module.ts new file mode 100644 index 0000000000..26b63938ad --- /dev/null +++ b/public/docs/_examples/toh-3/ts/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 + +// #docregion declarations +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + declarations: [ + AppComponent, + HeroDetailComponent + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } +// #enddocregion declarations diff --git a/public/docs/_examples/toh-3/ts/app/main.ts b/public/docs/_examples/toh-3/ts/app/main.ts index dae4ddf676..e6bbb4c282 100644 --- a/public/docs/_examples/toh-3/ts/app/main.ts +++ b/public/docs/_examples/toh-3/ts/app/main.ts @@ -1,7 +1,6 @@ // #docregion pt1 -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); // #enddocregion pt1 diff --git a/public/docs/_examples/toh-4/ts/app/app.component.1.ts b/public/docs/_examples/toh-4/ts/app/app.component.1.ts index 9df27d44bd..5a0a4d0524 100644 --- a/public/docs/_examples/toh-4/ts/app/app.component.1.ts +++ b/public/docs/_examples/toh-4/ts/app/app.component.1.ts @@ -6,7 +6,6 @@ import { OnInit } from '@angular/core'; import { Component } from '@angular/core'; import { Hero } from './hero'; -import { HeroDetailComponent } from './hero-detail.component'; // #docregion hero-service-import import { HeroService } from './hero.service.1'; // #enddocregion hero-service-import @@ -20,7 +19,6 @@ import { HeroService } from './hero.service.1'; `, - directives: [HeroDetailComponent], // #docregion providers providers: [HeroService] // #enddocregion providers diff --git a/public/docs/_examples/toh-4/ts/app/app.component.ts b/public/docs/_examples/toh-4/ts/app/app.component.ts index cf2b42a034..00bee85659 100644 --- a/public/docs/_examples/toh-4/ts/app/app.component.ts +++ b/public/docs/_examples/toh-4/ts/app/app.component.ts @@ -3,7 +3,6 @@ import { Component, OnInit } from '@angular/core'; import { Hero } from './hero'; -import { HeroDetailComponent } from './hero-detail.component'; // #docregion hero-service-import import { HeroService } from './hero.service'; // #enddocregion hero-service-import @@ -73,7 +72,6 @@ import { HeroService } from './hero.service'; border-radius: 4px 0 0 4px; } `], - directives: [HeroDetailComponent], providers: [HeroService] }) export class AppComponent implements OnInit { diff --git a/public/docs/_examples/toh-4/ts/app/app.module.ts b/public/docs/_examples/toh-4/ts/app/app.module.ts new file mode 100644 index 0000000000..3df186c62a --- /dev/null +++ b/public/docs/_examples/toh-4/ts/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/app/main.1.ts b/public/docs/_examples/toh-4/ts/app/main.1.ts index f8cf0497d6..2470c9595e 100644 --- a/public/docs/_examples/toh-4/ts/app/main.1.ts +++ b/public/docs/_examples/toh-4/ts/app/main.1.ts @@ -1,5 +1,4 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component.1'; - -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/toh-4/ts/app/main.ts b/public/docs/_examples/toh-4/ts/app/main.ts index 42dbeb9f7d..2470c9595e 100644 --- a/public/docs/_examples/toh-4/ts/app/main.ts +++ b/public/docs/_examples/toh-4/ts/app/main.ts @@ -1,5 +1,4 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; - -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/toh-5/ts/app/app.component.1.ts b/public/docs/_examples/toh-5/ts/app/app.component.1.ts index 789e14d9be..cbdd040437 100644 --- a/public/docs/_examples/toh-5/ts/app/app.component.1.ts +++ b/public/docs/_examples/toh-5/ts/app/app.component.1.ts @@ -1,24 +1,13 @@ // #docplaster // #docregion -import { Component } from '@angular/core'; +import { Component } from '@angular/core'; -import { HeroService } from './hero.service'; -import { HeroesComponent } from './heroes.component'; -// #enddocregion - -// #docregion @Component({ selector: 'my-app', template: `

    {{title}}

    - `, - directives: [HeroesComponent], - providers: [ - // #enddocregion - // #docregion - HeroService - ] + ` }) export class AppComponent { title = 'Tour of Heroes'; diff --git a/public/docs/_examples/toh-5/ts/app/app.component.2.ts b/public/docs/_examples/toh-5/ts/app/app.component.2.ts index c2699f317b..82e5147775 100644 --- a/public/docs/_examples/toh-5/ts/app/app.component.2.ts +++ b/public/docs/_examples/toh-5/ts/app/app.component.2.ts @@ -1,27 +1,16 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -// #docregion import-router -import { ROUTER_DIRECTIVES } from '@angular/router'; -// #enddocregion import-router - -import { HeroService } from './hero.service'; @Component({ selector: 'my-app', // #docregion template template: `

    {{title}}

    - Heroes + Heroes - `, + ` // #enddocregion template - // #docregion directives-and-providers - directives: [ROUTER_DIRECTIVES], - providers: [ - HeroService - ] - // #enddocregion directives-and-providers }) export class AppComponent { title = 'Tour of Heroes'; diff --git a/public/docs/_examples/toh-5/ts/app/app.component.3.ts b/public/docs/_examples/toh-5/ts/app/app.component.3.ts index ff64e27ab1..2d1f83853c 100644 --- a/public/docs/_examples/toh-5/ts/app/app.component.3.ts +++ b/public/docs/_examples/toh-5/ts/app/app.component.3.ts @@ -1,9 +1,6 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -import { HeroService } from './hero.service'; @Component({ selector: 'my-app', @@ -11,10 +8,8 @@ import { HeroService } from './hero.service'; template: `

    {{title}}

    `, @@ -22,10 +17,6 @@ import { HeroService } from './hero.service'; // #docregion style-urls styleUrls: ['app/app.component.css'], // #enddocregion style-urls - directives: [ROUTER_DIRECTIVES], - providers: [ - HeroService - ] }) export class AppComponent { title = 'Tour of Heroes'; diff --git a/public/docs/_examples/toh-5/ts/app/app.component.ts b/public/docs/_examples/toh-5/ts/app/app.component.ts index 3f3e758dcf..fbf2279067 100644 --- a/public/docs/_examples/toh-5/ts/app/app.component.ts +++ b/public/docs/_examples/toh-5/ts/app/app.component.ts @@ -1,9 +1,6 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; - -import { HeroService } from './hero.service'; @Component({ selector: 'my-app', @@ -11,8 +8,8 @@ import { HeroService } from './hero.service'; template: `

    {{title}}

    `, @@ -20,10 +17,6 @@ import { HeroService } from './hero.service'; // #docregion style-urls styleUrls: ['app/app.component.css'], // #enddocregion style-urls - directives: [ROUTER_DIRECTIVES], - providers: [ - HeroService - ] }) export class AppComponent { title = 'Tour of Heroes'; diff --git a/public/docs/_examples/toh-5/ts/app/app.module.1.ts b/public/docs/_examples/toh-5/ts/app/app.module.1.ts new file mode 100644 index 0000000000..473ca08fb8 --- /dev/null +++ b/public/docs/_examples/toh-5/ts/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 { HeroesComponent } from './heroes.component'; + +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule + ], + declarations: [ + AppComponent, + HeroesComponent + ], + providers: [ + HeroService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/toh-5/ts/app/app.module.2.ts b/public/docs/_examples/toh-5/ts/app/app.module.2.ts new file mode 100644 index 0000000000..1a4ac0f48d --- /dev/null +++ b/public/docs/_examples/toh-5/ts/app/app.module.2.ts @@ -0,0 +1,30 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { routing } from './app.routing'; + +import { HeroesComponent } from './heroes.component'; + +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing + ], + declarations: [ + AppComponent, + HeroesComponent + ], + providers: [ + HeroService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/toh-5/ts/app/app.module.3.ts b/public/docs/_examples/toh-5/ts/app/app.module.3.ts new file mode 100644 index 0000000000..2eca272b26 --- /dev/null +++ b/public/docs/_examples/toh-5/ts/app/app.module.3.ts @@ -0,0 +1,47 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { routing } from './app.routing'; + +import { HeroesComponent } from './heroes.component'; +// #docregion dashboard-declaration +import { DashboardComponent } from './dashboard.component'; +// #enddocregion dashboard-declaration +// #docregion hero-detail-declaration +import { HeroDetailComponent } from './hero-detail.component'; +// #enddocregion hero-detail-declaration +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing + ], +// #docregion dashboard-declaration, hero-detail-declaration + + declarations: [ +// #enddocregion dashboard-declaration, hero-detail-declaration + AppComponent, + HeroesComponent, +// #docregion dashboard-declaration + DashboardComponent, +// #enddocregion dashboard-declaration +// #docregion hero-detail-declaration + HeroDetailComponent +// #enddocregion hero-detail-declaration +// #docregion dashboard-declaration, hero-detail-declaration + ], +// #enddocregion dashboard-declaration, hero-detail-declaration + providers: [ + HeroService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/toh-5/ts/app/app.module.ts b/public/docs/_examples/toh-5/ts/app/app.module.ts new file mode 100644 index 0000000000..47cc9415bb --- /dev/null +++ b/public/docs/_examples/toh-5/ts/app/app.module.ts @@ -0,0 +1,34 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +import { AppComponent } from './app.component'; +import { routing } from './app.routing'; + +import { HeroesComponent } from './heroes.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroDetailComponent } from './hero-detail.component'; + +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing + ], + declarations: [ + AppComponent, + HeroesComponent, + DashboardComponent, + HeroDetailComponent + ], + providers: [ + HeroService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/toh-5/ts/app/app.routes.2.ts b/public/docs/_examples/toh-5/ts/app/app.routes.2.ts deleted file mode 100644 index 075cc37496..0000000000 --- a/public/docs/_examples/toh-5/ts/app/app.routes.2.ts +++ /dev/null @@ -1,14 +0,0 @@ -// #docregion -import { provideRouter, RouterConfig } from '@angular/router'; -import { HeroesComponent } from './heroes.component'; - -const routes: RouterConfig = [ - { - path: 'heroes', - component: HeroesComponent - } -]; - -export const appRouterProviders = [ - provideRouter(routes) -]; diff --git a/public/docs/_examples/toh-5/ts/app/app.routing.1.ts b/public/docs/_examples/toh-5/ts/app/app.routing.1.ts new file mode 100644 index 0000000000..055b26754e --- /dev/null +++ b/public/docs/_examples/toh-5/ts/app/app.routing.1.ts @@ -0,0 +1,17 @@ +// #docregion +// #docregion routing-config +import { Routes, RouterModule } from '@angular/router'; + +import { HeroesComponent } from './heroes.component'; + +const appRoutes: Routes = [ + { + path: 'heroes', + component: HeroesComponent + } +]; +// #enddocregion routing-config + +// #docregion routing-export +export const routing = RouterModule.forRoot(appRoutes); +// #enddocregion routing-export diff --git a/public/docs/_examples/toh-5/ts/app/app.routes.1.ts b/public/docs/_examples/toh-5/ts/app/app.routing.2.ts similarity index 76% rename from public/docs/_examples/toh-5/ts/app/app.routes.1.ts rename to public/docs/_examples/toh-5/ts/app/app.routing.2.ts index 0c34add29c..a75df506e7 100644 --- a/public/docs/_examples/toh-5/ts/app/app.routes.1.ts +++ b/public/docs/_examples/toh-5/ts/app/app.routing.2.ts @@ -1,12 +1,13 @@ // #docregion -import { provideRouter, RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; + import { DashboardComponent } from './dashboard.component'; -import { HeroesComponent } from './heroes.component'; +import { HeroesComponent } from './heroes.component'; // #docregion hero-detail-import import { HeroDetailComponent } from './hero-detail.component'; // #enddocregion hero-detail-import -const routes: RouterConfig = [ +const appRoutes: Routes = [ // #docregion redirect-route { path: '', @@ -32,6 +33,4 @@ const routes: RouterConfig = [ } ]; -export const appRouterProviders = [ - provideRouter(routes) -]; +export const routing = RouterModule.forRoot(appRoutes); diff --git a/public/docs/_examples/toh-5/ts/app/app.routes.ts b/public/docs/_examples/toh-5/ts/app/app.routing.ts similarity index 60% rename from public/docs/_examples/toh-5/ts/app/app.routes.ts rename to public/docs/_examples/toh-5/ts/app/app.routing.ts index c6074c3607..62071e2567 100644 --- a/public/docs/_examples/toh-5/ts/app/app.routes.ts +++ b/public/docs/_examples/toh-5/ts/app/app.routing.ts @@ -1,13 +1,13 @@ // #docregion -import { provideRouter, RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; -import { DashboardComponent } from './dashboard.component'; -import { HeroesComponent } from './heroes.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroesComponent } from './heroes.component'; // #docregion hero-detail-import import { HeroDetailComponent } from './hero-detail.component'; // #enddocregion hero-detail-import -const routes: RouterConfig = [ +const appRoutes: Routes = [ { path: '', redirectTo: '/dashboard', @@ -27,6 +27,4 @@ const routes: RouterConfig = [ } ]; -export const appRouterProviders = [ - provideRouter(routes) -]; +export const routing = RouterModule.forRoot(appRoutes); diff --git a/public/docs/_examples/toh-5/ts/app/hero-detail.component.ts b/public/docs/_examples/toh-5/ts/app/hero-detail.component.ts index 61edccb90e..713d27c9f2 100644 --- a/public/docs/_examples/toh-5/ts/app/hero-detail.component.ts +++ b/public/docs/_examples/toh-5/ts/app/hero-detail.component.ts @@ -1,10 +1,10 @@ // #docplaster // #docregion // #docregion import-oninit, v2 -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, OnInit } from '@angular/core'; // #enddocregion import-oninit // #docregion import-activated-route -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Params } from '@angular/router'; // #enddocregion import-activated-route import { Hero } from './hero'; @@ -23,10 +23,9 @@ import { HeroService } from './hero.service'; }) // #enddocregion extract-template // #docregion implement -export class HeroDetailComponent implements OnInit, OnDestroy { +export class HeroDetailComponent implements OnInit { // #enddocregion implement hero: Hero; - sub: any; // #docregion ctor constructor( @@ -38,7 +37,7 @@ export class HeroDetailComponent implements OnInit, OnDestroy { // #docregion ng-oninit ngOnInit() { // #docregion get-id - this.sub = this.route.params.subscribe(params => { + this.route.params.forEach((params: Params) => { let id = +params['id']; this.heroService.getHero(id) .then(hero => this.hero = hero); @@ -47,12 +46,6 @@ export class HeroDetailComponent implements OnInit, OnDestroy { } // #enddocregion ng-oninit - // #docregion ng-ondestroy - ngOnDestroy() { - this.sub.unsubscribe(); - } - // #enddocregion ng-ondestroy - // #docregion go-back goBack() { window.history.back(); diff --git a/public/docs/_examples/toh-5/ts/app/main.ts b/public/docs/_examples/toh-5/ts/app/main.ts index f04d528627..091a7d82a7 100644 --- a/public/docs/_examples/toh-5/ts/app/main.ts +++ b/public/docs/_examples/toh-5/ts/app/main.ts @@ -1,9 +1,6 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +// main entry point +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { AppComponent } from './app.component'; -import { appRouterProviders } from './app.routes'; - -bootstrap(AppComponent, [ - appRouterProviders -]); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/toh-6/ts/app/app.component.ts b/public/docs/_examples/toh-6/ts/app/app.component.ts index d49c87ccbf..16d6396184 100644 --- a/public/docs/_examples/toh-6/ts/app/app.component.ts +++ b/public/docs/_examples/toh-6/ts/app/app.component.ts @@ -1,9 +1,7 @@ // #docplaster // #docregion import { Component } from '@angular/core'; -import { ROUTER_DIRECTIVES } from '@angular/router'; -import { HeroService } from './hero.service'; // #docregion rxjs-extensions import './rxjs-extensions'; // #enddocregion rxjs-extensions @@ -14,16 +12,12 @@ import './rxjs-extensions'; template: `

    {{title}}

    `, - styleUrls: ['app/app.component.css'], - directives: [ROUTER_DIRECTIVES], - providers: [ - HeroService, - ] + styleUrls: ['app/app.component.css'] }) export class AppComponent { title = 'Tour of Heroes'; diff --git a/public/docs/_examples/toh-6/ts/app/app.module.1.ts b/public/docs/_examples/toh-6/ts/app/app.module.1.ts new file mode 100644 index 0000000000..6924d5a390 --- /dev/null +++ b/public/docs/_examples/toh-6/ts/app/app.module.1.ts @@ -0,0 +1,44 @@ +// #docplaster +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +// Imports for loading & configuring the in-memory web api +import { HttpModule, XHRBackend } from '@angular/http'; + +import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; + +import { AppComponent } from './app.component'; +import { routing } from './app.routing'; + +import { HeroesComponent } from './heroes.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroDetailComponent } from './hero-detail.component'; + +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing, + HttpModule + ], + declarations: [ + AppComponent, + HeroesComponent, + DashboardComponent, + HeroDetailComponent + ], + providers: [ + HeroService, + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion final diff --git a/public/docs/_examples/toh-6/ts/app/app.module.2.ts b/public/docs/_examples/toh-6/ts/app/app.module.2.ts new file mode 100644 index 0000000000..2ec598cd0b --- /dev/null +++ b/public/docs/_examples/toh-6/ts/app/app.module.2.ts @@ -0,0 +1,44 @@ +// #docplaster +// #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 { routing } from './app.routing'; + +import { HeroesComponent } from './heroes.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroDetailComponent } from './hero-detail.component'; +// #docregion hero-search-declaration +import { HeroSearchComponent } from './hero-search.component'; +// #enddocregion hero-search-declaration + +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing, + HttpModule + ], +// #docregion hero-search-declaration + + declarations: [ + AppComponent, + HeroesComponent, + DashboardComponent, + HeroDetailComponent, + HeroSearchComponent + ], +// #enddocregion hero-search-declaration + providers: [ + HeroService + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/toh-6/ts/app/app.module.ts b/public/docs/_examples/toh-6/ts/app/app.module.ts new file mode 100644 index 0000000000..0beff178e0 --- /dev/null +++ b/public/docs/_examples/toh-6/ts/app/app.module.ts @@ -0,0 +1,45 @@ +// #docregion +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { FormsModule } from '@angular/forms'; + +// Imports for loading & configuring the in-memory web api +import { HttpModule, XHRBackend } from '@angular/http'; + +import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; +import { InMemoryDataService } from './in-memory-data.service'; + +import { AppComponent } from './app.component'; +import { routing } from './app.routing'; + +import { HeroesComponent } from './heroes.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroDetailComponent } from './hero-detail.component'; +import { HeroSearchComponent } from './hero-search.component'; + +import { HeroService } from './hero.service'; + +@NgModule({ + imports: [ + BrowserModule, + FormsModule, + routing, + HttpModule + ], + declarations: [ + AppComponent, + HeroesComponent, + DashboardComponent, + HeroDetailComponent, + HeroSearchComponent + ], + providers: [ + HeroService, + { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server + { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { +} +// #enddocregion diff --git a/public/docs/_examples/toh-6/ts/app/app.routes.ts b/public/docs/_examples/toh-6/ts/app/app.routing.ts similarity index 56% rename from public/docs/_examples/toh-6/ts/app/app.routes.ts rename to public/docs/_examples/toh-6/ts/app/app.routing.ts index e143b2a3bf..c5753a4ee9 100644 --- a/public/docs/_examples/toh-6/ts/app/app.routes.ts +++ b/public/docs/_examples/toh-6/ts/app/app.routing.ts @@ -1,11 +1,11 @@ // #docregion -import { provideRouter, RouterConfig } from '@angular/router'; +import { Routes, RouterModule } from '@angular/router'; -import { DashboardComponent } from './dashboard.component'; -import { HeroesComponent } from './heroes.component'; +import { DashboardComponent } from './dashboard.component'; +import { HeroesComponent } from './heroes.component'; import { HeroDetailComponent } from './hero-detail.component'; -const routes: RouterConfig = [ +const appRoutes: Routes = [ { path: '', redirectTo: '/dashboard', @@ -25,6 +25,4 @@ const routes: RouterConfig = [ } ]; -export const appRouterProviders = [ - provideRouter(routes) -]; +export const routing = RouterModule.forRoot(appRoutes); diff --git a/public/docs/_examples/toh-6/ts/app/dashboard.component.ts b/public/docs/_examples/toh-6/ts/app/dashboard.component.ts index 41b4de58df..4a90a4d6bf 100644 --- a/public/docs/_examples/toh-6/ts/app/dashboard.component.ts +++ b/public/docs/_examples/toh-6/ts/app/dashboard.component.ts @@ -1,16 +1,14 @@ // #docregion , search import { Component, OnInit } from '@angular/core'; -import { Router } from '@angular/router'; +import { Router } from '@angular/router'; import { Hero } from './hero'; import { HeroService } from './hero.service'; -import { HeroSearchComponent } from './hero-search.component'; @Component({ selector: 'my-dashboard', templateUrl: 'app/dashboard.component.html', - styleUrls: ['app/dashboard.component.css'], - directives: [HeroSearchComponent] + styleUrls: ['app/dashboard.component.css'] }) // #enddocregion search export class DashboardComponent implements OnInit { diff --git a/public/docs/_examples/toh-6/ts/app/hero-detail.component.ts b/public/docs/_examples/toh-6/ts/app/hero-detail.component.ts index 85d722999e..062917401e 100644 --- a/public/docs/_examples/toh-6/ts/app/hero-detail.component.ts +++ b/public/docs/_examples/toh-6/ts/app/hero-detail.component.ts @@ -1,9 +1,9 @@ // #docplaster // #docregion, variables-imports -import { Component, EventEmitter, Input, OnInit, OnDestroy, Output } from '@angular/core'; +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; // #enddocregion variables-imports -import { ActivatedRoute } from '@angular/router'; +import { ActivatedRoute, Params } from '@angular/router'; import { Hero } from './hero'; import { HeroService } from './hero.service'; @@ -14,11 +14,10 @@ import { HeroService } from './hero.service'; styleUrls: ['app/hero-detail.component.css'] }) // #docregion variables-imports -export class HeroDetailComponent implements OnInit, OnDestroy { +export class HeroDetailComponent implements OnInit { @Input() hero: Hero; @Output() close = new EventEmitter(); error: any; - sub: any; navigated = false; // true if navigated here // #enddocregion variables-imports @@ -29,7 +28,7 @@ export class HeroDetailComponent implements OnInit, OnDestroy { // #docregion ngOnInit ngOnInit() { - this.sub = this.route.params.subscribe(params => { + this.route.params.forEach((params: Params) => { if (params['id'] !== undefined) { let id = +params['id']; this.navigated = true; @@ -43,10 +42,6 @@ export class HeroDetailComponent implements OnInit, OnDestroy { } // #enddocregion ngOnInit - ngOnDestroy() { - this.sub.unsubscribe(); - } - // #docregion save save() { this.heroService diff --git a/public/docs/_examples/toh-6/ts/app/heroes.component.ts b/public/docs/_examples/toh-6/ts/app/heroes.component.ts index 3bf618f5bd..e5c5ab1c49 100644 --- a/public/docs/_examples/toh-6/ts/app/heroes.component.ts +++ b/public/docs/_examples/toh-6/ts/app/heroes.component.ts @@ -5,13 +5,11 @@ import { Router } from '@angular/router'; import { Hero } from './hero'; import { HeroService } from './hero.service'; // #docregion hero-detail-component -import { HeroDetailComponent } from './hero-detail.component'; @Component({ selector: 'my-heroes', templateUrl: 'app/heroes.component.html', - styleUrls: ['app/heroes.component.css'], - directives: [HeroDetailComponent] + styleUrls: ['app/heroes.component.css'] }) // #enddocregion hero-detail-component export class HeroesComponent implements OnInit { diff --git a/public/docs/_examples/toh-6/ts/app/main.ts b/public/docs/_examples/toh-6/ts/app/main.ts index bda66d0377..091a7d82a7 100644 --- a/public/docs/_examples/toh-6/ts/app/main.ts +++ b/public/docs/_examples/toh-6/ts/app/main.ts @@ -1,33 +1,6 @@ -// #docplaster -// #docregion final -// Imports for loading & configuring the in-memory web api -import { XHRBackend } from '@angular/http'; +// #docregion +// main entry point +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; +import { AppModule } from './app.module'; -import { InMemoryBackendService, SEED_DATA } from 'angular2-in-memory-web-api'; -import { InMemoryDataService } from './in-memory-data.service'; - -// The usual bootstrapping imports -// #docregion v1 -import { bootstrap } from '@angular/platform-browser-dynamic'; -import { HTTP_PROVIDERS } from '@angular/http'; - -import { AppComponent } from './app.component'; -import { appRouterProviders } from './app.routes'; - -// #enddocregion v1, final -/* -// #docregion v1 -bootstrap(AppComponent, [ - appRouterProviders, - HTTP_PROVIDERS -]); -// #enddocregion v1 -*/ -// #docregion final -bootstrap(AppComponent, [ - appRouterProviders, - HTTP_PROVIDERS, - { provide: XHRBackend, useClass: InMemoryBackendService }, // in-mem server - { provide: SEED_DATA, useClass: InMemoryDataService } // in-mem server data -]); -// #enddocregion final +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/typings.json b/public/docs/_examples/typings.json index a4bf5572aa..3d826df25a 100644 --- a/public/docs/_examples/typings.json +++ b/public/docs/_examples/typings.json @@ -2,6 +2,6 @@ "globalDependencies": { "core-js": "registry:dt/core-js#0.0.0+20160602141332", "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", - "node": "registry:dt/node#6.0.0+20160720070758" + "node": "registry:dt/node#6.0.0+20160807145350" } } diff --git a/public/docs/_examples/upgrade-adapter/e2e-spec.ts b/public/docs/_examples/upgrade-adapter/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-adapter/e2e-spec.ts rename to public/docs/_examples/upgrade-adapter/e2e-spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/e2e-spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-2-hybrid/e2e-spec.ts rename to public/docs/_examples/upgrade-phonecat-2-hybrid/e2e-spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.spec.ts.disabled similarity index 92% rename from public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.spec.ts rename to public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.spec.ts.disabled index 5b9ad15f32..3881a30e4d 100644 --- a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.spec.ts +++ b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/checkmark/checkmark.pipe.spec.ts.disabled @@ -1,10 +1,7 @@ // #docregion import { - describe, beforeEachProviders, - it, - inject, - expect + inject } from '@angular/core/testing'; import { CheckmarkPipe } from './checkmark.pipe'; diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts rename to public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/core/phone/phone.service.spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts rename to public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-detail/phone-detail.component.spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts b/public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts rename to public/docs/_examples/upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.component.spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-3-final/e2e-spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-3-final/e2e-spec.ts rename to public/docs/_examples/upgrade-phonecat-3-final/e2e-spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/checkmark/checkmark.pipe.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/checkmark/checkmark.pipe.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/checkmark/checkmark.pipe.spec.ts rename to public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/checkmark/checkmark.pipe.spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts rename to public/docs/_examples/upgrade-phonecat-3-final/ts/app/core/phone/phone.service.spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts index f59e9c7afd..4db648092b 100644 --- a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts +++ b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/main.ts @@ -6,6 +6,7 @@ import { APP_BASE_HREF } from '@angular/common'; import { bootstrap } from '@angular/platform-browser-dynamic'; +import { FormsModule } from '@angular/forms'; import { HTTP_PROVIDERS } from '@angular/http'; import { ROUTER_PROVIDERS } from '@angular/router-deprecated'; import { Phone } from './core/phone/phone.service'; @@ -13,11 +14,14 @@ import { AppComponent } from './app.component'; // #enddocregion imports // #docregion bootstrap -bootstrap(AppComponent, [ - HTTP_PROVIDERS, - ROUTER_PROVIDERS, - { provide: APP_BASE_HREF, useValue: '!' }, - { provide: LocationStrategy, useClass: HashLocationStrategy }, - Phone -]); +bootstrap(AppComponent, { + imports: [FormsModule], + providers: [ + HTTP_PROVIDERS, + ROUTER_PROVIDERS, + { provide: APP_BASE_HREF, useValue: '!' }, + { provide: LocationStrategy, useClass: HashLocationStrategy }, + Phone + ] +}); // #enddocregion bootstrap diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts rename to public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-detail/phone-detail.component.spec.ts.disabled diff --git a/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts b/public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts.disabled similarity index 100% rename from public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts rename to public/docs/_examples/upgrade-phonecat-3-final/ts/app/phone-list/phone-list.component.spec.ts.disabled diff --git a/public/docs/_examples/user-input/ts/app/app.component.ts b/public/docs/_examples/user-input/ts/app/app.component.ts index 330d84b5a1..35be5f232d 100644 --- a/public/docs/_examples/user-input/ts/app/app.component.ts +++ b/public/docs/_examples/user-input/ts/app/app.component.ts @@ -1,26 +1,8 @@ // #docregion import { Component } from '@angular/core'; -import { ClickMeComponent } from './click-me.component'; -import { ClickMe2Component } from './click-me2.component'; - -import { LoopbackComponent } from './loop-back.component'; - -import { KeyUpComponent_v1, - KeyUpComponent_v2, - KeyUpComponent_v3, - KeyUpComponent_v4 } from './keyup.components'; - -import { LittleTourComponent } from './little-tour.component'; - @Component({ selector: 'my-app', - templateUrl: 'app/app.component.html', - directives: [ - ClickMeComponent, ClickMe2Component, - LoopbackComponent, - KeyUpComponent_v1, KeyUpComponent_v2, KeyUpComponent_v3, KeyUpComponent_v4, - LittleTourComponent - ] + templateUrl: 'app/app.component.html' }) export class AppComponent { } diff --git a/public/docs/_examples/user-input/ts/app/app.module.ts b/public/docs/_examples/user-input/ts/app/app.module.ts new file mode 100644 index 0000000000..41f13f9f11 --- /dev/null +++ b/public/docs/_examples/user-input/ts/app/app.module.ts @@ -0,0 +1,37 @@ +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; + +import { AppComponent } from './app.component'; +import { ClickMeComponent } from './click-me.component'; +import { ClickMe2Component } from './click-me2.component'; +import { + KeyUpComponent_v1, + KeyUpComponent_v2, + KeyUpComponent_v3, + KeyUpComponent_v4 +} from './keyup.components'; +import { LittleTourComponent } from './little-tour.component'; +import { LoopbackComponent } from './loop-back.component'; + + +@NgModule({ + imports: [ + BrowserModule + ], + declarations: [ + AppComponent, + ClickMeComponent, + ClickMe2Component, + KeyUpComponent_v1, + KeyUpComponent_v2, + KeyUpComponent_v3, + KeyUpComponent_v4, + LittleTourComponent, + LoopbackComponent + ], + providers: [ + + ], + bootstrap: [ AppComponent ] +}) +export class AppModule { } diff --git a/public/docs/_examples/user-input/ts/app/main.ts b/public/docs/_examples/user-input/ts/app/main.ts index 42dbeb9f7d..6af7a5b2ae 100644 --- a/public/docs/_examples/user-input/ts/app/main.ts +++ b/public/docs/_examples/user-input/ts/app/main.ts @@ -1,5 +1,5 @@ -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; -import { AppComponent } from './app.component'; +import { AppModule } from './app.module'; -bootstrap(AppComponent); +platformBrowserDynamic().bootstrapModule(AppModule); diff --git a/public/docs/_examples/webpack/e2e-spec.ts b/public/docs/_examples/webpack/e2e-spec.ts.disabled similarity index 100% rename from public/docs/_examples/webpack/e2e-spec.ts rename to public/docs/_examples/webpack/e2e-spec.ts.disabled diff --git a/public/docs/_examples/webpack/ts/package.webpack.json b/public/docs/_examples/webpack/ts/package.webpack.json index e383ba3355..4b3dc3e15d 100644 --- a/public/docs/_examples/webpack/ts/package.webpack.json +++ b/public/docs/_examples/webpack/ts/package.webpack.json @@ -10,14 +10,14 @@ }, "license": "MIT", "dependencies": { - "@angular/common": "2.0.0-rc.4", - "@angular/compiler": "2.0.0-rc.4", - "@angular/core": "2.0.0-rc.4", - "@angular/forms": "0.2.0", - "@angular/http": "2.0.0-rc.4", - "@angular/platform-browser": "2.0.0-rc.4", - "@angular/platform-browser-dynamic": "2.0.0-rc.4", - "@angular/router": "3.0.0-beta.1", + "@angular/common": "2.0.0-rc.5", + "@angular/compiler": "2.0.0-rc.5", + "@angular/core": "2.0.0-rc.5", + "@angular/forms": "0.3.0", + "@angular/http": "2.0.0-rc.5", + "@angular/platform-browser": "2.0.0-rc.5", + "@angular/platform-browser-dynamic": "2.0.0-rc.5", + "@angular/router": "3.0.0-rc.1", "core-js": "^2.4.0", "reflect-metadata": "0.1.2", "rxjs": "5.0.0-beta.6", diff --git a/public/docs/_examples/webpack/ts/src/app/app.component.spec.ts b/public/docs/_examples/webpack/ts/src/app/app.component.spec.ts index c2e85fd099..d7d0696aef 100644 --- a/public/docs/_examples/webpack/ts/src/app/app.component.spec.ts +++ b/public/docs/_examples/webpack/ts/src/app/app.component.spec.ts @@ -1,18 +1,17 @@ // #docregion import { - it, + addProviders, inject, - describe, - beforeEachProviders, - expect } from '@angular/core/testing'; import { AppComponent } from './app.component'; describe('App', () => { - beforeEachProviders(() => [ - AppComponent - ]); + beforeEach(() => { + addProviders([ + AppComponent + ]); + }); it ('should work', inject([AppComponent], (app: AppComponent) => { // Add real test here diff --git a/public/docs/_examples/webpack/ts/src/app/app.module.ts b/public/docs/_examples/webpack/ts/src/app/app.module.ts new file mode 100644 index 0000000000..362f3401fa --- /dev/null +++ b/public/docs/_examples/webpack/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/webpack/ts/src/main.ts b/public/docs/_examples/webpack/ts/src/main.ts index 8ea0ea84ce..0057db95ab 100644 --- a/public/docs/_examples/webpack/ts/src/main.ts +++ b/public/docs/_examples/webpack/ts/src/main.ts @@ -1,8 +1,8 @@ // #docregion -import { bootstrap } from '@angular/platform-browser-dynamic'; +import { browserDynamicPlatform } from '@angular/platform-browser-dynamic'; import { enableProdMode } from '@angular/core'; -import { AppComponent } from './app/app.component'; +import { AppModule } from './app/app.module'; // #docregion enable-prod if (process.env.ENV === 'production') { @@ -10,5 +10,5 @@ if (process.env.ENV === 'production') { } // #enddocregion enable-prod -bootstrap(AppComponent, []); +browserDynamicPlatform().bootstrapModule(AppModule); // #enddocregion diff --git a/public/docs/_examples/webpack/ts/typings.1.json b/public/docs/_examples/webpack/ts/typings.1.json index 3385926d1f..3d826df25a 100644 --- a/public/docs/_examples/webpack/ts/typings.1.json +++ b/public/docs/_examples/webpack/ts/typings.1.json @@ -2,6 +2,6 @@ "globalDependencies": { "core-js": "registry:dt/core-js#0.0.0+20160602141332", "jasmine": "registry:dt/jasmine#2.2.0+20160621224255", - "node": "registry:dt/node#6.0.0+20160621231320" + "node": "registry:dt/node#6.0.0+20160807145350" } } diff --git a/public/docs/js/latest/_data.json b/public/docs/js/latest/_data.json index 7d5ed7b13a..7bbc458b55 100644 --- a/public/docs/js/latest/_data.json +++ b/public/docs/js/latest/_data.json @@ -3,7 +3,7 @@ "icon": "home", "title": "Angular Docs", "menuTitle": "Docs Home", - "banner": "Welcome to Angular in JavaScript! The current Angular 2 release is rc.4. Please consult the Change Log about recent enhancements, fixes, and breaking changes." + "banner": "Welcome to Angular in JavaScript! The current Angular 2 release is rc.5. Please consult the Change Log about recent enhancements, fixes, and breaking changes." }, "quickstart": { diff --git a/public/docs/js/latest/guide/_data.json b/public/docs/js/latest/guide/_data.json index 1e0d9aa221..250d244eb4 100644 --- a/public/docs/js/latest/guide/_data.json +++ b/public/docs/js/latest/guide/_data.json @@ -29,7 +29,7 @@ "basics": true }, - "forms-deprecated": { + "forms": { "title": "Forms", "intro": "A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors.", "nextable": true, diff --git a/public/docs/js/latest/guide/forms-deprecated.jade b/public/docs/js/latest/guide/forms-deprecated.jade index 5a963272c8..b825bf6f2c 100644 --- a/public/docs/js/latest/guide/forms-deprecated.jade +++ b/public/docs/js/latest/guide/forms-deprecated.jade @@ -2,7 +2,7 @@ include ../_util-fns .alert.is-important :marked - This guide is using the deprecated forms API. + This guide is using the deprecated forms API, which is disabled as of RC5, thus this sample only works up to RC4. We have created a new version using the new API here. diff --git a/public/docs/js/latest/quickstart.jade b/public/docs/js/latest/quickstart.jade index 315d687414..045eb758ab 100644 --- a/public/docs/js/latest/quickstart.jade +++ b/public/docs/js/latest/quickstart.jade @@ -29,11 +29,12 @@ figure.image-display .file app .children .file app.component.js + .file app.module.js .file main.js .file index.html .file styles.css :marked - Functionally, it's an `index.html`, `styles.css` and two JavaScript files in an `app/` folder. + Functionally, it's an `index.html`, `styles.css` and three JavaScript files in an `app/` folder. We can handle that! Of course we won't build many apps that only run in plunker. @@ -41,6 +42,7 @@ figure.image-display 1. Set up our development environment 1. Write the Angular root component for our app + 1. Add an Angular Module 1. Bootstrap it to take control of the main web page 1. Write the main page (`index.html`) 1. Add some CSS (`styles.css`) @@ -124,7 +126,7 @@ code-example(format=""). '(component schema)')(format=".") :marked - The **`Component`** method takes a configuration object with two + The **`Component`** method takes a configuration object with three properties. The **`Class`** method is where we implement the component itself, giving it properties and methods that bind to the view and whatever behavior is appropriate for this part of the UI. @@ -167,7 +169,7 @@ code-example(format=""). provided by another module, we get it from the `app` object. When another module needs to refer to `AppComponent`, it gets it from the `app.AppComponent` like this: -+makeExample('quickstart/js/app/main.js', 'import','app/main.js (import)')(format=".") ++makeExample('quickstart/js/app/app.module.js', 'import', 'app/app.module.js (import)')(format=".") :marked Angular is also modular. It is a collection of library modules. @@ -210,22 +212,36 @@ code-example(format=""). Now we need something to tell Angular to load this component. + ### Add an NgModule + + Angular apps are composed of [Angular Modules](guide/ngmodules.html) that + snuggly contain all our components and everything else we need for our app. + + Create the `app/app.module.ts` file with the following content: + ++makeExample('quickstart/js/app/app.module.js', null, 'app/app.module.js') + +.l-sub-section + :marked + Read more about the `NgModule` configuration in the [appendix below](#ngmodule). + +:marked ### Bootstrap it! - + Add a new file , `main.js`, to the `app/` folder as follows: +makeExample('quickstart/js/app/main.js', null, 'app/main.js')(format=".") :marked We need two things to launch the application: - 1. Angular's browser `bootstrap` function - 1. The application root component that we just wrote. + 1. Angular's `platformBrowserDynamic().bootstrapModule` function + 1. The application root module that we just wrote. We have them both in our 'namespaces'. - Then we call `bootstrap`, passing in the **root component type**, `AppComponent`. + Then we call `bootstrapModule`, passing in the **root app module**, `AppModule`. .l-sub-section :marked - Learn why we need `bootstrap` from `ng.platformBrowserDynamic` - and why we create a separate *main.js* file in the [appendix below](#main). + Learn why we need `bootstrapModule` from `ng.platformBrowserDynamic` + and why we create a separate js files in the [appendix below](#main). :marked We've asked Angular to launch the app in a browser with our component at the root. Where will Angular put it? @@ -250,13 +266,14 @@ code-example(format=""). 1. We load the JavaScript libraries we need; learn about them [below](#libraries). - 2. We load our JavaScript files, paying attention to their order (`main.js` needs `app.component.js` to be there first). + 2. We load our JavaScript files, paying attention to their order (`main.js` needs `app.module.js` to be there first, and `app.module.js` needs `app.component.js`). 3. We add the `` tag in the ``. **This is where our app lives!** - When Angular calls the `bootstrap` function in `main.js`, it reads the `AppComponent` - metadata, finds the `my-app` selector, locates an element tag named `my-app`, - and loads our application between those tags. + When Angular calls the `bootstrapModule` function in `main.js`, + it reads the `AppModule` metadata, sees that `AppComponent` is the bootstrap component, + finds the `my-app` selector, locates an element tag named `my-app`, + and renders our application's view between those tags. :marked ### Add some style @@ -314,6 +331,7 @@ figure.image-display .file app .children .file app.component.js + .file app.module.js .file main.js .file index.html .file package.json @@ -322,12 +340,18 @@ figure.image-display And here are the files: +makeTabs(` quickstart/js/app/app.component.js, + quickstart/js/app/app.module.js, quickstart/js/app/main.js, quickstart/js/index.html, quickstart/js/package.1.json, quickstart/js/styles.1.css `,null, - `app/app.component.js, app/main.js, index.html, package.json, styles.css`) + `app/app.component.js, + app/app.module.js, + app/main.js, + index.html, + package.json, + styles.css`) :marked .l-main-section @@ -448,13 +472,32 @@ code-example(format=""). Just make sure there are no `npm ERR!` messages at the very end of `npm install`. +.l-main-section +:marked + + ### Appendix: ***NgModule*** + The `NgModule` decorator is listing: + + 1. What other Angular Modules ours uses + 1. Which components and directives we declare in our components + 1. The component to bootstrap at the start + + We import our lone `app.AppComponent` and add it to both `declaration` and `bootstrap` array. + + Notice that we also add `ng.platformBrowser.BrowserModule` to the `imports` array. + This is the Angular Module that contains all the needed Angular bits and pieces to run our app in the browser. + + Angular itself is split into separate Angular Modules so we only need to import the ones we really use. + + One of the most common ones is `FormsModule`, and soon we'll also see `RouterModule` and `HttpModule`. + .l-main-section :marked ### Appendix: ***main.js*** #### Bootstrapping is platform-specific - We use the `bootstrap` function from `ng.platformBrowserDynamic`, + We use the `platformBrowserDynamic().bootstrapModule` function from `ng.platformBrowserDynamic`, not `ng.core`. There's a good reason. We only call "core" those capabilities that are the same across all platform targets. @@ -469,11 +512,11 @@ code-example(format=""). These targets require a different kind of bootstrap function that we'd import from a different library. - #### Why do we create a separate ***main.js*** file? + #### Why do we create a separate ***main.js***, ***app.module.js*** and ***app.component.js*** files? The *main.js* file is tiny. This is just a QuickStart. - We could have folded its few lines into the `app.component.js` file - and spared ourselves some complexity. + We could have folded its few lines into the `app.module.js` file, and that one into + `app.component.js` and spared ourselves some complexity. We didn't for what we believe to be good reasons: 1. Doing it right is easy @@ -485,7 +528,7 @@ code-example(format=""). #### It's easy Sure it's an extra step and an extra file. How hard is that in the scheme of things? - We'll see that a separate `main.js` is beneficial for *most* apps + We'll see that a separate `main.js` and `app.module.js` is beneficial for *most* apps even if it isn't critical for the QuickStart. Let's develop good habits now while the cost is low. @@ -493,32 +536,33 @@ code-example(format=""). We should be thinking about testability from the beginning even if we know we'll never test the QuickStart. - It is difficult to unit test a component when there is a call to `bootstrap` in the same file. + It is difficult to unit test a component when there is a call to `bootstrapModule` in the same file. As soon as we load the component file to test the component, - the `bootstrap` function tries to load the application in the browser. + the `bootstrapModule` function tries to load the application in the browser. It throws an error because we're not expecting to run the entire application, just test the component. - Relocating the `bootstrap` function to `main.js` eliminates this spurious error + Relocating the `bootstrapModule` function to `main.js` eliminates this spurious error and leaves us with a clean component module file. #### Reusability We refactor, rename, and relocate files as our application evolves. - We can't do any of those things while the file calls `bootstrap`. + We can't do any of those things while the file calls `bootstrapModule`. we can't move it. We can't reuse the component in another application. We can't pre-render the component on the server for better performance. #### Separation of concerns - A component's responsibility is to present and manage a view. + A component's responsibility is to present and manage a view, and a NgModule's + reponsibility is to define the application context. - Launching the application has nothing to do with view management. + Launching the application has nothing to do with any of these. That's a separate concern. The friction we're encountering in testing and reuse stems from this unnecessary mix of responsibilities. #### Import/Export - While writing a separate `main.js` file we learned an essential Angular skill: + While writing a separate `main.js` and `app.module.js` files we learned an essential Angular skill: how to 'export' from one 'module' and 'import' into another via our simple namespace abstraction. We'll do a lot of that as we learn more Angular. diff --git a/public/docs/ts/latest/_data.json b/public/docs/ts/latest/_data.json index 735c69c2b2..6f63472d16 100644 --- a/public/docs/ts/latest/_data.json +++ b/public/docs/ts/latest/_data.json @@ -3,7 +3,7 @@ "icon": "home", "title": "Angular Docs", "menuTitle": "Docs Home", - "banner": "Welcome to Angular in TypeScript! The current Angular 2 release is rc.4. Please consult the Change Log about recent enhancements, fixes, and breaking changes." + "banner": "Welcome to Angular in TypeScript! The current Angular 2 release is rc.5. Please consult the Change Log about recent enhancements, fixes, and breaking changes." }, "cli-quickstart": { diff --git a/public/docs/ts/latest/cookbook/_data.json b/public/docs/ts/latest/cookbook/_data.json index 2607a72898..ecfc0e22d6 100644 --- a/public/docs/ts/latest/cookbook/_data.json +++ b/public/docs/ts/latest/cookbook/_data.json @@ -28,14 +28,14 @@ "dynamic-form-deprecated": { "title": "Dynamic Forms", - "intro": "Render dynamic forms with NgFormModel" + "intro": "Render dynamic forms with NgFormModel", + "basics": true, + "hide": true }, "dynamic-form": { "title": "Dynamic Forms", - "intro": "Render dynamic forms with FormGroup", - "basics": true, - "hide": true + "intro": "Render dynamic forms with FormGroup" }, "set-document-title": { diff --git a/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade b/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade index 8860d2bfe9..9145c58b1d 100644 --- a/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade +++ b/public/docs/ts/latest/cookbook/a1-a2-quick-reference.jade @@ -16,13 +16,11 @@ a(id="top") * [Filters/Pipes](#filters-pipes) - built-in *filters*, known as *pipes* in Angular 2 - * [Controllers/Components](#controllers-components) - *controllers* are *components* in Angular 2. - Also covers modules. + * [Modules/Controllers/Components](#controllers-components) - *modules* are *modules* but different + and *controllers* are *components* in Angular 2. * [Style Sheets](#style-sheets) - more options for CSS in Angular 2. - * [String date pipe](#string-dates) - a tip for displaying string date values. - .l-main-section :marked ## Template Basics @@ -49,7 +47,7 @@ table(width="100%") associated with this template. When using the `controller as` syntax, - the binding is prefixed with the controller alias (`vm`) because we + the binding is prefixed with the controller alias (`vm` or `$ctrl`) because we have to be specific about the source of the binding. td :marked @@ -131,11 +129,15 @@ table(width="100%") td :marked ### Bootstrapping - +makeExample('cb-a1-a2-quick-reference/ts/app/main.1.ts')(format="." ) + +makeExample('cb-a1-a2-quick-reference/ts/app/main.ts','','main.ts')(format="." ) +
    + +makeExample('cb-a1-a2-quick-reference/ts/app/app.module.1.ts','','app.module.ts')(format="." ) + :marked Angular 2 does not have a bootstrap directive. - We always launch the app in code by explicitly calling a bootstrap function - and passing it the name of the application's module (`AppComponent`). + We always launch the app in code by explicitly bootstrapping the application's root module (`AppModule`) + in `main.ts` + and the application's root component (`AppComponent`) in `app.module.ts`. For more information see [Quick Start](../quickstart.html). tr(style=top) @@ -499,7 +501,7 @@ table(width="100%") ### date +makeExample('cb-a1-a2-quick-reference/ts/app/app.component.html', 'date')(format=".") :marked - The Angular 2 `date` pipe is similar. See [note](#string-dates) about string date values. + The Angular 2 `date` pipe is similar. tr(style=top) td @@ -604,7 +606,10 @@ table(width="100%") a(id="controllers-components") .l-main-section :marked - ## Controllers / Components + ## Modules / Controllers / Components + In both Angular 1 and Angular 2, we use Angular modules to + help us organize our application into cohesive blocks of functionality. + In Angular 1, we write the code that provides the model and the methods for the view in a **controller**. In Angular 2, we build a **component**. @@ -647,15 +652,14 @@ table(width="100%") of other modules that this module depends upon. td :marked - ### import - +makeExample('cb-a1-a2-quick-reference/ts/app/movie-list.component.ts', 'import')(format=".") + ### Angular modules + +makeExample('cb-a1-a2-quick-reference/ts/app/app.module.1.ts')(format=".") :marked - Angular 2 does not have its own module system. Instead we use ES 2015 modules. - ES 2015 modules are file based, so each code file is its own module. + Angular 2 modules, defined with the `NgModule` decorator, serve the same purpose: + - `imports`: specifies the list of other modules that this module depends upon + - `declaration`: keeps track of our components, pipes, and directives. - We `import` what we need from the module files. - - For more information on modules see [Architecture Overview](../guide/architecture.html#module). + For more information on modules see [Angular Modules](../guide/ngmodule.html). tr(style=top) td :marked @@ -773,23 +777,3 @@ table(width="100%") other parts of the application. :marked [Back to top](#top) - -a(id="string-dates") -.l-main-section -:marked - ## Appendix: String dates - - Currently the Angular 2 `date` pipe does not process string dates such as - "2015-12-19T00:00:00". - - As a work around, subclass the Angular `DatePipe` with a version that can convert strings - and substitute that pipe in the HTML: - -+makeExample('cb-a1-a2-quick-reference/ts/app/date.pipe.ts', 'date-pipe', 'date.pipe.ts')(format=".") -:marked - Then import and declare that pipe in the `@Component` metadata `pipes` array: -:marked -+makeExample('cb-a1-a2-quick-reference/ts/app/movie-list.component.ts', 'date-pipe')(format=".") - -:marked - [Back to top](#top) diff --git a/public/docs/ts/latest/cookbook/dependency-injection.jade b/public/docs/ts/latest/cookbook/dependency-injection.jade index 352ecd3c2b..acfb42c1f5 100644 --- a/public/docs/ts/latest/cookbook/dependency-injection.jade +++ b/public/docs/ts/latest/cookbook/dependency-injection.jade @@ -80,7 +80,7 @@ include ../_util-fns .l-main-section :marked ## External module configuration - We can register _certain_ module providers when bootstrapping rather than in the root application component. + We can register _certain_ module providers in the `NgModule` rather than in the root application component. We'd do this when we expect to select or configure external modules that support our application but (a) aren't conceptually part of the application and (b) that we could change later without @@ -95,10 +95,10 @@ include ../_util-fns We'll switch to the real backend in production. The application shouldn't know or care one way or the other. - See both examples in the following `main.ts` - where we list their service providers in an array in the second parameter of the `bootstrap` method. + See both examples in the following `app.module.ts` + where we list their service providers in the `providers` array of the `NgModule` (AppModule). -+makeExample('cb-dependency-injection/ts/app/main.ts','bootstrap','app/main.ts')(format='.') ++makeExample('cb-dependency-injection/ts/app/app.module.ts','bootstrap','app/app.module.ts')(format='.') a(id="injectable") a(id="nested-dependencies") @@ -872,33 +872,12 @@ a(id="forwardref") The *Parent Finder* sample is full of circular class references that are impossible to break. - In the [*Alex/Cathy* example](#known-parent) above: - - * the `AlexComponent` lists the `CathyComponent` in its component metadata `directives` array - so it can display *Cathy* in its template. - - * the `CathyComponent` constructor injects the parent `AlexComponent` which means that the `alex` parameter - of its constructor has the `AlexComponent` type. - - *Alex* refers to *Cathy* and *Cathy* refers to *Alex*. We're stuck. We must define one of them first. - - We defined *Alex* first and built its `C_DIRECTIVES` array with a forward reference to *Cathy*: -+makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','C_DIRECTIVES','parent-finder.component.ts (C_DIRECTIVES)')(format='.') :marked -.l-sub-section - :marked - Defining *Alex* and *Cathy* in separate files won't help. - *Alex* would have to import *Cathy* and *Cathy* would have to import *Alex*. - - We *had* to define *Alex* first because, - while we can add `forwardRef(CathyComponent)` to *Alex*'s `directives` array, - we can't write `public alex: forwardRef(AlexComponent))` in *Cathy*'s constructor. -:marked - We face a similar dilemma when a class makes *a reference to itself* + We face this dilemma when a class makes *a reference to itself* as does the `AlexComponent` in its `providers` array. The `providers` array is a property of the `@Component` decorator function which must appear *above* the class definition. - Again we break the circularity with `forwardRef`: + We break the circularity with `forwardRef`: +makeExample('cb-dependency-injection/ts/app/parent-finder.component.ts','alex-providers','parent-finder.component.ts (AlexComponent providers)')(format='.') :marked diff --git a/public/docs/ts/latest/cookbook/dynamic-form-deprecated.jade b/public/docs/ts/latest/cookbook/dynamic-form-deprecated.jade index 453b46bc7d..029c7c7743 100644 --- a/public/docs/ts/latest/cookbook/dynamic-form-deprecated.jade +++ b/public/docs/ts/latest/cookbook/dynamic-form-deprecated.jade @@ -2,7 +2,7 @@ include ../_util-fns .alert.is-important :marked - This cookbook is using the deprecated forms API. + This cookbook is using the deprecated forms API, which is disabled as of RC5, thus this sample only works up to RC4. We have created a new version of this cookbook using the new API here. diff --git a/public/docs/ts/latest/cookbook/dynamic-form.jade b/public/docs/ts/latest/cookbook/dynamic-form.jade index 2c865b64cf..705fadc488 100644 --- a/public/docs/ts/latest/cookbook/dynamic-form.jade +++ b/public/docs/ts/latest/cookbook/dynamic-form.jade @@ -1,11 +1,5 @@ include ../_util-fns -.alert.is-important - :marked - This cookbook uses the new forms API. - - The old forms API is deprecated, but we still maintain a separate version of the cookbook using the deprecated forms API here. - :marked We can't always justify the cost and time to build handcrafted forms, especially if we'll need a great number of them, they're similar to each other, and they change frequently @@ -44,16 +38,21 @@ include ../_util-fns :marked ## Bootstrap - During bootstrap we have to register the new forms module by calling `provideForms()` and pass the result to the provider array. + We start by creating an `NgModule` called `AppModule`. -+makeExample('cb-dynamic-form/ts/app/main.ts','','app/main.ts') - -:marked - The old forms API is going through a deprecation phase. During this transition Angular is supporting both form modules. - - To remind us that the old API is deprecated, Angular will print a warning message to the console. + In our example we will be using Reactive Forms. - Since we are converting to the new API, and no longer need the old API, we call `disableDeprecatedForms()` to disable the old form functionality and the warning message. + Reactive Forms belongs to a different `NgModule` called `ReactiveFormsModule`, so in order to access any Reactive Forms directives, we have to import `ReactiveFormsModule` from `AppModule`. + + We bootstrap our `AppModule` in main.ts. + ++makeTabs( + `cb-dynamic-form/ts/app/app.module.ts, + cb-dynamic-form/ts/app/main.ts`, + null, + `app.module.ts, + main.ts` +) .l-main-section @@ -121,9 +120,7 @@ include ../_util-fns In both components we're relying on Angular's **formGroup** to connect the template HTML to the underlying control objects, populated from the question model with display and validation rules. - `formControlName` and `formGroup` have to be registered as directives before we can use them in our templates. - - It turns out we get access to all form directives by importing and registering `REACTIVE_FORM_DIRECTIVES`. + `formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`. :marked diff --git a/public/docs/ts/latest/cookbook/set-document-title.jade b/public/docs/ts/latest/cookbook/set-document-title.jade index f25fe798f8..60fa73a49b 100644 --- a/public/docs/ts/latest/cookbook/set-document-title.jade +++ b/public/docs/ts/latest/cookbook/set-document-title.jade @@ -38,18 +38,8 @@ code-example(format=''). for getting and setting the current HTML document title: * `getTitle() : string` — Gets the title of the current HTML document. - * `setTitle( newTitle : string )` — Sets the title of the current HTML document. - - While this class is part of the Browser platform package, it is *not part of the default Browser - platform providers* that Angular loads automatically. - This means as we bootstrap our application using the Browser platform `boostrap()` - function, we'll also have to include `Title` service explicitly as one of the bootstrap providers: - -+makeExample( "cb-set-document-title/ts/app/main.ts", "bootstrap-title", "app/main.ts (provide Title service)" )(format='.') -:marked - Once we've explicitly provided the `Title` service we can then inject the `Title` service into any of our - custom application components and services. - + * `setTitle( newTitle : string )` — Sets the title of the current HTML document. + Let's inject the `Title` service into the root `AppComponent` and expose a bindable `setTitle` method that calls it: +makeExample( "cb-set-document-title/ts/app/app.component.ts", "class", "app/app.component.ts (class)" )(format='.') @@ -63,9 +53,10 @@ figure.image-display +makeTabs( `cb-set-document-title/ts/app/main.ts, - cb-set-document-title/ts/app/app.component.ts`, - '', - 'app/main.ts, app/app.component.ts' ) + cb-set-document-title/ts/app/app.module.ts, + cb-set-document-title/ts/app/app.component.ts`, + '', + 'app/main.ts, app/app.module.ts, app/app.component.ts' ) // Todo: tie this back to the router so we can see how to use this Title service to (re)set the title diff --git a/public/docs/ts/latest/glossary.jade b/public/docs/ts/latest/glossary.jade index 50c6100cda..3342e16d0f 100644 --- a/public/docs/ts/latest/glossary.jade +++ b/public/docs/ts/latest/glossary.jade @@ -21,6 +21,19 @@ include _util-fns // #enddocregion a1 .l-main-section + +:marked + ## Angular Module +.l-sub-section + :marked + Helps us organize an application into cohesive blocks of functionality. + An Angular module identifies the components, directives, and pipes that are used by the application + along with the list of external Angular modules that the application needs, such as `FormsModule`. + + Every Angular application has an application root module class. By convention the class is + called `AppModule` and resides in a file named `app.component.ts`. + + See the [Angular Module](/docs/ts/latest/guide/ngmodule.html) chapter for details and examples. :marked ## Annotation .l-sub-section @@ -50,10 +63,10 @@ include _util-fns ## Barrel .l-sub-section :marked - A barrel is a way to *rollup exports* from several modules into a single convenience module. - The barrel itself is a module file that re-exports *selected* exports of other modules. + A barrel is a way to *rollup exports* from several ES2015 modules into a single convenience ES2015 module. + The barrel itself is an ES2015 module file that re-exports *selected* exports of other ES2015 modules. - Imagine three modules in a `heroes` folder: + Imagine three ES2015 modules in a `heroes` folder: code-example. // heroes/hero.component.ts export class HeroComponent {} @@ -81,12 +94,17 @@ include _util-fns import { Hero, HeroService } from '../heroes'; // index is implied :marked The Angular [scoped packages](#scoped-package) each have a barrel named `index`. + // #enddocregion b-c :marked That's why we can write this: +makeExcerpt('quickstart/ts/app/app.component.ts', 'import', '') // #docregion b-c +.alert.is-important + :marked + Note that you can often achieve this same goal using [Angular modules](#angular-module) instead. + :marked ## Binding .l-sub-section @@ -102,10 +120,9 @@ include _util-fns ## Bootstrap .l-sub-section :marked - We launch an Angular application by "bootstrapping" it with the `bootstrap` method. - The `bootstrap` method identifies an application's top level "root" [Component](#component) - and optionally registers service [providers](#provider) with the - [dependency injection system](#dependency-injection). + We launch an Angular application by "bootstrapping" it using the application root Angular module (`AppModule`). + The bootstraping identifies an application's top level "root" [Component](#component), which is the first + component that is loaded for the application. For more information see the [QuickStart](/docs/ts/latest/quickstart.html). One can bootstrap multiple apps in the same `index.html`, each with its own top level root. @@ -271,9 +288,7 @@ include _util-fns Registering providers is a critical preparatory step. Angular registers some of its own providers with every injector. - We can register our own providers. Quite often the best time to register a `Provider` - is when we [bootstrap](#bootstrap) the application. - There are other opportunities to register as well. + We can register our own providers. Learn more in the [Dependency Injection](/docs/ts/latest/guide/dependency-injection.html) chapter. :marked @@ -450,6 +465,14 @@ include _util-fns :marked ## Module .l-sub-section + + .alert.is-important + :marked + In Angular, there are two types of modules: + - [Angular modules](#angular-module). + See the [Angular Module](/docs/ts/latest/guide/ngmodule.html) chapter for details and examples. + - ES2015 modules as described in this section. + :marked Angular apps are modular. @@ -487,6 +510,17 @@ include _util-fns .l-main-section +:marked + ## Observable +.l-sub-section + :marked + We can think of an observable as an array whose items arrive asynchronously over time. + Observables help us manage asynchronous data, such as data coming from a backend service. + Observables are used within Angular itself, including Angular's event system and its http client service. + + To use observables, Angular uses a third-party library called Reactive Extensions (RxJS). + Observables are a proposed feature for ES 2016, the next version of JavaScript. + :marked ## Output .l-sub-section @@ -505,7 +539,7 @@ include _util-fns .l-sub-section :marked The practice of writing compound words or phrases such that each word or abbreviation begins with a capital letter. - Class names are typically spelled in PascalCase. Examples include: `Person` and `Customer`. + Class names are typically spelled in PascalCase. Examples include: `Person` and `HeroDetailComponent`. This form is also known as **upper camel case**, to distinguish it from **lower camel case** which we simply call [camelCase](#camelcase). In this documentation, "PascalCase" means *upper camel case* and "camelCase" means *lower camel case*. @@ -516,7 +550,7 @@ include _util-fns :marked An Angular pipe is a function that transforms input values to output values for display in a [view](#view). We use the `#{atSym}Pipe` !{decorator} - to associate the pipe function with a name. We then can use that + to associate the pipe function with a name. We can then use that name in our HTML to declaratively transform values on screen. Here's an example that uses the built-in `currency` pipe to display @@ -543,6 +577,23 @@ include _util-fns .l-main-section + +:marked + ## Reactive Forms +.l-sub-section + :marked + A technique for building Angular forms through code in a component. + The alternate technique is [Template-Driven Forms](#template-driven-forms). + + When building reactive forms: + - The "source of truth" is the component. The validation is defined using code in the component. + - Each control is explicitly created in the component class with `new FormControl()` or with `FormBuilder`. + - The template input elements do *not* use `ngModel`. + - The associated Angular directives are all prefixed with `Form` such as `FormGroup`, `FormControl`, and `FormControlName`. + + Reactive forms are powerful, flexible, and great for more complex data entry form scenarios, such as dynamic generation + of form controls. + :marked ## Router .l-sub-section @@ -555,18 +606,32 @@ include _util-fns The Angular [Component Router](/docs/ts/latest/guide/router.html) is a richly featured mechanism for configuring and managing the entire view navigation process including the creation and destruction of views. + + In most cases, components becomes attached to a [router](#router) by means + of a `RouterConfig` that defines routes to views. + + A [routing component's](#routing-component) template has a `RouterOutlet` element where it can display views produced by the router. + + Other views in the application likely have anchor tags or buttons with `RouterLink` directives that users can click to navigate. + + See the [Component Router](/docs/ts/latest/guide/router.html) chapter to learn more. + +:marked + ## RouterModule +.l-sub-section + :marked + A separate [Angular module](#angular-module) that provides the necessary service providers and directives for navigating through application views. + + See the [Component Router](/docs/ts/latest/guide/router.html) chapter to learn more. + + :marked ## Routing Component .l-sub-section :marked - A [Component](#component) with an attached router. + An Angular [Component](#component) with a RouterOutlet that displays views based on router navigations. - In most cases, the component became attached to a [router](#router) by means - of a `#{atSym}RouterConfig` #{decorator} that defined routes to views controlled by this component. - - The component's template has a `RouterOutlet` element where it can display views produced by the router. - - It likely has anchor tags or buttons with `RouterLink` directives that users can click to navigate. + See the [Component Router](/docs/ts/latest/guide/router.html) chapter to learn more. .l-main-section @@ -587,6 +652,23 @@ include _util-fns +makeExcerpt('architecture/ts/app/app.component.ts', 'import', '') // #docregion n-s-2 +:marked + ## Service +.l-sub-section + :marked + Components are great and all … but what do we do with data or logic that are not associated + with a specific view or that we want to share across components? We build services! + + Applications often require services such as a hero data service or a logging service. + Our components depend on these services to do the heavy lifting. + + A service is a class with a focused purpose. + We often create a service to implement features that are + independent from any specific view, + provide share data or logic across components, or encapsulate external interactions. + + See the [Services](/docs/ts/latest/tutorial/toh-pt4.html) chapter of the tutorial to learn more. + :marked ## Structural Directive .l-sub-section @@ -597,6 +679,8 @@ include _util-fns The `ngIf` "conditional element" directive and the `ngFor` "repeater" directive are good examples in this category. + + See the [Structural Directives](/docs/ts/latest/guide/structural-directives.html) chapter to learn more. // #enddocregion n-s-2 // #docregion t1 @@ -612,11 +696,31 @@ include _util-fns We write templates in a special [Template Syntax](/docs/ts/latest/guide/template-syntax.html). + +:marked + ## Template-Driven Forms +.l-sub-section + :marked + A technique for building Angular forms using HTML forms and input elements in the view. + The alternate technique is [Reactive Forms](#reactive-forms). + + When building template-driven forms: + - The "source of truth" is the template. The validation is defined using attributes on the individual input elements. + - [Two-way binding](#data-binding) with `ngModel` keeps the component model in synchronization with the user's entry into the input elements. + - Behind the scenes, Angular creates a new control for each input element that has a `name` attribute and + two-way binding set up. + - The associated Angular directives are all prefixed with `ng` such as `ngForm`, `ngModel`, and `ngModelGroup`. + + Template-driven forms are convenient, quick, and simple and are a good choice for many basic data entry form scenarios. + + Learn how to build template-driven forms + in the [Forms](/docs/ts/latest/guide/forms.html) chapter. + :marked ## Template Expression .l-sub-section :marked - An expression in a JavaScript-like syntax that Angular evaluates within + An expression is a JavaScript-like syntax that Angular evaluates within a [data binding](#data-binding). Learn how to write template expressions in the [Template Syntax](/docs/ts/latest/guide/template-syntax.html#template-expressions) chapter. diff --git a/public/docs/ts/latest/guide/_data.json b/public/docs/ts/latest/guide/_data.json index 3bc69e1c15..b59ef46195 100644 --- a/public/docs/ts/latest/guide/_data.json +++ b/public/docs/ts/latest/guide/_data.json @@ -33,15 +33,15 @@ "title": "Forms", "intro": "A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors.", "nextable": true, - "basics": true, - "hide": true + "basics": true }, "forms-deprecated": { "title": "Forms", "intro": "A form creates a cohesive, effective, and compelling data entry experience. An Angular form coordinates a set of data-bound user controls, tracks changes, validates input, and presents errors.", "nextable": true, - "basics": true + "basics": true, + "hide": true }, "dependency-injection": { diff --git a/public/docs/ts/latest/guide/architecture.jade b/public/docs/ts/latest/guide/architecture.jade index 3510c864bb..a13f303e0d 100644 --- a/public/docs/ts/latest/guide/architecture.jade +++ b/public/docs/ts/latest/guide/architecture.jade @@ -12,10 +12,11 @@ block angular-parts The framework consists of several cooperating libraries, some of them core and some optional. :marked - With Angular, we write applications by composing HTML *templates* with Angularized-markup, + We write Angular applications by composing HTML *templates* with Angularized-markup, writing *component* classes to manage those templates, adding application logic in *services*, - and handing the top root component to Angular's *bootstrapper*. - + and boxing components and services in *modules*. + + Then we launch the app by *bootstrapping* the top _root module_. Angular takes over, presenting our application content in a browser and responding to user interactions according to the instructions we provided. @@ -49,118 +50,114 @@ figure figure img(src="/resources/images/devguide/architecture/module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" ) :marked - Angular apps are modular. + Angular apps are modular and Angular has its own modularity system called _Angular Modules_ + or, occasionally, _NgModules_. - In general we assemble our application from many **modules**. + _Angular Modules_ are a big deal. + We can only introduce them there; the [Angular Modules](ngmodule.html) chapter covers them in depth. - A typical module is a cohesive block of code dedicated to a single purpose. - A module **exports** something of value in that code, typically one thing such as a class. -

    - -block modules-in-dart - //- N/A - -block modules-are-optional - .l-sub-section - :marked - ### Modules are optional - We highly recommend modular design. TypeScript has great support for ES2015 module syntax and our chapters assume we're taking a modular - approach using that syntax. That's why we list *Module* among the basic building blocks. - - Angular itself doesn't require a modular approach nor this particular syntax. Don't use it if you don't want it. - Each chapter has plenty to offer after you steer clear of the `import` and `export` statements. - - Find setup and organization clues in the JavaScript track (select it from the combo-box at the top of this page) - which demonstrates Angular 2 development with plain old JavaScript and no module system. - -- var _app_comp_filename = _docsFor == 'dart' ? 'app_component.dart' : 'app.component.ts'; +

    :marked - Perhaps the first module we meet is a module that exports a *component* class. - The component is one of the basic Angular blocks, we write a lot of them, - and we'll talk about components in the next segment. For the moment it is enough to know that a - component class is the kind of thing we'd export from a module. + Every Angular app has at least one module, the _root module_, conventionally named `AppModule`. - Most applications have an `AppComponent`. By convention, we'll find it in a file named `!{_app_comp_filename}`. - Look inside such a file and we'll see a declaration such as this one. + While the _root_ module may be the only module in a small application, most apps have many more + _feature_ modules, each a cohesive block of code dedicated to an application domain, + a workflow, or a closely-related set of capabilities. -+makeExcerpt('app/app.component.ts ()', 'export') - -block export-qualifier + An Angular module, whether a _root_ or _feature_, is a class with an `@NgModule` decorator. +.l-sub-section :marked - The `export` statement tells TypeScript that this is a module whose - `AppComponent` class is public and accessible to other modules of the application. - + Decorators are functions that modify JavaScript classes. + Angular has many decorators that attach metadata to classes so that it knows + what those classes mean and how they should work. + + Learn more about decorators on the web. :marked - When we need a reference to the `AppComponent`, we **import** it like this: + `NgModule` is a decorator function that takes a single metadata object whose properties describe the module. + The most important are + * `declarations` - the _view classes_ that belong to this module. + Angular has three kinds of view classes: [components](#components), [directives](#directives) and [pipes](pipes.html). -+makeExcerpt('app/main.ts', 'import') + * `exports` - subset of declarations that should be visible and usable in the component [templates](#templates) of other modules. -block ts-import + * `imports` - other modules whose exported classes are needed by component templates declared in _this_ module. + + * `providers` creators of [services](#services) that this module contributes to + the global collection of services; they become accessible in all parts of the app. + + * `bootstrap` - identifies the main application view, called the _root component_, + that hosts all other app views. Only the _root module_ should set this `bootstrap` property. + + Here's a simple root module: ++makeExample('app/mini-app.ts', 'module', 'app/app.module.ts')(format='.') + +.l-sub-section :marked - The `import` statement tells the system it can get an `AppComponent` from a module named `app.component` - located in a neighboring file. - The **module name** (AKA module id) is often the same as the filename without its extension. + The `export` of `AppComponent` is just for show. + A root module has no reason to export anything because ... it's the root. + We don't expect other modules to import the root module. + + But if one did, it could use the `AppComponent` in its component templates. +:marked + We launch an application by _bootstrapping_ its root module. + During development we're likely to bootstrap the `AppModule` in a `main.ts` file like this one. + ++makeExample('app/main.ts', '', 'app/main.ts')(format='.') :marked - ### Libraries + ### Angular Modules vs. JavaScript Modules + + The Angular module — a class decorated with `@NgModule` — is a fundamental feature of Angular itself. + + JavaScript also has its own module system for managing collections of JavaScript objects. + It's completely different and unrelated to the Angular module system. + + In JavaScript each _file_ is a module and all objects defined in the file belong to that module. + The module declares some objects to be public by marking them with the `export` key word. + Other JavaScript modules use *import statements* to access public objects from other modules. + ++makeExample('app/app.module.ts', 'imports', '')(format='.') ++makeExample('app/app.module.ts', 'export', '')(format='.') + +.l-sub-section + :marked + Learn more about the JavaScript module system on the web. +:marked + These are two different and _complementary_ module systems. We use them both to write our apps. + + ### Angular Libraries figure img(src="/resources/images/devguide/architecture/library-module.png" alt="Component" align="left" style="width:240px; margin-left:-40px;margin-right:10px" ) -block angular-library-modules - :marked - Some modules are _libraries_ of other modules. - Angular itself ships as a collection of library modules within several npm packages. - Their names begin with the `!{_at_angular}` prefix. - - Each Angular library contains a [barrel](../glossary.html#barrel) module - that is actually a public façade over several logically-related private modules. - :marked - `!{_at_angular}/core` is the primary Angular library from which we get most of what we need. -
    + Angular itself ships as a collection of JavaScript modules. We can think of them as library modules. - There are other important Angular libraries too, such as `!{_at_angular}/common`, - `!{_at_angular}/http` and `!{_at_angular}/router`. - We import what we need from an Angular !{_library_module}. - -block angular-imports - :marked - For example, we import the Angular **`Component` *function*** from `@angular/core` like this: - +makeExcerpt('app/app.component.ts', 'import') - :marked - Compare that syntax to our previous import of `AppComponent`. - +makeExcerpt('app/main.ts', 'import') - - :marked - Notice the difference? - In the first case, when importing from an Angular library module, - the import statement refers to the bare module name, `@angular/core`, *without a path prefix*. - - When we import from one of *our* own files, we prefix the module name with the file path. - In this example we specify a relative file path (`./`). That means the - source module is in the same folder (`./`) as the module importing it. - We could path up and around the application folder structure if the source module were somewhere else. - .l-sub-section - :marked - We import and export in the ECMAScript 2015 (ES2015) module syntax. - Learn more about that syntax [here](http://www.2ality.com/2014/09/es6-modules-final.html) - and many other places on the web. - - The infrastructure *behind* module loading and importing is an important subject. - But it's a subject outside the scope of this introduction to Angular. - While we're focused on our application, *import* and *export* - is about all we need to know. - -- var _export = _docsFor == 'dart' ? 'publicly declare' : 'export'; -- var _declare = _docsFor == 'dart' ? 'declare' : 'export'; + Each Angular library name begin with the `!{_at_angular}` prefix. + + We install them with the **npm** package manager and import parts of them with JavaScript `import` statements. +

    :marked - The key take-aways are: - * Angular apps are composed of modules. - * Modules !{_export} things — classes, function, values — that other modules import. - * We prefer to write our application as a collection of modules, each module exporting one thing. + For example, we import Angular's `Component` decorator from the `@angular/core` library like this: ++makeExample('app/app.component.ts', 'import', '')(format='.') +:marked + We also import Angular _modules_ from Angular _libraries_ using JavaScript import statements: ++makeExample('app/mini-app.ts', 'import-browser-module', '')(format='.') +:marked + Our application module needs material from within that `BrowserModule` + so we add it to the `@NgModule` metadata `imports` like this. ++makeExample('app/mini-app.ts', 'ngmodule-imports', '')(format='.') +:marked + We're using both the Angular and JavaScript module systems _together_. - The first module we write will most likely !{_declare} a component. + It's easy to confuse the two systems because they share the common vocabulary of "imports" and "exports". + Hang in there. The confusion will yield to clarity with time and experience. + +.l-sub-section + :marked + Learn more in the [Angular Modules](ngmodule.html) chapter. + +.l-hr .l-main-section :marked @@ -192,6 +189,8 @@ figure For the moment, have faith that Angular will call the constructor and deliver an appropriate `HeroService` when we need it. +.l-hr + .l-main-section :marked ## Templates @@ -231,6 +230,8 @@ figure In this manner we'll compose complex component trees to build out our richly featured application.
    +.l-hr + .l-main-section :marked ## Metadata @@ -259,7 +260,6 @@ figure block ts-decorator :marked - A decorator is a function. Decorators often have a configuration parameter. The `@Component` decorator takes a required configuration object with the information Angular needs to create and present the component and its view. @@ -299,6 +299,8 @@ figure The architectural takeaway is that we must add metadata to our code so that Angular knows what to do. +.l-hr + .l-main-section :marked ## Data binding @@ -357,6 +359,8 @@ figure Data binding is also important for communication between parent and child components.
    +.l-hr + .l-main-section :marked ## Directives @@ -417,6 +421,8 @@ block dart-bool `HeroListComponent` are one kind of custom directive. +.l-hr + .l-main-section :marked ## Services @@ -469,6 +475,8 @@ figure Angular does help us *follow* these principles by making it easy to factor our application logic into services and make those services available to components through *dependency injection*. +.l-hr + .l-main-section :marked ## Dependency injection @@ -503,18 +511,19 @@ figure If the injector doesn't have a `HeroService`, how does it know how to make one? In brief, we must have previously registered a **provider** of the `HeroService` with the injector. - A provider is something that can create or return a service, typically the service class itself. + A provider is something that can create or return a service, typically the service class itself. - We can register providers at any level of the application component tree. - We often do so at the root when we bootstrap the application so that - the same instance of a service is available everywhere. + We can register providers in modules or in components. -+makeExcerpt('app/main.ts', 'bootstrap') + We often add providers to the [root module](#module) so that + the same instance of a service is available everywhere. + ++makeExample('app/app.module.ts', 'providers', 'app/app.module.ts (module providers)')(format='.') :marked - Alternatively, we might register at a component level, in the providers property of the `@Component` metadata: + Alternatively, we might register at a component level in the `providers` property of the `@Component` metadata: -+makeExcerpt('app/hero-list.component.ts', 'providers') ++makeExample('app/hero-list.component.ts', 'providers', 'app/hero-list.component.ts (component providers)')(format='.') :marked Registering at a component level means we get a new instance of the @@ -535,6 +544,8 @@ figure * We register *providers* with injectors. +.l-hr + .l-main-section :marked ## Wrap up @@ -560,15 +571,10 @@ figure > [**Animations**](animations.html): The animation library makes it easy for developers to animate component behavior without deep knowledge of animation techniques or CSS. - > **Bootstrap**: A method to configure and launch the root application component. - > **Change detection**: Learn how Angular decides that a component property value has changed and when to update the screen. Learn how it uses **zones** to intercept asynchronous activity and run its change detection strategies. - > **Component router**: With the component Router service, users can navigate a multi-screen application - in a familiar web browsing style using URLs. - > **Events**: The DOM raises events. So can components and services. Angular offers mechanisms for publishing and subscribing to events. diff --git a/public/docs/ts/latest/guide/attribute-directives.jade b/public/docs/ts/latest/guide/attribute-directives.jade index e6f0db9472..b27047af9c 100644 --- a/public/docs/ts/latest/guide/attribute-directives.jade +++ b/public/docs/ts/latest/guide/attribute-directives.jade @@ -126,10 +126,11 @@ p Meanwhile, we'll revise the `AppComponent` to reference this template. +makeExample('attribute-directives/ts/app/app.component.ts',null,'app/app.component.ts') :marked - We've added an `import` statement to fetch the 'Highlight' directive and, - added that class to a `directives` component metadata so that Angular + We'll add an `import` statement to fetch the 'Highlight' directive and, + added that class to the `declarations` NgModule metadata so that Angular will recognize our directive when it encounters `myHighlight` in the template. - ++makeExample('attribute-directives/ts/app/app.module.ts',null,'app/app.module.ts') +:marked We run the app and see that our directive highlights the paragraph text. figure.image-display @@ -138,15 +139,15 @@ figure.image-display :marked ### Your directive isn't working? - Did you remember to set the `directives` attribute of `@Component`? It is easy to forget! + Did you remember to add the directive to the the `declarations` attribute of `@NgModule`? It is easy to forget! Open the console in the browser tools and look for an error like this: code-example(format="nocode"). EXCEPTION: Template parse errors: - Can't bind to 'myHighlight' since it isn't a known native property + Can't bind to 'myHighlight' since it isn't a known property of 'p'. :marked Angular detects that we're trying to bind to *something* but it doesn't know what. - We have to tell it by listing `HighlightDirective` in the `directives` metadata array. + We have to tell it by listing `HighlightDirective` in the `declarations` metadata array. :marked Let's recap what happened. @@ -318,6 +319,7 @@ figure.image-display `attribute-directives/ts/app/app.component.ts, attribute-directives/ts/app/app.component.html, attribute-directives/ts/app/highlight.directive.ts, + attribute-directives/ts/app/app.module.ts, attribute-directives/ts/app/main.ts, attribute-directives/ts/index.html `, @@ -325,6 +327,7 @@ figure.image-display `app.component.ts, app.component.html, highlight.directive.ts, + app.module.ts, main.ts, index.html `) diff --git a/public/docs/ts/latest/guide/dependency-injection.jade b/public/docs/ts/latest/guide/dependency-injection.jade index 2ad5b7a7ed..8755cbec03 100644 --- a/public/docs/ts/latest/guide/dependency-injection.jade +++ b/public/docs/ts/latest/guide/dependency-injection.jade @@ -253,37 +253,37 @@ block ctor-syntax We do have to configure the injector by registering the **providers** that create the services our application requires. We'll explain what [providers](#providers) are later in this chapter. - Before we do, let's see an example of provider registration during bootstrapping: -+makeExample('dependency-injection/ts/app/main.1.ts', 'bootstrap-discouraged')(format='.') + We can either register a provider within an [NgModule](ngmodule.html) or in application components -:marked - The injector now knows about our `HeroService`. - An instance of our `HeroService` will be available for injection across our entire application. + ### Registering providers in an NgModule + Here's our AppModule where we register a `Logger`, an `UserService`, and an `APP_CONFIG` provider. - Of course we can't help wondering about that comment telling us not to do it this way. - It *will* work. It's just not a best practice. - The bootstrap provider option is intended for configuring and overriding Angular's own - preregistered services, such as its routing support. - - The preferred approach is to register application providers in application components. +- var stylePattern = { otl: /(providers)/ }; ++makeExample('dependency-injection/ts/app/app.module.ts', 'ngmodule','app/app.module.ts', stylePattern)(format='.') + :marked ### Registering providers in a component Here's a revised `HeroesComponent` that registers the `HeroService`. -- var stylePattern = { otl: /(providers:.*),/ }; +makeExample('dependency-injection/ts/app/heroes/heroes.component.1.ts', 'full','app/heroes/heroes.component.ts', stylePattern)(format='.') :marked - Look closely at the `providers` part of the `@Component` metadata. - An instance of the `HeroService` is now available for injection in this `HeroesComponent` - and all of its child components. + ### When to use the NgModule and when an application component? + On the one hand, a provider in an NgModule is registered in the root injector. That means that every provider + registered within an NgModule will be accessible in the entire application. - The `HeroesComponent` itself doesn't happen to need the `HeroService`. - But its child `HeroListComponent` does, so we head there next. + On the other hand, a provider registered in an application component is available only on that component and all its children. + + We want the `APP_CONFIG` service to be available all across the application, but a `HeroService` is only used within the *Heroes* + feature area — and nowhere else. — + +.l-sub-section + :marked + Read also **Should I add providers to the root AppModule or the root AppComponent?** at the [NgModule](ngmodule.html#q-root-component-or-module) chapter. :marked ### Preparing the HeroListComponent for injection @@ -470,9 +470,9 @@ block real-logger :marked We're likely to need the same logger service everywhere in our application, so we put it in the project's `#{_appDir}` folder, and - we register it in the `providers` #{_array} of the metadata for our application root component, `AppComponent`. + we register it in the `providers` #{_array} of the metadata for our application module, `AppModule`. -+makeExcerpt('app/providers.component.ts','providers-logger','app/app.component.ts (excerpt)') ++makeExcerpt('app/providers.component.ts','providers-logger','app/app.module.ts (excerpt)') :marked If we forget to register the logger, Angular throws an exception when it first looks for the logger: @@ -497,7 +497,7 @@ code-example(format="nocode"). We must register a service *provider* with the injector, or it won't know how to create the service. - Earlier we registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppComponent` like this: + Earlier we registered the `Logger` service in the `providers` #{_array} of the metadata for the `AppModule` like this: +makeExample('dependency-injection/ts/app/providers.component.ts','providers-logger') @@ -801,9 +801,9 @@ block what-should-we-use-as-token block dart-map-alternative :marked - Or we can provide and inject the configuration object in our top-level `AppComponent`. + Or we can provide and inject the configuration object in an ngModule like `AppModule`. - +makeExcerpt('app/app.component.ts','providers') + +makeExcerpt('app/app.module.ts','ngmodule-providers') #optional :marked diff --git a/public/docs/ts/latest/guide/displaying-data.jade b/public/docs/ts/latest/guide/displaying-data.jade index 5fbf0e7ff7..376c2d254c 100644 --- a/public/docs/ts/latest/guide/displaying-data.jade +++ b/public/docs/ts/latest/guide/displaying-data.jade @@ -278,6 +278,7 @@ block hero-class block final-code +makeTabs(`displaying-data/ts/app/app.component.ts, displaying-data/ts/app/hero.ts, + displaying-data/ts/app/app.module.ts, displaying-data/ts/app/main.ts`, - 'final,,', - 'app/app.component.ts, app/hero.ts, main.ts') + 'final,,,', + 'app/app.component.ts, app/hero.ts, app.module.ts, main.ts') diff --git a/public/docs/ts/latest/guide/forms-deprecated.jade b/public/docs/ts/latest/guide/forms-deprecated.jade index f29cb3087a..ad2d70a669 100644 --- a/public/docs/ts/latest/guide/forms-deprecated.jade +++ b/public/docs/ts/latest/guide/forms-deprecated.jade @@ -2,7 +2,7 @@ include ../_util-fns .alert.is-important :marked - This guide is using the deprecated forms API. + This guide is using the deprecated forms API, which is disabled as of RC5, thus this sample only works up to RC4. We have created a new version using the new API here. diff --git a/public/docs/ts/latest/guide/forms.jade b/public/docs/ts/latest/guide/forms.jade index 90facf5e1f..dbe9b8ded6 100644 --- a/public/docs/ts/latest/guide/forms.jade +++ b/public/docs/ts/latest/guide/forms.jade @@ -1,15 +1,7 @@ include ../_util-fns -.alert.is-important - :marked - This guide is using the new forms API. To use this API, you must opt in by adding special - providers to your bootstrap file (see the Bootstrap seection below). - - The old forms API is deprecated, but we still maintain a separate version of the guide using - the deprecated forms API here. - :marked - We’ve all used a form to login, submit a help request, place an order, book a flight, + We’ve all used a form to log in, submit a help request, place an order, book a flight, schedule a meeting and perform countless other data entry tasks. Forms are the mainstay of business applications. @@ -23,38 +15,22 @@ include ../_util-fns **two-way data binding, change tracking, validation, and error handling** ... which we shall cover in this chapter on Angular forms. - We will build a simple form from scratch, one step at a time. Along the way we'll learn + We will build a simple form from scratch, one step at a time. Along the way we'll learn how to - - to build an Angular form with a component and template + - build an Angular form with a component and template - - two-way data binding with `[(ngModel)]` syntax for reading and writing values to input controls + - two-way data bind with `[(ngModel)]` syntax for reading and writing values to input controls - - using `ngModel` in combination with a form lets us track the change state and validity of form controls + - track the change state and validity of form controls using `ngModel` in combination with a form - - special CSS classes that follow the state of the controls and can be used to provide strong visual feedback + - provide strong visual feedback using special CSS classes that track the state of the controls - - displaying validation errors to users and enable/disable form controls + - display validation errors to users and enable/disable form controls - - sharing information among controls with template reference variables + - use [template reference variables](./template-syntax.html#ref-vars) for sharing information among HTML elements Live Example -.l-main-section -:marked - ## Bootstrap - - We start by showing how to bootstrap the application and add the necessary dependencies to use forms. - - During bootstrap we have to register the new forms module by calling `provideForms()` and pass the result to the provider array. -+makeExample('forms/ts/app/main.ts','','app/main.ts') - -:marked - The old forms API is going through a deprecation phase. During this transition Angular is supporting both form modules. - - To remind us that the old API is deprecated, Angular will print a warning message to the console. - - Since we are converting to the new API, and no longer need the old API, we call `disableDeprecatedForms()` to disable the old form functionality and the warning message. - .l-main-section :marked ## Template-Driven Forms @@ -101,7 +77,7 @@ figure.image-display 1. Create the component that controls the form 1. Create a template with the initial form layout 1. Bind data properties to each form input control with the `ngModel` two-way data binding syntax - 1. Add the **#name** attribute to each form input control + 1. Add the `name` attribute to each form input control 1. Add custom CSS to provide visual feedback 1. Show and hide validation error messages 1. Handle form submission with **ngSubmit** @@ -137,7 +113,8 @@ include ../_quickstart_repo We can create a new hero like this: code-example(format=""). let myHero = new Hero(42, 'SkyDog', - 'Fetch any object at any distance', 'Leslie Rollover'); + 'Fetch any object at any distance', + 'Leslie Rollover'); console.log('My hero is called ' + myHero.name); // "My hero is called SkyDog" :marked @@ -156,13 +133,15 @@ code-example(format=""). :marked There’s nothing special about this component, nothing form-specific, nothing to distinguish it from any component we've written before. - Understanding this component requires only the Angular 2 concepts we’ve learned in previous chapters + Understanding this component requires only the Angular concepts we’ve learned in previous chapters 1. We import the `Component` decorator from the Angular library as we usually do. + 1. We import the `Hero` model we just created. + 1. The `@Component` selector value of "hero-form" means we can drop this form in a parent template with a `` tag. - 1. The `templateUrl` property points to a separate file for template HTML called `hero-form.component.html`. + 1. The `templateUrl` property points to a separate file for the template HTML called `hero-form.component.html`. 1. We defined dummy data for `model` and `powers` as befits a demo. Down the road, we can inject a data service to get and save real data @@ -182,7 +161,38 @@ code-example(format=""). We made a good choice to put the HTML template elsewhere. We'll write that template in a moment. Before we do, we'll take a step back - and revise the `app.component.ts` to make use of our new `HeroFormComponent`. + and revise the `app.module.ts` and `app.component.ts` to make use of our new `HeroFormComponent`. + +.l-main-section +:marked + ## Revise the *app.module.ts* + + `app.module.ts` defines the application's root module. In it we identify the external modules we'll use in our application + and declare the components that belong to this module, such as our `HeroFormComponent`. + + Because template-driven forms are in their own module, we need to add the `FormsModule` to the array of + `imports` for our application module before we can use forms. + + Replace the contents of the "QuickStart" version with the following: ++makeExample('forms/ts/app/app.module.ts', null, 'app/app.module.ts') + +:marked +.l-sub-section + :marked + There are three changes: + + 1. We import `FormsModule` and our new `HeroFormComponent`. + + 1. We add the `FormsModule` to the list of `imports` defined in the `ngModule` decorator. This gives our application + access to all of the template-driven forms features, including `ngModel`. + + 1. We add the `HeroFormComponent` to the list of `declarations` defined in the `ngModule` decorator. This makes + the `HeroFormComponent` component visible throughout this module. + +.alert.is-important + :marked + If a component, directive, or pipe belongs to a module in the `imports` array, ​_DON'T_​ declare it in the `declarations` array. + If you wrote it and it should belong to this module, ​_DO_​ declare it in the `declarations` array. .l-main-section :marked @@ -196,14 +206,10 @@ code-example(format=""). :marked .l-sub-section :marked - There are only three changes: - - 1. We import the new `HeroFormComponent`. + There is only one changes: 1. The `template` is simply the new element tag identified by the component's `selector` property. - - 1. The `directives` array tells Angular that our template depends upon the `HeroFormComponent` - which is itself a Directive (as are all Components). + This will display the hero form when the application component is loaded. .l-main-section :marked @@ -220,11 +226,11 @@ code-example(format=""). The *Name* `` control has the HTML5 `required` attribute; the *Alter Ego* `` control does not because `alterEgo` is optional. - We've got a *Submit* button at the bottom with some classes on it. + We've got a *Submit* button at the bottom with some classes on it for styling. **We are not using Angular yet**. There are no bindings. No extra directives. Just layout. - The `container`,`form-group`, `form-control`, and `btn` classes + The `container`, `form-group`, `form-control`, and `btn` classes come from [Twitter Bootstrap](http://getbootstrap.com/css/). Purely cosmetic. We're using Bootstrap to gussy up our form. Hey, what's a form without a little style! @@ -295,7 +301,6 @@ figure.image-display so we can see what we're doing. We left ourselves a note to throw it away when we're done. - :marked Focus on the binding syntax: `[(ngModel)]="..."`. @@ -309,18 +314,33 @@ figure.image-display The diagnostic is evidence that we really are flowing values from the input box to the model and back again. **That's two-way data binding!** - Notice that we also added a `name` attribute to our `` tag. This is a requirement when using `[(ngModel)]` in combination with a form, so that we can easily refer to it in the aggregate form value and validity state. + Notice that we also added a `name` attribute to our `` tag and set it to "name" + which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful. + Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form. - Let's add similar `[(ngModel)]` bindings to *Alter Ego* and *Hero Power*. +.l-sub-section + :marked + Internally Angular creates `FormControls` and registers them with an `NgForm` directive that Angular + attached to the `
    ` tag. Each `FormControl` is registered under the name we assigned to the `name` attribute. + We'll talk about `NgForm` [later in this chapter](#ngForm). + +:marked + Let's add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*. We'll ditch the input box binding message and add a new binding at the top to the component's `diagnostic` property. Then we can confirm that two-way data binding works *for the entire Hero model*. - After revision the core of our form should have three `[(ngModel)]` bindings that + After revision the core of our form should have three `[(ngModel)]` bindings and `name` attributes that look much like this: +makeExample('forms/ts/app/hero-form.component.html', 'ngModel-2', 'app/hero-form.component.html (excerpt)') +.l-sub-section + :marked + - Each input element has an `id` property that is used by the `label` element's `for` attribute + to match the label to it's input control. + - Each input element has a `name` property that is required by Angular Forms to register the control with the form. + :marked If we ran the app right now and changed every Hero model property, the form might display like this: figure.image-display @@ -382,28 +402,9 @@ figure.image-display Using `ngModel` in a form gives us more than just two way data binding. It also tells us if the user touched the control, if the value changed, or if the value became invalid. - `ngModel` doesn't just track state; it updates the control with special Angular CSS classes from the set we listed above. + The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state. We can leverage those class names to change the appearance of the control and make messages appear or disappear. - - We'll explore those effects soon. Right now - let's make sure we have `ngModel` and the corresponding name attribute on all three form controls, - starting with the *Name* input box. -+makeExample('forms/ts/app/hero-form.component.html', 'ngModelName-1', 'app/hero-form.component.html (excerpt)')(format=".") -:marked - We set the `name` attribute to "name" which makes sense for our app. Any unique value will do. - -.l-sub-section - :marked - Internally Angular creates `FormControls` and registers them with an `NgForm` directive that Angular attached to the `` tag. Each `FormControl` is registered under the name we assigned to the `name` attribute. - We'll talk about `NgForm` [later in the chapter](#ngForm). - -.l-main-section -:marked - ## Add Custom CSS for Visual Feedback - - The *NgModel* directive doesn't just track state. - It updates the control with three classes that reflect the state. table tr @@ -448,8 +449,15 @@ figure.image-display :marked The (`ng-valid` | `ng-invalid`) pair are most interesting to us. We want to send a strong visual signal when the data are invalid and we want to mark required fields. + So we add custom CSS for visual feedback. - We realize we can do both at the same time with a colored bar on the left of the input box: + **Delete** the `#spy` template reference variable and `TODO` as they have served their purpose. + +.l-main-section +:marked + ## Add Custom CSS for Visual Feedback + We realize we can mark required fields and invalid data at the same time with a colored bar + on the left of the input box: figure.image-display img(src="/resources/images/devguide/forms/validity-required-indicator.png" width="400px" alt="Invalid Form") @@ -597,7 +605,7 @@ figure.image-display Angular did. Angular creates and attaches an `NgForm` directive to the `` tag automatically. - The `NgForm` directive supplements the `form` element with additional features. + The `NgForm` directive supplements the `form` element with additional features. It holds the controls we created for the elements with `ngModel` directive and `name` attribute and monitors their properties including their validity. It also has its own `valid` property which is true only *if every contained @@ -671,13 +679,14 @@ figure.image-display :marked ## Conclusion - The Angular 2 form discussed in this chapter takes advantage of the following framework features to provide support for data modification, validation and more: + The Angular form techniques discussed in this chapter take + advantage of the following framework features to provide support for data modification, validation and more: - An Angular HTML form template. - A form component class with a `Component` decorator. - The `ngSubmit` directive for handling the form submission. - - Template reference variables such as `#heroForm`, `#name`, `#alter-ego` and `#power`. - - The `[(ngModel)]` syntax for two-way data binding, validation and change tracking. + - Template reference variables such as `#heroForm`, `#name` and `#power`. + - The `[(ngModel)]` syntax and a `name` attribute for two-way data binding, validation and change tracking. - The reference variable’s `valid` property on input controls to check if a control is valid and show/hide error messages. - Controlling the submit button's enabled state by binding to `NgForm` validity. - Custom CSS classes that provide visual feedback to users about invalid controls. @@ -689,6 +698,7 @@ figure.image-display .file app .children .file app.component.ts + .file app.module.ts .file hero.ts .file hero-form.component.html .file hero-form.component.ts @@ -706,6 +716,7 @@ figure.image-display `forms/ts/app/hero-form.component.ts, forms/ts/app/hero-form.component.html, forms/ts/app/hero.ts, + forms/ts/app/app.module.ts, forms/ts/app/app.component.ts, forms/ts/app/main.ts, forms/ts/index.html, @@ -714,6 +725,7 @@ figure.image-display `hero-form.component.ts, hero-form.component.html, hero.ts, + app.module.ts, app.component.ts, main.ts, index.html, diff --git a/public/docs/ts/latest/guide/hierarchical-dependency-injection.jade b/public/docs/ts/latest/guide/hierarchical-dependency-injection.jade index 6fcf422eda..84e0c6ab3f 100644 --- a/public/docs/ts/latest/guide/hierarchical-dependency-injection.jade +++ b/public/docs/ts/latest/guide/hierarchical-dependency-injection.jade @@ -69,8 +69,8 @@ figure.image-display Such a proliferation of injectors makes little sense until we consider the possibility that injectors at different levels can be configured with different providers. We don't *have* to re-configure providers at every level. But we *can*. - If we don't re-configure, the tree of injectors appears to be flat. All requests bubble up to the root injector that we - configured with the `bootstrap` method. + If we don't re-configure, the tree of injectors appears to be flat. All requests bubble up to the root NgModule injector that we + configured with the `bootstrapModule` method. The ability to configure one or more providers at different levels opens up interesting and useful possibilities. @@ -142,9 +142,9 @@ figure.image-display +makeExample('hierarchical-dependency-injection/ts/app/hero-editor.component.ts', 'providers') :marked This adds a `RestoreService` provider to the injector of the `HeroEditComponent`. - Couldn’t we simply alter our bootstrap call to this? + Couldn’t we simply alter our root NgModule to include this provider? -+makeExample('hierarchical-dependency-injection/ts/app/main.ts', 'bad-alternative') ++makeExample('hierarchical-dependency-injection/ts/app/app.module.ts', 'bad-alternative') :marked Technically we could, but our component wouldn’t quite behave the way it is supposed to. Remember that each injector treats the services that it provides as singletons. However, in order to be able to have multiple instances of `HeroEditComponent` edit multiple heroes at the same time we need to have multiple instances of the `RestoreService`. More specifically, each instance of `HeroEditComponent` needs to be bound to its own instance of the `RestoreService`. diff --git a/public/docs/ts/latest/guide/pipes.jade b/public/docs/ts/latest/guide/pipes.jade index 3d50ef7f42..0308d8c1de 100644 --- a/public/docs/ts/latest/guide/pipes.jade +++ b/public/docs/ts/latest/guide/pipes.jade @@ -166,10 +166,10 @@ figure.image-display Two things to note: 1. We use our custom pipe the same way we use the built-in pipes. - 1. We must include our pipe in the `pipes` #{_array} of the `@Component` #{_decorator}. + 1. We must include our pipe in the `declarations` #{_array} of the AppModule. .callout.is-helpful - header Remember the pipes #{_array}! + header Remember the declarations #{_array}! :marked Angular reports an error if we neglect to list our custom pipe. We didn't list the `DatePipe` in our previous example because all @@ -334,9 +334,9 @@ block pure-change +makeExample('pipes/ts/app/flying-heroes.pipe.ts','filter')(format='.') We can derive a `FlyingHeroesImpureComponent` that we derive from the `FlyingHeroesComponent`. -+makeExample('pipes/ts/app/flying-heroes.component.ts','impure-component','app/flying-heroes.component.ts (FlyingHeroesImpureComponent)')(format='.') ++makeExample('pipes/ts/app/flying-heroes-impure.component.html','template-flying-heroes','app/flying-heroes-impure.component.html (FlyingHeroesImpureComponent)')(format='.') :marked - The only substantive change is the pipe. + The only substantive change is the pipe in the template. We can confirm in the that the _flying heroes_ display updates as we enter new heroes even when we mutate the `heroes` #{_array}. diff --git a/public/docs/ts/latest/guide/router.jade b/public/docs/ts/latest/guide/router.jade index 1ae824f6bd..9e5c5372af 100644 --- a/public/docs/ts/latest/guide/router.jade +++ b/public/docs/ts/latest/guide/router.jade @@ -47,14 +47,16 @@ include ../_util-fns * navigating under [program control](#navigate) * toggling css classes for the [active router link](#router-link-active) * embedding critical information in the URL with [route parameters](#route-parameters) + * providing non-critical information in [optional route parameters](#optional-route-parameters) * add [child routes](#child-routing-component) under a feature section * [redirecting](#redirect) from one route to another * confirming or canceling navigation with [guards](#guards) * [CanActivate](#can-activate-guard) to prevent navigation to a route * [CanDeactivate](#can-deactivate-guard) to prevent navigation away from the current route - * passing optional information in [query parameters](#query-parameters) - * persisting information across routes with [global query parameters](#global-query-parameters) + * [Resolve](#resolve-guard) to pre-fetch data before activating a route + * providing optional information across routes with [query parameters](#query-parameters) * jumping to anchor elements using a [fragment](#fragment) + * loading feature areas [asynchronously](#asynchronous-routing) * choosing the "HTML5" or "hash" [URL style](#browser-url-styles) We proceed in phases marked by milestones building from a simple two-pager with placeholder views @@ -83,7 +85,7 @@ include ../_util-fns It is not part of the Angular 2 core. It is in its own library package, `@angular/router`. We import what we need from it as we would from any other Angular package. -+makeExample('router/ts/app/app.component.1.ts','import-router', 'app/app.component.ts (import)')(format=".") ++makeExample('router/ts/app/app.routing.ts','import-router', 'app/app.routing.ts (import)')(format=".") .l-sub-section :marked We cover other options in the [details below](#browser-url-styles). @@ -93,19 +95,24 @@ include ../_util-fns from which it can determine the component to display. A router has no routes until we configure it. - The preferred way is to bootstrap our application with an array of routes using the **`provideRouter`** function. + We bootstrap our application with an array of routes that we'll provide to our **`RouterModule.forRoot`** function. In the following example, we configure our application with four route definitions. -+makeExample('router/ts/app/app.routes.1.ts','route-config','app/app.routes.ts')(format='.') ++makeExample('router/ts/app/app.routing.1.ts','route-config','app/app.routing.ts')(format='.') .l-sub-section :marked - The `RouterConfig` is an array of *routes* that describe how to navigate. + The `Routes` is an array of *routes* that describe how to navigate. Each *Route* maps a URL `path` to a component. There are no **leading slashes** in our **path**. The router parses and builds the URL for us, allowing us to use relative and absolute paths when navigating between application views. + The `data` property in the second route is a place to store arbitrary data associated to each + specific route. This data is accessible with each activated route and can be used to store + items such as page titles, breadcrumb text and other read-only data. We'll use the [resolve guard](#resolve-guard) + to retrieve additional data later in the chapter. + The `:id` in the third route is a token for a route parameter. In a URL such as `/hero/42`, "42" is the value of the `id` parameter. The corresponding `HeroDetailComponent` will use that value to find and present the hero whose `id` is 42. @@ -115,15 +122,12 @@ include ../_util-fns if the URL requested doesn't match any paths for routes defined in our configuration. This is useful for displaying a 404 page or redirecting to another route. - We pass the configuration array to the `provideRouter()` function which returns - (among other things) a configured *Router* [service provider](dependency-injection.html#!#injector-providers). - - Finally, we export this provider in the `appRouterProviders` array - so we can simplify registration of router dependencies later in `main.ts`. - We don't have any other providers to register right now. But we will. :marked - Next we open `main.ts` where we must register our router providers in the `bootstrap` method. -+makeExample('router/ts/app/main.ts','','app/main.ts')(format='.') + We export the `routing` constant so we can import it into our `app.module.ts` file where we'll add + a configured *Router* module to our root NgModule imports. +:marked + Next we open `app.module.ts` where we must register our routing, routing providers, and declare our two route components. ++makeExample('router/ts/app/app.module.1.ts','router-basics','app/app.module.ts (basic setup)')(format='.') :marked ### Router Outlet Given this configuration, when the browser URL for this application becomes `/heroes`, @@ -171,9 +175,7 @@ code-example(format="", language="html"). application using the `Router` service and the `routerState` property. The router state provides us with methods to traverse up and down the route tree from any activated route - to get information we may need from parent, child and sibling routes. It also contains the URL *fragment* - and *query parameters* which are **global** to all routes. We'll use the `RouterState` to access - [Query Parameters](#query-parameters). + to get information we may need from parent, child and sibling routes. :marked ### Let's summarize @@ -193,9 +195,14 @@ table Displays the application component for the active URL. Manages navigation from one component to the next. tr - td RouterConfig + td RouterModule td. - Contains an array of Routes, each mapping a URL path to a component. + A separate Angular module that provides the necessary service providers + and directives for navigating through application views. + tr + td Routes + td. + Defines an array of Routes, each mapping a URL path to a component. tr td Route td. @@ -216,12 +223,16 @@ table td. The directive for adding/removing classes from an HTML element when an associated routerLink contained on or inside the element becomes active/inactive. + tr + td ActivatedRoute + td. + A service that is provided to each route component that contains route specific + information such as route parameters, static data, resolve data, global query params and the global fragment. tr td RouterState td. The current state of the router including a tree of the currently activated - routes in our application along with the URL query params, fragment - and convenience methods for traversing the route tree. + routes in our application along convenience methods for traversing the route tree. tr td Link Parameters Array td. @@ -312,11 +323,14 @@ figure.image-display :marked This app illustrates the router features we'll cover in this chapter + * organizing the application features into modules * navigating to a component (*Heroes* link to "Heroes List") * including a route parameter (passing the Hero `id` while routing to the "Hero Detail") * child routes (the *Crisis Center* has its own routes) * the `CanActivate` guard (checking route access) * the `CanDeactivate` guard (ask permission to discard unsaved changes) + * the `Resolve` guard (pre-fetching route data) + * lazy loading feature area modules .l-main-section @@ -329,7 +343,6 @@ figure.image-display :marked - ### Set the *<base href>* The Component Router uses the browser's [history.pushState](https://developer.mozilla.org/en-US/docs/Web/API/History_API#Adding_and_modifying_history_entries) @@ -369,29 +382,34 @@ figure.image-display :marked ### Configure the routes for the Router + + We begin by importing some symbols from the router library. + + The Component Router is in its own `@angular/router` package. + It's not part of the Angular 2 core. The router is an optional service because not all applications + need routing and, depending on your requirements, you may need a different routing library. + We teach our router how to navigate by configuring it with routes. - We recommend creating a separate `app.routes.ts` file dedicated to this purpose. + We recommend creating a separate `app.routing.ts` file dedicated to this purpose. .l-sub-section :marked Defining configuration in a separate file paves the way for a future in which we load routing configuration immediately but *delay loading the components themselves* until the user needs them. - Such *asynchronous routing* can make our application launch more quickly. - We'll cover asynchronous routing in a future chapter update. + Such [*asynchronous routing*](#asynchronous-routing) can make our application launch more quickly. :marked - Here is our first configuration. + Here is our first configuration. We pass the array of routes to the `RouterModule.forRoot` method + which returns a module containing the configured `Router` service provider ... and some other, + unseen providers that the routing library requires. We export this as the `routing` token. -+makeExample('router/ts/app/app.routes.2.ts','', 'app/app.routes.ts')(format=".") ++makeExample('router/ts/app/app.routing.2.ts','', 'app/app.routing.ts')(format=".") -h4#import Import from the Component Router library -:marked - We begin by importing some symbols from the router library. - - The Component Router is in its own `@angular/router` package. - It's not part of the Angular 2 core. - The router is an optional service because not all applications need routing and, - depending on your requirements, you may need a different routing library. +.l-sub-section + :marked + We also export an empty `appRoutingProviders` array + so we can simplify registration of router dependencies later in `app.module.ts`. + We don't have any providers to register right now. But we will. a#route-config h4#define-routes Define routes @@ -416,36 +434,23 @@ h4#define-routes Define routes the `CrisisListComponent`, display its view, and update the browser's address location and history with the URL for that path.* -h4#provideRouter Call provideRouter -:marked - We pass the route configuration to the `provideRouter` function which returns an array containing the configured - `Router` service provider ... and some other, unseen providers that the routing library requires. - -:marked - We add the `provideRouter` array to an `appRouterProviders` array and export it. - - We could add *additional* service providers to `appRouterProviders` — - providers that are specific to our routing configuration. - We don't have any yet. We will have some later in this chapter. - .l-sub-section :marked Learn about *providers* in the [Dependency Injection](dependency-injection.html#!#injector-providers) chapter. -h4#register-providers Register routing in bootstrap +h4#register-providers Register routing in the root NgModule :marked - Our app launches from the `main.ts` file in the `/app` folder. - It's short and not much different from the default `main.ts`. + Our app launches from the `app.module.ts` file in the `/app` folder. - The important difference: we import the `appRouterProviders` array - and pass it as the second parameter of the `bootstrap` function. -+makeExample('router/ts/app/main.1.ts','all', 'main.ts')(format=".") + We import the `routing` token we exported from the `app.routing.ts` file and add it to the `imports` array. + + We import our `CrisisListComponent` and `HeroListComponent` components and add them to our *declarations* + so they will be registered within our root NgModule. + + We also import the `appRoutingProviders` array and add it to the `providers` array. ++makeExample('router/ts/app/app.module.1.ts','', 'app.module.ts')(format=".") :marked - Providing the router providers at bootstrap makes the Router available everywhere in our application. -.alert.is-important - :marked - We must register router providers in `bootstrap`. - We cannot wait to do it in `AppComponent`. + Providing the router module in our root NgModule makes the Router available everywhere in our application. h3#shell The AppComponent shell :marked @@ -500,11 +505,10 @@ h3#router-link RouterLinkActive binding To override this behavior, we can bind to the `[routerLinkActiveOptions]` input binding with the `{ exact: true }` expression. By using `{ exact: true }`, a given `RouterLink` will only be active if its URL is an exact match to the current URL. -h3#router-directives ROUTER_DIRECTIVES +h3#router-directives Router Directives :marked - `RouterLink`, `RouterLinkActive` and `RouterOutlet` are directives in the `ROUTER_DIRECTIVES` collection. - Remember to add them to the `directives` array of the `@Component` metadata. -+makeExample('router/ts/app/app.component.1.ts','directives')(format=".") + `RouterLink`, `RouterLinkActive` and `RouterOutlet` are directives provided by the Angular `RouterModule` package. + They are readily available for us to use in our template. :marked The current state of `app.component.ts` looks like this: +makeExample('router/ts/app/app.component.1.ts','', 'app/app.component.ts')(format=".") @@ -518,8 +522,8 @@ h3#router-directives ROUTER_DIRECTIVES We've learned how to * load the router library * add a nav bar to the shell template with anchor tags, `routerLink` and `routerLinkActive` directives - * added a `router-outlet` to the shell template where views will be displayed - * configure the router with `provideRouter` + * add a `router-outlet` to the shell template where views will be displayed + * configure the router module with `RouterModule.forRoot` * set the router to compose "HTML 5" browser URLs. The rest of the starter app is mundane, with little interest from a router perspective. @@ -532,7 +536,8 @@ h3#router-directives ROUTER_DIRECTIVES .file app .children .file app.component.ts - .file app.routes.ts + .file app.module.ts + .file app.routing.ts .file crisis-list.component.ts .file hero-list.component.ts .file main.ts @@ -547,14 +552,16 @@ h3#router-directives ROUTER_DIRECTIVES Here are the files discussed in this milestone +makeTabs( `router/ts/app/app.component.1.ts, - router/ts/app/app.routes.2.ts, - router/ts/app/main.1.ts, + router/ts/app/app.module.1.ts, + router/ts/app/app.routing.2.ts, + router/ts/app/main.ts, router/ts/app/hero-list.component.ts, router/ts/app/crisis-list.component.ts, router/ts/index.html`, - ',,all,,', + ',,,,', `app.component.ts, - app.routes.ts, + app.module.ts, + app.routing.ts, main.ts, hero-list.component.ts, crisis-list.component.ts, @@ -566,7 +573,7 @@ h2#heroes-feature Milestone #2: The Heroes Feature We've seen how to navigate using the `RouterLink` directive. Now we'll learn some new tricks such as how to - * organize our app into *feature areas* + * organize our app and routes into *feature areas* using modules * navigate imperatively from one component to another * pass information in route parameters @@ -593,6 +600,10 @@ figure.image-display :marked ### Add Heroes functionality + We want to break our app out into different *submodules* that we then import + into our main module so it can make use of them. First, we'll create a `heroes.module.ts` + in our heroes folder. + We delete the placeholder `hero-list.component.ts` that's in the `app/` folder. @@ -601,7 +612,14 @@ figure.image-display We copy the `hero-detail.component.ts` and the `hero.service.ts` files into the `heroes/` folder. - When we're done organizing, we have three *Hero Management* files: + We provide the `HeroService` in the `providers` array of our `Heroes` module + so its available to all components within our module. + + Our `Heroes` module is ready for routing. ++makeExample('router/ts/app/heroes/heroes.module.1.ts','', 'app/heroes/heroes.module.ts')(format=".") + +:marked + When we're done organizing, we have four *Hero Management* files: .filetree .file app/heroes @@ -609,11 +627,9 @@ figure.image-display .file hero-detail.component.ts .file hero-list.component.ts .file hero.service.ts + .file heroes.module.ts :marked - We provide the `HeroService` in the application root `AppComponent` - so that it is available everywhere in the app. - Now it's time for some surgery to bring these files and the rest of the app into alignment with our application router. @@ -633,17 +649,29 @@ figure.image-display We recommend giving each feature area its own route configuration file. - Create a new `hero.routes.ts` in the `heroes` folder like this: -+makeExample('router/ts/app/heroes/heroes.routes.ts','', 'app/heroes/heroes.routes.ts')(format=".") + Create a new `heroes.routing.ts` in the `heroes` folder like this: ++makeExample('router/ts/app/heroes/heroes.routing.ts','', 'app/heroes/heroes.routing.ts')(format=".") :marked - We use the same techniques we learned for `app.routes.ts`. + We use the same techniques we learned for `app.routing.ts`. We import the two components from their new locations in the `app/heroes/` folder, define the two hero routes. - and add them to an exported `HeroesRoutes` array. + and add export our `heroesRouting` that returns configured `RouterModule` for our submodule. +:marked + Now that we have routes for our `Heroes` module, we'll need to register them with the *Router*. + We'll import the *RouterModule* like we did in the root NgModule, but there is a slight difference here. + In our root routing setup, we used the static **forRoot** method to register our routes and application level + service providers. Since we are in a submodule, we'll use **Router.forChild** method to only register additional routes. We do this + because the *Router* will combine all the provided routes from the submodule together and build our configuration. This allows us to continue defining + our feature-specific routes without modifying our main route configuration. + + We import our `heroesRouting` token from `heroes.routing.ts` into our `Heroes` module and register the routing. ++makeExample('router/ts/app/heroes/heroes.module.ts', 'heroes-routes', 'app/heroes/heroes.module.ts (Heroes routing)')(format=".") + +:marked ### Route definition with a parameter The route to `HeroDetailComponent` has a twist. -+makeExample('router/ts/app/heroes/heroes.routes.ts','hero-detail-route')(format=".") ++makeExample('router/ts/app/heroes/heroes.routing.ts','hero-detail-route')(format=".") :marked Notice the `:id` token in the path. That creates a slot in the path for a **Route Parameter**. In this case, we're expecting the router to insert the `id` of a hero into that slot. @@ -657,29 +685,13 @@ code-example(format="." language="bash"). pattern and go to the same "Magneta" detail view. .l-sub-section :marked - #### Route parameter or query parameter? + #### Route parameter: Required or optional? Embedding the route parameter token, `:id`, in the route definition path is a good choice for our scenario because the `id` is *required* by the `HeroDetailComponent` and because the value `15` in the path clearly distinguishes the route to "Magneta" from a route for some other hero. - A [query parameter](#query-parameter) might be a better choice if we were passing an *optional* value to `HeroDetailComponent`. - -h3#merge-hero-routes Merge hero routes into application routes -:marked - Our application doesn't know about our hero routes yet. - We'll need to merge them into the application routes we defined in `app.routes.ts`. - - Update `app.routes.ts` as follows: -+makeExample('router/ts/app/app.routes.3.ts', '', 'app/app.routes.ts (v.2)')(format=".") -:marked - We replace the `HeroListComponent` import with an `HeroesRoutes` import. - - We *flatten* the `HeroesRoutes` into the `routes` array with the ES6 *spread operator* (`...`). - - As a result, the `app.routes.ts` no longer has specific knowledge of the hero feature, its components, or its route details. - It won't change as we evolve the hero feature with more components and different routes. - That's a key benefit of creating a separate route configuration for each feature area. + An [optional-route-parameter](#optional-route-parameter) might be a better choice if we were passing an *optional* value to `HeroDetailComponent`. h3#navigate Navigate to hero detail imperatively :marked @@ -811,6 +823,31 @@ h3#nav-to-list Navigating back to the list component that we can bind to a `[routerLink]` directive. It holds the **path to the `HeroListComponent`**: +makeExample('router/ts/app/heroes/hero-detail.component.1.ts','gotoHeroes')(format=".") + +h3#merge-hero-routes Import hero module into root NgModule +:marked + Our heroes feature is ready, but application doesn't know about our heroes module yet. + We'll need to import it into the root NgModule we defined in `app.module.ts`. + + Update `app.module.ts` as follows: + ++makeExample('router/ts/app/app.module.2.ts','hero-import', 'app.module.ts (Heroes module import)')(format=".") + +:marked + We imported the `HeroesModule` and added it to our root NgModule `imports`. + + We removed the `HeroListComponent` from the root NgModule's `declarations` because its being provided by the `HeroesModule` + now. This is important because their can be only **one** owner for a declared component. In our case, the `Heroes` module is + the owner of the `Heroes` components and is making them available to the root NgModule. + + As a result, the `app.module.ts` no longer has specific knowledge of the hero feature, its components, or its route details. + We can evolve the hero feature with more components and different routes. + That's a key benefit of creating a separate module for each feature area. + + Since our `Heroes` routes are defined within our submodule, we can also remove our initial `heroes` route from the `app.routing.ts`. + ++makeExample('router/ts/app/app.routing.3.ts','', 'app.routing.ts (v.2)')(format=".") + :marked ### Heroes App Wrap-up @@ -820,6 +857,7 @@ h3#nav-to-list Navigating back to the list component * organize our app into *feature areas* * navigate imperatively from one component to another * pass information along in route parameters and subscribe to them in our component + * import our feature area NgModule into our root NgModule After these changes, the folder structure looks like this: .filetree @@ -832,9 +870,11 @@ h3#nav-to-list Navigating back to the list component .file hero-detail.component.ts .file hero-list.component.ts .file hero.service.ts - .file heroes.routes.ts + .file heroes.module.ts + .file heroes.routing.ts .file app.component.ts - .file app.routes.ts + .file app.module.ts + .file app.routing.ts .file crisis-list.component.ts .file main.ts .file node_modules ... @@ -850,18 +890,22 @@ h3#nav-to-list Navigating back to the list component Here are the relevant files for this version of the sample application. +makeTabs( `router/ts/app/app.component.1.ts, - router/ts/app/app.routes.3.ts, + router/ts/app/app.module.2.ts, + router/ts/app/app.routing.3.ts, router/ts/app/heroes/hero-list.component.1.ts, router/ts/app/heroes/hero-detail.component.1.ts, router/ts/app/heroes/hero.service.ts, - router/ts/app/heroes/heroes.routes.ts`, + router/ts/app/heroes/heroes.module.ts, + router/ts/app/heroes/heroes.routing.ts`, null, `app.component.ts, - app.routes.ts, + app.module.ts, + app.routing.ts, hero-list.component.ts, hero-detail.component.ts, hero.service.ts, - heroes.routes.ts`) + heroes.module.ts, + heroes.routing.ts`) :marked @@ -870,7 +914,7 @@ h3#nav-to-list Navigating back to the list component ## Milestone #3: The Crisis Center The *Crisis Center* is a fake view at the moment. Time to make it useful. - The new *Crisis Center* begins as a virtual copy of the *Heroes* feature. + The new *Crisis Center* begins as a virtual copy of the *Heroes* module. We create a new `app/crisis-center` folder, copy the Hero files, and change every mention of "hero" to "crisis". @@ -879,7 +923,7 @@ h3#nav-to-list Navigating back to the list component When the user selects a crisis, the app navigates to the `CrisisDetailComponent` for display and editing of the crisis name. - Voilà, instant feature module! + Voilà, another feature module! There's no point to this exercise unless we can learn something. We do have new ideas and techniques in mind: @@ -894,10 +938,10 @@ h3#nav-to-list Navigating back to the list component * The router should block access to certain features until the user logs-in. - * Our `CrisisService` is only needed within the *Crisis Center* feature area. - We should limit access to it to that feature area. + * Our `CrisisService` is only needed within the *Crisis Center* module. + We should limit access to it to that module. - * Changes to a sub-module such as *Crisis Center* shouldn't provoke changes to the `AppComponent` or + * Changes to a submodule such as *Crisis Center* shouldn't provoke changes to the root `NgModule` or any other feature's component. We need to [*separate our concerns*](https://blog.8thlight.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html). @@ -913,7 +957,7 @@ h3#nav-to-list Navigating back to the list component ### A Crisis Center with child routes We'll organize the *Crisis Center* to conform to the following recommended pattern for Angular applications. - * each feature area in its own folder + * each feature area in its own folder within a defined module * each area with its own area root component * each area root component with its own router-outlet and child routes * area routes rarely (if ever) cross @@ -926,7 +970,7 @@ figure.image-display h3#child-routing-component Child Routing Component :marked Add the following `crisis-center.component.ts` to the `crisis-center` folder: -+makeExample('router/ts/app/crisis-center/crisis-center.component.1.ts', 'minus-imports', 'crisis-center/crisis-center.component.ts (minus imports)')(format='.') ++makeExample('router/ts/app/crisis-center/crisis-center.component.ts', 'minus-imports', 'crisis-center/crisis-center.component.ts (minus imports)')(format='.') :marked The `CrisisCenterComponent` is much like the `AppComponent` shell. @@ -950,14 +994,14 @@ h3#child-routing-component Child Routing Component :marked ### Service isolation - The`CrisisService` is neither needed nor wanted outside the *Crisis Center* domain. - Instead of registering it with the root `AppComponent` providers — + The `CrisisService` is neither needed nor wanted outside the *Crisis Center* domain. + Instead of registering it with the root NgModule's providers — which makes it visible everywhere — - we register the `CrisisService` in the component's providers array. -+makeExample('router/ts/app/crisis-center/crisis-center.component.1.ts', 'providers')(format='.') + we register the `CrisisService` in the `CrisisCenterModule` providers array. ++makeExample('router/ts/app/crisis-center/crisis-center.module.1.ts', 'providers')(format='.') :marked - This limits the scope of the `CrisisService` to the *Crisis Center* component and its sub-component tree. - No component outside of the *Crisis Center* can access it. + This limits the scope of the `CrisisService` to the *Crisis Center* routes. + No module outside of the *Crisis Center* can access it. There's a practical benefit to restricting its scope in this way. @@ -967,20 +1011,17 @@ h3#child-routing-component Child Routing Component Second, we can delay loading this service into memory until we need it. We can remove it from the application launch bundle, reducing the size of the initial payload and improving performance. - We can load it optionally, asynchronously with the other *Crisis Center* components + We can load it optionally, [asynchronously](#asynchronous-routing) with the other *Crisis Center* components if and when the user begins that workflow. -.l-sub-section - :marked - We'll describe asynchronous module loading in a future update. :marked ### Child Route Configuration The `CrisisCenterComponent` is a *Routing Component* like the `AppComponent`. It has its own `RouterOutlet` and its own child routes. - We create a `crisis-center.routes.ts` file as we did the `heroes.routes.ts` file. + We create a `crisis-center.routing.ts` file as we did the `heroes.routing.ts` file. But this time we define **child routes** *within* the parent `crisis-center` route. -+makeExample('router/ts/app/crisis-center/crisis-center.routes.1.ts', 'routes', 'app/crisis-center/crisis-center.routes.ts (Routes)' )(format='.') ++makeExample('router/ts/app/crisis-center/crisis-center.routing.1.ts', 'routes', 'app/crisis-center/crisis-center.routing.ts (Routes)' )(format='.') :marked Notice that the parent `crisis-center` route has a `children` property with an array of two routes. @@ -1007,16 +1048,19 @@ code-example(format=""). localhost:3000/crisis-center/2 :marked - Here's the complete `crisis-center.routes.ts` with its imports. -+makeExample('router/ts/app/crisis-center/crisis-center.routes.1.ts', '', 'app/crisis-center/crisis-center.routes.ts' )(format='.') + Here's the complete `crisis-center.routing.ts` with its imports. ++makeExample('router/ts/app/crisis-center/crisis-center.routing.1.ts', '', 'app/crisis-center/crisis-center.routing.ts' )(format='.') -h3#merge-crisis-routes Merge crisis routes into the application routes +h3#import-crisis-module Import crisis center module into the root NgModule routes :marked - As with hero routes, we must update the router configuration at the top of the application - by merging the crisis routes into the app routes: -+makeExample('router/ts/app/app.routes.4.ts', '', 'app/app.routes.ts' )(format='.') + As with the `Heroes` module, we must import the `Crisis Center` module into the root NgModule: ++makeExample('router/ts/app/app.module.3.ts', '', 'app/app.module.ts (Crisis Center Module)' )(format='.') :marked - We used the spread operator again (...) to insert the crisis routes array. + We also remove the initial crisis center route from our `app.routing.ts`. Our routes + are now being provided by our `HeroesModule` and our `CrisisCenter` submodules. We'll keep our `app.routing.ts` file + for general routes which we'll cover later in the chapter. + ++makeExample('router/ts/app/app.routing.4.ts', '', 'app/app.routing.ts (v.3)' )(format='.') a#redirect h3#redirect Redirecting routes @@ -1033,7 +1077,7 @@ code-example(format=""). The preferred solution is to add a `redirect` route that transparently translates from the initial relative URL (`''`) to the desired default path (`/crisis-center`): -+makeExample('router/ts/app/crisis-center/crisis-center.routes.2.ts', 'redirect', 'app/crisis-center/crisis-center.routes.ts (redirect route)' )(format='.') ++makeExample('router/ts/app/crisis-center/crisis-center.routing.2.ts', 'redirect', 'app/crisis-center/crisis-center.routing.ts (redirect route)' )(format='.') :marked A redirect route requires a `pathMatch` property to tell the router how to match a URL to the path of a route. @@ -1063,7 +1107,7 @@ code-example(format=""). :marked The updated route definitions look like this: -+makeExample('router/ts/app/crisis-center/crisis-center.routes.2.ts', 'routes', 'app/crisis-center/crisis-center.routes.ts (Routes v.2)' )(format='.') ++makeExample('router/ts/app/crisis-center/crisis-center.routing.2.ts', 'routes', 'app/crisis-center/crisis-center.routing.ts (Routes v.2)' )(format='.') .l-main-section h2#guards Route Guards @@ -1091,15 +1135,17 @@ h2#guards Route Guards The guard could ask the user a question, save changes to the server, or fetch fresh data. These are all asynchronous operations. - Accordingly, a routing guard can return an `Observable` and the + Accordingly, a routing guard can return an `Observable` or a `Promise` and the router will wait for the observable to resolve to `true` or `false`. - The router supports two kinds of guards: + The router supports three kinds of guards: 1. [CanActivate](../api/router/index/CanActivate-interface.html) to mediate navigation *to* a route. 2. [CanDeactivate](../api/router/index/CanDeactivate-interface.html) to mediate navigation *away* from the current route. + 3. [Resolve](../api/router/index/Resolve-interface.html) to perform route data retrieval before *before* route activation. + .l-sub-section :marked We'll examine other router guards in a future update to this chapter. @@ -1135,7 +1181,7 @@ h3#can-activate-guard CanActivate: requiring authentication +makeExample('router/ts/app/crisis-center/crisis-admin.component.1.ts', '', 'crisis-admin.component.ts')(format=".") :marked Next, we add a child route to the `crisis-center.routes` with the path, `/admin`. -+makeExample('router/ts/app/crisis-center/crisis-center.routes.3.ts', 'admin-route-no-guard', 'crisis-center.routes.ts (admin route)')(format=".") ++makeExample('router/ts/app/crisis-center/crisis-center.routing.3.ts', 'admin-route-no-guard', 'crisis-center.routing.ts (admin route)')(format=".") :marked And we add a link to the `AppComponent` shell that users can click to get to this feature. +makeExample('router/ts/app/app.component.4.ts', 'template', 'app/app.component.ts (template)')(format=".") @@ -1163,9 +1209,9 @@ h3#can-activate-guard CanActivate: requiring authentication It simply logs to console and `returns` true immediately, allowing navigation to proceed: +makeExample('router/ts/app/auth-guard.service.1.ts', '', 'app/auth-guard.service.ts')(format=".") :marked - Next we open `crisis-center.routes.ts `, import the `AuthGuard` class, and + Next we open `crisis-center.routing.ts `, import the `AuthGuard` class, and update the admin route with a `CanActivate` guard property that references it: -+makeExample('router/ts/app/crisis-center/crisis-center.routes.ts', 'admin-route', 'crisis-center.routes.ts (guarded admin route)')(format=".") ++makeExample('router/ts/app/crisis-center/crisis-center.routing.ts', 'admin-route', 'crisis-center.routing.ts (guarded admin route)')(format=".") Our admin feature is now protected by the guard, albeit protected poorly. :marked #### Teach *AuthGuard* to authenticate @@ -1200,16 +1246,21 @@ h3#can-activate-guard CanActivate: requiring authentication We need a `LoginComponent` for the user to log in to the app. After logging in, we'll redirect to our stored URL if available, or use the default URL. There is nothing new about this component or the way we wire it into the router configuration. - Here is the pertinent code, offered without comment: + + We'll register a `/login` route in our `app.routing.ts` and add the necessary providers to the `appRoutingProviders` + array we created earlier. In our `app.module.ts`, we'll import the `LoginComponent` and add it to our root NgModule `declarations`. + +makeTabs( - `router/ts/app/login.component.ts, - router/ts/app/login.routes.ts, - router/ts/app/app.routes.5.ts + `router/ts/app/app.module.ts, + router/ts/app/app.routing.5.ts, + router/ts/app/login.component.1.ts, + router/ts/app/login.routing.ts `, null, - `app/login.component.ts, - app/login.routes.ts, - app/app.routes.ts + `app/app.module.ts, + app/app.routing.ts, + app/login.component.ts, + app/login.routing.ts `) h3#can-deactivate-guard CanDeactivate: handling unsaved changes @@ -1266,41 +1317,111 @@ h3#can-deactivate-guard CanDeactivate: handling unsaved changes by waiting for the user's answer asynchronously. Waiting for the user asynchronously is like waiting for the server asynchronously. :marked - The `DialogService` (injected in the `AppComponent` for app-wide use) does the asking. + The `DialogService` (provided in the root `NgModule` for app-wide use) does the asking. It returns a [promise](http://exploringjs.com/es6/ch_promises.html) that *resolves* when the user eventually decides what to do: either to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`). - We will take the result of that promise and convert it to an `Observable` for our guard to use. - :marked We create a `Guard` that will check for the presence of a `canDeactivate` function in our component, in this case being `CrisisDetailComponent`. We don't need to know the details of how our `CrisisDetailComponent` confirms deactivation. This makes our guard reusable, which is an easy win for us. +makeExample('router/ts/app/can-deactivate-guard.service.ts', '', 'can-deactivate-guard.service.ts') + :marked - Looking at our `CrisisDetailComponent`, we have implemented our confirmation workflow for unsaved changes. + Alternatively, We could make a component-specific `CanDeactivate` guard for our `CrisisDetailComponent`. The `canDeactivate` method provides us + with the current instance of our `component`, the current `ActivatedRoute` and `RouterStateSnapshot` in case we needed to access + some external information. This would be useful if we only wanted to use this guard for this component and needed to ask the component's + properties in or to confirm whether the router should allow navigation away from it. ++makeExample('router/ts/app/can-deactivate-guard.service.1.ts', '', 'can-deactivate-guard.service.ts (component-specific)') + +:marked + Looking back at our `CrisisDetailComponent`, we have implemented our confirmation workflow for unsaved changes. +makeExample('router/ts/app/crisis-center/crisis-detail.component.1.ts', 'cancel-save-only', 'crisis-detail.component.ts (excerpt)') :marked Notice that the `canDeactivate` method *can* return synchronously; it returns `true` immediately if there is no crisis or there are no pending changes. - But it can also return a promise or an `Observable` and the router will wait for that + But it can also return a `Promise` or an `Observable` and the router will wait for that to resolve to truthy (navigate) or falsey (stay put). :marked - We add the `Guard` to our crisis detail route in `crisis-center.routes.ts` using the `canDeactivate` array. -+makeExample('router/ts/app/crisis-center/crisis-center.routes.4.ts', '', 'crisis-center.routes.ts') + We add the `Guard` to our crisis detail route in `crisis-center.routing.ts` using the `canDeactivate` array. ++makeExample('router/ts/app/crisis-center/crisis-center.routing.4.ts', '', 'crisis-center.routing.ts') :marked - We also need to add the `Guard` to our main `appRouterProviders` so the `Router` can inject it during the navigation process. -+makeExample('router/ts/app/app.routes.ts', '', 'app.routes.ts') + We also need to add the `Guard` to our main `appRoutingProviders` so the `Router` can inject it during the navigation process. ++makeExample('router/ts/app/app.routing.5.ts', '', 'app.routing.ts') :marked Now we have given our user a safeguard against unsaved changes. + +h3#resolve-guard Resolve: pre-fetching component data +:marked + In our `Hero Detail` and `Crisis Detail`, we waited until the route was activated to fetch our respective hero or crisis. + + This worked well for us, but we can always do better. + If we were using a real world api, there may be some delay in when the data we want to display gets returned. + We don't want to display a blank component until the data loads in this situation. + + We'd like to pre-fetch data from the server so its ready the moment our route is activated. + We'd also like to handle the situation where our data fails to load or some other error condition occurs. + This would help us in our `Crisis Center` if we navigated to an `id` that doesn't return a record. + We could send the user back to the `Crisis List` where we only show valid crisis centers. + We want to delay rendering of our route component until all necessary data has been fetched or some action + has occurred. + + We need the `Resolve` guard. + + ### Preload route information + + We'll update our `Crisis Detail` route to resolve our Crisis before loading the route, or if the user happens to + navigate to an invalid crisis center `:id`, we'll navigate back to our list of existing crises. + + Like the `CanActivate` and `CanDeactivate` guards, the **`Resolve`** guard is an interface we can implement as a service + to resolve route data synchronously or asynchronously. In our `Crisis Detail` component, we used the `ngOnInit` to retrieve the `Crisis` + information. We also navigated the user away from the route if the `Crisis` was not found. It would be more efficient to perform this + action before the route is ever activated. + + We'll create a `CrisisDetailResolve` service that will handle retrieving the `Crisis` and navigating the user away if the `Crisis` does + not exist. Then we can be assured that when we activate the `CrisisDetailComponent`, the associated Crisis will already be available + for display. + + Let's create our `crisis-detail-resolve.service.ts` file within our `Crisis Center` feature area. + ++makeExample('router/ts/app/crisis-center/crisis-detail-resolve.service.ts', '', 'crisis-detail-resolve.service.ts') + +:marked + We'll take the relevant parts of the `ngOnInit` lifecycle hook in our `CrisisDetailComponent` and moved them into our `CrisisDetailResolve` guard. + We import the `Crisis` model and `CrisisService` and also the `Router` for navigation from our resolve implementation. We want to be explicit about + the data we are resolving, so we implement the `Resolve` interface with a type of `Crisis`. This lets us know that what we will resolve will match our + `Crisis` model. We inject the `CrisisService` and `Router` and implement the `resolve` method that supports a `Promise`, `Observable` or a synchronous + return value. + + We'll use our `CrisisService.getCrisis` method that returns a promise to prevent our route from loading until the data is fetched. If we don't find a valid `Crisis`, + we navigate the user back to the `CrisisList`, canceling the previous in-flight navigation to the crisis details. + + Now that our guard is ready, we'll import it in our `crisis-center.routing.ts` and use the `resolve` object in our route configuration. + ++makeExample('router/ts/app/crisis-center/crisis-center.routing.5.ts', 'crisis-detail-resolve', 'crisis-center.routing.ts (resolve)') + +:marked + We'll add the `CrisisDetailResolve` service to our crisis center module's `providers`, so its available to the `Router` during the navigation process. + ++makeExample('router/ts/app/crisis-center/crisis-center.module.ts', 'crisis-detail-resolve', 'crisis-center.module.ts (crisis detail resolve provider)') + +:marked + Now that we've added our `Resolve` guard to fetch data before the route loads, we no longer need to do this once we get into our `CrisisDetailComponent`. + We'll update the `CrisisDetailComponent` to use the `ActivatedRoute.data`, which is where our `crisis` property from our `Resolve` guard will be provided. + Once activated, all we need to do is set our local `crisis` and `editName` properties from our resolved `Crisis` information. We no longer need to subscribe + and unsubscribe to the `ActivatedRoute` params to fetch the `Crisis` because it is being provided synchronously at the time the route component is activated. + ++makeExample('router/ts/app/crisis-center/crisis-detail.component.ts', 'crisis-detail-resolve', 'crisis-detail.component.ts (ngOnInit v.2)') + +:marked **Two critical points** 1. The router interface is optional. We don't inherit from a base class. We simply implement the interface method or not. @@ -1312,33 +1433,39 @@ h3#can-deactivate-guard CanDeactivate: handling unsaved changes +makeTabs( `router/ts/app/app.component.ts, - router/ts/app/auth-guard.service.2.ts, - router/ts/app/can-deactivate-guard.service.ts, router/ts/app/crisis-center/crisis-center.component.ts, - router/ts/app/crisis-center/crisis-center.routes.ts, + router/ts/app/crisis-center/crisis-center.routing.ts, router/ts/app/crisis-center/crisis-list.component.1.ts, - router/ts/app/crisis-center/crisis-detail.component.1.ts, + router/ts/app/crisis-center/crisis-detail.component.ts, + router/ts/app/crisis-center/crisis-detail-resolve.service.ts, router/ts/app/crisis-center/crisis.service.ts `, null, `app.component.ts, - auth-guard.service.ts, - can-deactivate-guard.service.ts, crisis-center.component.ts, - crisis-center.routes.ts, + crisis-center.routing.ts, crisis-list.component.ts, crisis-detail.component.ts, + crisis-detail-resolve.service.ts, crisis.service.ts `) ++makeTabs( + `router/ts/app/auth-guard.service.2.ts, + router/ts/app/can-deactivate-guard.service.ts + `, + null, + `auth-guard.service.ts, + can-deactivate-guard.service.ts + `) - - + + .l-main-section :marked - ## Milestone #4: Query Parameters + ## Milestone #4: Route Parameters - We use [*route parameters*](#route-parameters) to specify a *required* parameterized value *within* the route URL + We use [*route parameters*](#route-parameters) to specify a *required* parameter value *within* the route URL as we do when navigating to the `HeroDetailComponent` in order to view-and-edit the hero with *id:15*. code-example(format="." language="bash"). localhost:3000/hero/15 @@ -1359,24 +1486,21 @@ figure.image-display These kinds of parameters don't fit easily in a URL *path*. Even if we could define a suitable URL token scheme, doing so greatly complicates the pattern matching required to translate an incoming URL to a named route. - The **URL query string** is the ideal vehicle for conveying arbitrarily complex information during navigation. - The query string isn't involved in pattern matching and affords enormous flexiblity of expression. - Almost anything serializable can appear in a query string. + Optional parameters are the ideal vehicle for conveying arbitrarily complex information during navigation. + Optional parameters aren't involved in pattern matching and affords enormous flexibility of expression. - The Component Router supports navigation with query strings as well as route parameters. - We define _optional_ query string parameters in an *object* after we define our required route parameters. + The Component Router supports navigation with optional parameters as well as required route parameters. + We define _optional_ parameters in an *object* after we define our required route parameters. - - ### Route Parameters or Query Parameters? + ### Route Parameters: Required or Optional? There is no hard-and-fast rule. In general, - *prefer a route parameter when* + *prefer a required route parameter when* * the value is required. * the value is necessary to distinguish one route path from another. - *prefer a query parameter when* - * the value is optional. + *prefer an optional parameter when* * the value is complex and/or multi-variate. @@ -1387,8 +1511,8 @@ figure.image-display +makeExample('router/ts/app/heroes/hero-list.component.1.ts','link-parameters-array')(format=".") :marked The router embedded the `id` value in the navigation URL because we had defined it - as a route parameter with an `:id` placeholder token in the route `path`: -+makeExample('router/ts/app/heroes/heroes.routes.ts','hero-detail-route')(format=".") + as a route parameter with an `:id` placeholder token in the route `path`: ++makeExample('router/ts/app/heroes/heroes.routing.ts','hero-detail-route')(format=".") :marked When the user clicks the back button, the `HeroDetailComponent` constructs another *link parameters array* which it uses to navigate back to the `HeroListComponent`. @@ -1416,10 +1540,10 @@ figure.image-display code-example(format="." language="bash"). localhost:3000/heroes;id=15;foo=foo :marked - The `id` value appears in the query string (`;id=15;foo=foo`), not in the URL path. + The `id` value appears in the URL as (`;id=15;foo=foo`), not in the URL path. The path for the "Heroes" route doesn't have an `:id` token. :marked - The query string parameters are not separated by "?" and "&". + The optional route parameters are not separated by "?" and "&". They are **separated by semicolons (;)** This is *matrix URL* notation — something we may not have seen before. .l-sub-section @@ -1436,7 +1560,7 @@ code-example(format="." language="bash"). as this one can. :marked - ### Query parameters in the *ActivatedRoute* service + ### Route parameters in the *ActivatedRoute* service The list of heroes is unchanged. No hero row is highlighted. @@ -1478,20 +1602,17 @@ code-example(format="." language="bash"). figure.image-display img(src='/resources/images/devguide/router/selected-hero.png' alt="Selected List" ) :marked - The `foo` query string parameter is harmless and continues to be ignored. + The optional `foo` route parameter is harmless and continues to be ignored. - + :marked - ### Global Query parameters and Fragments + ### Query Parameters and Fragments :marked - In our [query parameters](#query-parameters) example, we only dealt with parameters specific to + In our [route parameters](#optional-route-parameters) example, we only dealt with parameters specific to our route, but what if we wanted optional parameters available to all routes? This is where our query parameters come into play and serve a special purpose in our application. - Traditional query string parameters (?name=value) **persist** across route navigations. This means we can pass these query params - around without having to specify them in each navigation method whether it be declaratively or imperatively. - [Fragments](https://en.wikipedia.org/wiki/Fragment_identifier) refer to certain elements on the page identified with an `id` attribute. @@ -1499,16 +1620,25 @@ figure.image-display We'll also provide an arbitrary `anchor` fragment, which we would use to jump to a certain point on our page. - We'll add the extra navigation object to our `router.navigate` method that navigates us to our `/login` route. + We'll add the `NavigationExtras` object to our `router.navigate` method that navigates us to our `/login` route. +makeExample('router/ts/app/auth-guard.service.ts','', 'auth-guard.service.ts (v.3)') + :marked - Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our global + We can also **preserve** query parameters and fragments across navigations without having to re-provide them + when navigating. In our `LoginComponent`, we'll add an *object* as the second argument in our `router.navigate` function + and provide the `preserveQueryParams` and `preserveFragment` to pass along the current query parameters + and fragment to the next route. + ++makeExample('router/ts/app/login.component.ts','preserve', 'login.component.ts (preserved)')(format=".") + +:marked + Since we'll be navigating to our *Crisis Admin* route after logging in, we'll update it to handle our query parameters and fragment. +makeExample('router/ts/app/crisis-center/crisis-admin.component.ts','', 'crisis-admin.component.ts (v.2)') :marked - *Query Parameters* and *Fragments* are available through the `routerState` property in our `Router` service. - Just like our *route parameters*, global query parameters and fragments are provided as an `Observable`. + *Query Parameters* and *Fragments* are also available through the `ActivatedRoute` service available to route components. + Just like our *route parameters*, query parameters and fragments are provided as an `Observable`. For our updated *Crisis Admin* component we'll feed the `Observable` directly into our template using the `AsyncPipe`, which will handle _unsubscribing_ from the `Observable` for us when the component is destroyed. @@ -1521,8 +1651,81 @@ figure.image-display Following the steps in this process, we can click on the *Crisis Admin* button, that takes us to the *Login* page with our provided `query params` and `fragment`. After we click the login button, we notice that we have been redirected to the `Crisis Admin` page with our `query params` and `fragment` still intact. We can use - these persistent bits of information for things that need to be provided with every page interaction like + these persistent bits of information for things that need to be provided with across pages interaction like authentication tokens or session ids. + +.l-main-section +:marked + ## Milestone #5: Asynchronous Routing + + As we have completed our milestones, our application has naturally gotten larger. As we continue to build + out feature areas our overall application size will get larger also. At some point we'll reach a tipping + point in where our application takes a significant enough time to load. This is not a viable long term solution. + + So how do we combat this problem? We introduce asynchronous routing into our application and take advantage of loading + feature areas _lazily_. This buys us multiple things: + + We can continue building out feature areas without increasing our initial bundle. + We can load feature areas only when requested by the user. + We can speed up load time for users that only visit certain areas of our application. + + These are all things we want to have in our application, so let's apply this to our current setup. We've already made + great strides by organizing our application into three modules: `AppModule`, `HeroesModule` and `CrisisCenterModule`. + Our `CrisisCenterModule` is the most feature-rich area of our application and also the largest, so we'll take advantage + of asynchronous routing and only load the `Crisis Center` feature area when requested. + +:marked + ### Lazy-Loading route configuration + + We'll start by pulling our `redirect` and `crisis-center` routes out of our `CrisisCenterModule` and including them in our + `app.routing.ts` file. We want to load our `Crisis Center` asynchronously, so we'll use the `loadChildren` property in + our route config where previously we used the `children` property to include our child routes. + + We'll also change our `crisis-center` **path** in our `crisis-center.routing.ts` to an empty path. The `Router` supports + *empty path* routes, which we can use for grouping routes together without adding anything additional paths to the URL. Our + users will still visit `/crisis-center` and our `CrisisCenterComponent` still serves as our *Routing Component* which contains + our child routes. + ++makeTabs( + `router/ts/app/app.routing.ts, + router/ts/app/crisis-center/crisis-center.routing.ts`, + 'lazy-load-crisis-center,lazy-load-crisis-center', + `app.routing.ts (load children), + crisis-center.routing.ts (empty path crisis center) + `) + +:marked + The `loadChildren` property is used by the `Router` to map to our bundle we want to lazy-load, in this case being the `CrisisCenterModule`. + + If we look closer at the `loadChildren` string, we can see that it maps directly to our `crisis-center.module` file where we previously built + out our `Crisis Center` feature area. After the path to the file we use a `#` to denote where our file path ends and to tell the `Router` the name + of our `CrisisCenter` NgModule. If we look in our `crisis-center.module` file, we can see it matches name of our exported NgModule class. + ++makeExample('router/ts/app/crisis-center/crisis-center.module.ts', 'crisis-center-module-export', 'crisis-center.module.ts (export)') + +:marked + The `loadChildren` property is used by the `Router` to map to our bundle we want to lazy-load, in this case being the `CrisisCenterModule`. + The router will take our loadChildren string and dynamically load in our `CrisisCenterModule`, add its routes to our configuration *dynamically* + and then load the requested route. This will only happen when route is **first** requested and the module will be immediately be available + for subsequent requests. + +.l-sub-section + :marked + Angular provides a built-in module loader that supports **`SystemJS`** to load modules asynchronously. If we were + using another bundling tool, such as **Webpack**, we would use the Webpack mechanism for asynchronously loading modules. + +:marked + We've built our feature area, we've updated our route configuration to take advantage of lazy-loading, now we have to do the final step + to break our `CrisisCenterModule` into a completely separate module. In our `app.module.ts`, we'll remove our `CrisisCenterModule` from the + `imports` array since we'll be loading it on-demand an we'll remove the imported `CrisisCenterModule`. + ++makeExample('router/ts/app/app.module.ts', '', 'app.module.ts (final)') + +:marked + If our initial redirect went to `/heroes` instead of going to `/crisis-center`, the `CrisisCenterModule` would not be loaded until the user + visited a `Crisis Center` route. We'll update our redirect in our `app.routing.ts` to make this change. + ++makeExample('router/ts/app/app.routing.ts', 'heroes-redirect', 'app.routing.ts (heroes redirect)') .l-main-section @@ -1549,7 +1752,7 @@ figure.image-display A link parameters array holds the ingredients for router navigation: * the *path* of the route to the destination component - * required route parameters and optional query parameters that go into the route URL + * required and optional route parameters that go into the route URL We can bind the `RouterLink` directive to such an array like this: +makeExample('router/ts/app/app.component.3.ts', 'h-anchor')(format=".") @@ -1557,7 +1760,7 @@ figure.image-display We've written a two element array when specifying a route parameter like this +makeExample('router/ts/app/heroes/hero-list.component.1.ts', 'nav-to-detail')(format=".") :marked - We can provide optional query parameters in an object like this: + We can provide optional route parameters in an object like this: +makeExample('router/ts/app/app.component.3.ts', 'cc-query-params')(format=".") :marked These three examples cover our needs for an app with one level routing. @@ -1656,7 +1859,7 @@ code-example(format=".", language="bash"). 1. `PathLocationStrategy` - the default "HTML 5 pushState" style. 1. `HashLocationStrategy` - the "hash URL" style. - The router's `provideRouter` function sets the `LocationStrategy` to the `PathLocationStrategy`, + The `RouterModule.forRoot` function sets the `LocationStrategy` to the `PathLocationStrategy`, making it the default strategy. We can switch to the `HashLocationStrategy` with an override during the bootstrapping process if we prefer it. .l-sub-section @@ -1714,11 +1917,6 @@ code-example(format=".", language="bash"). :marked ### *HashLocationStrategy* We can go old-school with the `HashLocationStrategy` by - providing it as the router's `LocationStrategy` during application bootstrapping. - - First, import the `provide` symbol for Dependency Injection and the - `Location` and `HashLocationStrategy` symbols from the router. - - Then *override* the default strategy defined in `provideRouter` by - providing the `HashLocationStrategy` later in the `AppComponent` providers array argument: -+makeExample('router/ts/app/main.2.ts','', 'main.ts (hash URL strategy)') + providing the `useHash: true` in an object as the second argument of the `RouterModule.forRoot` + in our root NgModule. ++makeExample('router/ts/app/app.module.4.ts','', 'app.module.ts (hash URL strategy)') diff --git a/public/docs/ts/latest/guide/server-communication.jade b/public/docs/ts/latest/guide/server-communication.jade index c254ff9377..fbe656ba37 100644 --- a/public/docs/ts/latest/guide/server-communication.jade +++ b/public/docs/ts/latest/guide/server-communication.jade @@ -83,34 +83,22 @@ block demos-list Learn about providers in the [Dependency Injection](dependency-injection.html) chapter. :marked - In this demo, we register providers in the `bootstrap()` method of - app/main.ts. + In this demo, we register providers by importing other NgModules to our root NgModule. -+makeExample('server-communication/ts/app/main.ts', 'v1', 'app/main.ts (v1)')(format='.') ++makeExample('server-communication/ts/app/app.module.1.ts', null, 'app/app.module.ts (v1)')(format='.') block http-providers :marked - We begin by importing the symbols we need, most of them familiar by now. The newcomer is `HTTP_PROVIDERS`, - a collection of service providers from the !{_Angular_http_library}. + We begin by importing the symbols we need, most of them familiar by now. + The newcomers are the `HttpModule` and the `JsonpModule` from the !{_Angular_http_library}. - We register HTTP providers in the bootstrap method by passing them in an array as the second parameter after the root component. - - ### Why register in *bootstrap*? - - We prefer to register application-wide providers in the metadata `providers` array - of the root `AppComponent` like this: - +makeExample('server-communication/ts/app/app.component.ts','http-providers')(format='.') + We add these modules to the application by passing them to the `imports` array in our root NgModule. +.l-sub-section :marked - Here we register the providers in the `bootstrap` method in the `main.ts` file. Why? - - This is a *sample application* that doesn't talk to a real server. - We're going to reconfigure the (typically-hidden) `XhrBackend` service with a fake provider - that fetches and saves sample data from an in-memory data store. - This replacement service is called the [*in-memory web api*](#in-mem-web-api). - - Such sleight-of-hand is something the root application component should *not* know about. - For this reason, and this reason *only*, we hide it *above* the `AppComponent` in `main.ts`. - + We need the HttpModule to make HTTP calls. + We don't need the JsonpModule for plain HTTP. + We will demonstrate JSONP support later in this chapter. + We're loading its module now to save time. .l-main-section#http-client :marked # The Tour of Heroes _HTTP_ Client Demo @@ -507,8 +495,8 @@ block wikipedia-jsonp+ +makeExample('server-communication/ts/app/wiki/wikipedia.service.ts',null,'app/wiki/wikipedia.service.ts') :marked The constructor expects Angular to inject its `jsonp` service. - We register that service with `JSONP_PROVIDERS` in the [component below](#wikicomponent) that calls our `WikipediaService`. - + We made that service available by importing the `JsonpModule` into our root NgModule. + :marked ### Search parameters @@ -543,9 +531,6 @@ block wikipedia-jsonp+ +makeExample('server-communication/ts/app/wiki/wiki.component.ts', null, 'app/wiki/wiki.component.ts') :marked - The `providers` array in the component metadata specifies the Angular `JSONP_PROVIDERS` collection that supports the `Jsonp` service. - We register that collection at the component level to make `Jsonp` injectable in the `WikipediaService`. - The component presents an `` element *search box* to gather search terms from the user. and calls a `search(term)` method after each `keyup` event. @@ -694,9 +679,9 @@ block redirect-to-web-api the in-memory web API service using standard Angular provider registration techniques. We initialize the in-memory web API with *seed data* from the mock hero dataset at the same time. :marked - Here is the revised (and final) version of app/main.ts> demonstrating these steps. + Here is the revised (and final) version of app/app.module.ts> demonstrating these steps. -+makeExcerpt('app/main.ts', 'final') ++makeExcerpt('app/app.module.ts') :marked See the full source code in the . diff --git a/public/docs/ts/latest/guide/structural-directives.jade b/public/docs/ts/latest/guide/structural-directives.jade index cd832d83d1..601367811b 100644 --- a/public/docs/ts/latest/guide/structural-directives.jade +++ b/public/docs/ts/latest/guide/structural-directives.jade @@ -284,7 +284,7 @@ block unless-intro +makeExample('structural-directives/ts/app/unless.directive.ts', null, 'unless.directive.ts') :marked - Now we add it to the `directives`array of the host component and try it. + Now we add it to the `declarations` array of the AppModule and try it. First we add some test HTML to the template: +makeExample('structural-directives/ts/app/structural-directives.component.html', 'myUnless')(format=".") diff --git a/public/docs/ts/latest/guide/style-guide.jade b/public/docs/ts/latest/guide/style-guide.jade index 0feeb5f8f8..e430b64a6d 100644 --- a/public/docs/ts/latest/guide/style-guide.jade +++ b/public/docs/ts/latest/guide/style-guide.jade @@ -1,5 +1,10 @@ include ../_util-fns +.alert.is-important + :marked + We are still preparing style recommendations for the new NgModules feature + introduced in RC5 and will add it to the style guide soon. + :marked Welcome to the Angular 2 Style Guide @@ -96,6 +101,7 @@ a(id='toc') +makeTabs( `style-guide/ts/01-01/main.ts, + style-guide/ts/01-01/app/app.module.ts, style-guide/ts/01-01/app/app.component.ts, style-guide/ts/01-01/app/heroes/heroes.component.ts, style-guide/ts/01-01/app/heroes/shared/hero.service.ts, @@ -103,6 +109,7 @@ a(id='toc') style-guide/ts/01-01/app/heroes/shared/mock-heroes.ts`, '', `main.ts, + app/app.module.ts, app/app.component.ts, app/heroes/heroes.component.ts, app/heroes/shared/hero.service.ts, diff --git a/public/docs/ts/latest/guide/template-syntax.jade b/public/docs/ts/latest/guide/template-syntax.jade index 3e0fb06ce8..9cf213cb1d 100644 --- a/public/docs/ts/latest/guide/template-syntax.jade +++ b/public/docs/ts/latest/guide/template-syntax.jade @@ -858,8 +858,20 @@ block style-property-name-dart-diff header [()] = banana in a box :marked To remember that the parentheses go inside the brackets, visualize a *banana in a box*. + +.callout.is-important + header FormsModule is Required to use ngModel + :marked + Before we can use `ngModel` two-way data binding, we need to import the `FormsModule` + package in our Angular module. We add it to the `NgModule` decorator's `imports` array. This array contains the list + of external modules used by our application. +
    Learn more about the `FormsModule` and `ngModel` in the + [Forms](../guide/forms.html#ngModel) chapter. + ++makeExample('template-syntax/ts/app/app.module.1.ts', '', 'app.module.ts (FormsModule import)') + :marked - Alternatively, we can use the canonical prefix form: + Alternatively to using `[(ngModel)]`, we can use the canonical prefix form: +makeExample('template-syntax/ts/app/app.component.html', 'NgModel-2')(format=".") :marked There’s a story behind this construction, a story that builds on the property and event binding techniques we learned previously. diff --git a/public/docs/ts/latest/guide/testing.jade b/public/docs/ts/latest/guide/testing.jade index 4859c9551c..7777219c47 100644 --- a/public/docs/ts/latest/guide/testing.jade +++ b/public/docs/ts/latest/guide/testing.jade @@ -1,3 +1,8 @@ +.alert.is-important + :marked + We are still preparing the testing guide with all the new testing features + introduced in RC5 and will update it very soon. + :marked We write **unit tests** to explore and confirm the **behavior** of parts of our application. diff --git a/public/docs/ts/latest/guide/upgrade.jade b/public/docs/ts/latest/guide/upgrade.jade index 4d906213c7..85b5a09bd6 100644 --- a/public/docs/ts/latest/guide/upgrade.jade +++ b/public/docs/ts/latest/guide/upgrade.jade @@ -1,5 +1,10 @@ include ../_util-fns +.alert.is-important + :marked + This guide is still in the process of being updated to RC5 and it's samples + may not work correctly. + :marked Having an existing Angular 1 application doesn't mean that we can't begin enjoying everything Angular 2 has to offer. That's because Angular 2 diff --git a/public/docs/ts/latest/guide/webpack.jade b/public/docs/ts/latest/guide/webpack.jade index 83404e4295..b6dc9da3de 100644 --- a/public/docs/ts/latest/guide/webpack.jade +++ b/public/docs/ts/latest/guide/webpack.jade @@ -417,12 +417,14 @@ code-example(format=""). `webpack/ts/src/app/app.component.ts, webpack/ts/src/app/app.component.html, webpack/ts/src/app/app.component.css, - webpack/ts/src/app/app.component.spec.ts`, + webpack/ts/src/app/app.component.spec.ts, + webpack/ts/src/app/app.module.ts`, null, `src/app/app.component.ts, src/app/app.component.html, src/app/app.component.css, - src/app/app.component.spec.ts` + src/app/app.component.spec.ts, + src/app/app.module.ts` ) diff --git a/public/docs/ts/latest/quickstart.jade b/public/docs/ts/latest/quickstart.jade index 5de3efe99c..90ccf172b1 100644 --- a/public/docs/ts/latest/quickstart.jade +++ b/public/docs/ts/latest/quickstart.jade @@ -37,9 +37,10 @@ figure.image-display - [Step 1](#create-and-configure): Create the app’s project folder and define package dependencies and special project setup - [Step 2](#root-component): Create the app’s Angular root component - - [Step 3](#main): Add main.ts, identifying the root component to Angular - - [Step 4](#index): Add `index.html`, the web page that hosts the application - - [Step 5](#build-and-run): Build and run the app + - [Step 3](#ngmodule): Create an Angular Module + - [Step 4](#main): Add main.ts, identifying the root component to Angular + - [Step 5](#index): Add `index.html`, the web page that hosts the application + - [Step 6](#build-and-run): Build and run the app - [Make some changes to the app](#make-some-changes) - [Wrap up](#wrap-up) @@ -112,7 +113,7 @@ block package-and-config-files li. #[b package.json] lists packages the QuickStart app depends on and defines some useful scripts. - See #[a(href="guide/npm-packages.html") Npm Package Configuration] for details. + See #[a(href="guide/npm-packages") Npm Package Configuration] for details. li. #[b tsconfig.json] is the TypeScript compiler configuration file. See #[a(href="#{_tsconfigUri}") TypeScript Configuration] for details. @@ -177,7 +178,7 @@ block install-packages package manager to install the libraries and packages their apps require. The Angular team recommends the starter-set of packages specified in the `dependencies` and `devDependencies` sections. - See the [npm packages](guide/npm-packages.html) chapter for details. + See the [npm packages](guide/npm-packages) chapter for details. #### Helpful scripts We've included a number of npm scripts in our suggested `package.json` to handle common development tasks: @@ -301,14 +302,47 @@ p. +ifDocsFor('ts') :marked We **export** `AppComponent` so that we can **import** it elsewhere in our application, - as we'll see when we create `main.ts`. + as we'll see when we create `app.module.ts`. .l-main-section -h2#main Step 3: Add #[code #[+adjExPath('main.ts')]] +h2#ngmodule Step 3: Our own #[code #[+adjExPath('app.module.ts')]] + +block create-ngmodule + :marked + We compose Angular apps into closely related blocks of functionality with [Angular Modules](guide/ngmodules.html). + Every app requires at least one module, the _root module_, that we call `AppModule` by convention. + p. + Create the file #[code #[+adjExPath('app/app.module.ts')]] with the following content: + ++makeExample('app/app.module.ts')(format='.') + +.l-verbose-section + :marked + We're passing metadata to the `NgModule` decorator function: + + 1. `imports` - the _other_ modules that export material we need in _this_ module. + Almost every application's _root module_ should import the `BrowserModule`. + + 1. `declarations` - components and directives that belong to _this_ module. + + 1. `bootstrap` - identifies the _root component_ that Angular should _bootstrap_ when it starts the application. + + We import our lone `app.component.ts` and add it to both the `declarations` and `bootstrap` arrays. + + ### Angular Modules import other modules + Notice that we also add the `BrowserModule` from `@angular/platform-browser` to the `imports` array. + This is the Angular Module that contains all the needed Angular bits and pieces to run our app in the browser. + + Angular itself is split into separate Angular Modules so we only need to import the ones we really use. + + One of the most common ones is `FormsModule`, and soon we'll also see `RouterModule` and `HttpModule`. + +.l-main-section +h2#main Step 4: Add #[code #[+adjExPath('main.ts')]] block create-main p. - Now we need something to tell Angular to load the root component. + Now we need something to tell Angular to load the app module. Create the file #[code #[+adjExPath('app/main.ts')]] with the following content: +makeExample('app/main.ts') @@ -317,41 +351,40 @@ block create-main :marked We import the two things we need to launch the application: - 1. Angular's browser `bootstrap` function - 1. The application root component, `AppComponent`. + 1. Angular's browser `platformBrowserDynamic` function + 1. The application module, `AppModule`. - Then we call `bootstrap` with `AppComponent`. + Then we call `platformBrowserDynamic().bootstrapModule` with `AppComponent`. ### Bootstrapping is platform-specific - Notice that we import the `bootstrap` function from `#{_angular_browser_uri}`, + Notice that we import the `platformBrowserDynamic` function from `#{_angular_browser_uri}`, not `#{_angular_core_uri}`. Bootstrapping isn't core because there isn't a single way to bootstrap the app. True, most applications that run in a browser call the bootstrap function from this library. - But it is possible to load a component in a different environment. + But it is possible to load a module in a different environment. We might load it on a mobile device with [Apache Cordova](https://cordova.apache.org/) or [NativeScript](https://www.nativescript.org/). We might wish to render the first page of our application on the server to improve launch performance or facilitate [SEO](http://www.google.com/webmasters/docs/search-engine-optimization-starter-guide.pdf). These targets require a different kind of bootstrap function that we'd import from a different library. - ### Why create separate *main.ts* and app component files? + ### Why create separate *main.ts*, app module and app component files? - Both main.ts and the app component files are tiny. + Both main.ts, app module and the app component files are tiny. This is just a QuickStart. - We could have merged these two files into one - and spared ourselves some complexity. + We could have merged these three files into one and spared ourselves some complexity. We'd rather demonstrate the proper way to structure an Angular application. - App bootstrapping is a separate concern from presenting a view. + App bootstrapping is a separate concern from creating a module or presenting a view. Mixing concerns creates difficulties down the road. - We might launch the `AppComponent` in multiple environments with different bootstrappers. + We might launch the `AppModule` in multiple environments with different bootstrappers. Testing the component is much easier if it doesn't also try to run the entire application. Let's make the small extra effort to do it *the right way*. .l-main-section -h2#index Step 4: Add #[code index.html] +h2#index Step 5: Add #[code index.html] :marked In the *#{_indexHtmlDir}* folder create an `index.html` file and paste the following lines into it: @@ -465,8 +498,9 @@ h2#index Step 4: Add #[code index.html] a(id="my-app") :marked - When Angular calls the `bootstrap` function in main.ts, it reads the `AppComponent` - metadata, finds the `my-app` selector, locates an element tag named `my-app`, + When Angular calls the `bootstrapModule` function in main.ts, + it reads the `AppModule` metadata, sees that `AppComponent` is the bootstrap component, + finds the `my-app` selector, locates an element tag named `my-app`, and renders our application's view between those tags. :marked @@ -480,7 +514,7 @@ h2#index Step 4: Add #[code index.html] +makeExcerpt('styles.1.css') .l-main-section -h2#build-and-run Step 5: Build and run the app! +h2#build-and-run Step 6: Build and run the app! block run-app :marked Open a terminal window and enter this command: @@ -528,6 +562,7 @@ block project-file-structure .file app .children .file app.component.ts + .file app.module.ts .file main.ts .file node_modules ... .file typings ... @@ -543,6 +578,7 @@ block project-file-structure block project-files +makeTabs(` quickstart/ts/app/app.component.ts, + quickstart/ts/app/app.module.ts, quickstart/ts/app/main.ts, quickstart/ts/index.html, quickstart/ts/package.1.json, @@ -550,8 +586,10 @@ block project-files quickstart/ts/typings.1.json, quickstart/ts/styles.1.css, quickstart/ts/systemjs.config.1.js` - ,null, + , + ',,,,,,,,', `app/app.component.ts, + app/app.module.ts, app/main.ts, index.html, package.json, diff --git a/public/docs/ts/latest/tutorial/toh-pt1.jade b/public/docs/ts/latest/tutorial/toh-pt1.jade index 9ee468e456..c75a3f81fc 100644 --- a/public/docs/ts/latest/tutorial/toh-pt1.jade +++ b/public/docs/ts/latest/tutorial/toh-pt1.jade @@ -7,7 +7,7 @@ include ../_util-fns Run the for this part. - Create a folder called `angular2-tour-of-heroes` and follow the [QuickStart](../quickstart.html) steps + Create a folder called `angular2-tour-of-heroes` and follow the [QuickStart](../quickstart.html) steps which provide the prerequisites, the folder structure, and the core files for our Tour of Heroes. include ../_quickstart_repo @@ -20,9 +20,10 @@ include ../_quickstart_repo .file app .children .file app.component.ts + .file app.module.ts .file main.ts .file node_modules ... - .file typings ... + .file typings ... .file index.html .file package.json .file styles.css @@ -141,14 +142,22 @@ code-example(language="bash"). and see those changes wherever we bind to the hero’s name. In short, we want two-way data binding. - Let’s update the template to use the **`ngModel`** built-in directive for two-way binding. + Before we can use two-way data binding for **form inputs**, we need to import the `FormsModule` + package in our Angular module. We add it to the `NgModule` decorator's `imports` array. This array contains the list + of external modules used by our application. + Now we have included the forms package which includes `ngModel`. + ++makeExample('toh-1/ts/app/app.module.ts', '', 'app.module.ts (FormsModule import)') .l-sub-section :marked - Learn more about `ngModel` in the + Learn more about the `FormsModule` and `ngModel` in the [Forms](../guide/forms.html#ngModel) and [Template Syntax](../guide/template-syntax.html#ngModel) chapters. + :marked + Let’s update the template to use the **`ngModel`** built-in directive for two-way binding. + Replace the `` with the following HTML code-example(language="html"). @@ -163,7 +172,7 @@ code-example(language="html"). ## The Road We’ve Travelled Let’s take stock of what we’ve built. - * Our Tour of Heroes uses the double curly braces of interpolation (a form of one-way data binding) + * Our Tour of Heroes uses the double curly braces of interpolation (a kind of one-way data binding) to display the application title and properties of a `Hero` object. * We wrote a multi-line template using ES2015’s template strings to make our template readable. * We can both display and change the hero’s name after adding a two-way data binding to the `` element @@ -182,5 +191,5 @@ code-example(language="html"). Our Tour of Heroes only displays one hero and we really want to display a list of heroes. We also want to allow the user to select a hero and display their details. We’ll learn more about how to retrieve lists, bind them to the - template, and allow a user to select it in the + template, and allow a user to select a hero in the [next tutorial chapter](./toh-pt2.html). diff --git a/public/docs/ts/latest/tutorial/toh-pt2.jade b/public/docs/ts/latest/tutorial/toh-pt2.jade index ce7c919672..75fdf2a293 100644 --- a/public/docs/ts/latest/tutorial/toh-pt2.jade +++ b/public/docs/ts/latest/tutorial/toh-pt2.jade @@ -25,6 +25,7 @@ include ../_util-fns .file app .children .file app.component.ts + .file app.module.ts .file main.ts .file node_modules ... .file typings ... diff --git a/public/docs/ts/latest/tutorial/toh-pt3.jade b/public/docs/ts/latest/tutorial/toh-pt3.jade index b45cd4980c..3171fe4c14 100644 --- a/public/docs/ts/latest/tutorial/toh-pt3.jade +++ b/public/docs/ts/latest/tutorial/toh-pt3.jade @@ -17,6 +17,7 @@ include ../_util-fns .file app .children .file app.component.ts + .file app.module.ts .file main.ts .file node_modules ... .file typings ... @@ -149,15 +150,26 @@ code-example(format="."). .l-main-section :marked - ## Refresh the AppComponent - We return to the `AppComponent` and teach it to use the `HeroDetailComponent`. + ## Refresh the AppModule + We return to the `AppModule`, the application's root module, and teach it to use the `HeroDetailComponent`. We begin by importing the `HeroDetailComponent` so we can refer to it. -+makeExample('toh-3/ts/app/app.component.ts', 'hero-detail-import') ++makeExample('toh-3/ts/app/app.module.ts', 'hero-detail-import') :marked - Find the location in the template where we removed the *Hero Detail* content + Then we add `HeroDetailComponent` to the `NgModule` decorator's `declarations` array. + This array contains the list of all components, pipes, and directives that we created + and that belong in our application's module. + ++makeExample('toh-3/ts/app/app.module.ts', 'declarations') + +.l-main-section +:marked + ## Refresh the AppComponent +:marked + Now that the application knows about our `HeroDetailComponent`, + find the location in the `AppComponent` template where we removed the *Hero Detail* content and add an element tag that represents the `HeroDetailComponent`. code-example(format="."). <my-hero-detail></my-hero-detail> @@ -177,21 +189,6 @@ code-example(format=".") Thanks to the binding, the `HeroDetailComponent` should receive the hero from the `AppComponent` and display that hero's detail beneath the list. The detail should update every time the user picks a new hero. - It's not happening yet! - - We click among the heroes. No details. We look for an error in the console of the browser development tools. No error. - - It is as if Angular were ignoring the new tag. That's because *it is ignoring the new tag*. - - ### The *directives* array - A browser ignores HTML tags and attributes that it doesn't recognize. So does Angular. - - We've imported `HeroDetailComponent`, we've used it in the template, but we haven't told Angular about it. - - We tell Angular about it by listing it in the metadata `directives` array. Let's add that array property to the bottom of the - `@Component` configuration object, immediately after the `template` and `styles` properties. -+makeExample('toh-3/ts/app/app.component.ts', 'directives', 'app/app.component.ts (Directives)') - :marked ### It works! When we view our app in the browser we see the list of heroes. @@ -211,6 +208,7 @@ code-example(format=".") .file app .children .file app.component.ts + .file app.module.ts .file hero.ts .file hero-detail.component.ts .file main.ts @@ -226,12 +224,14 @@ code-example(format=".") +makeTabs(` toh-3/ts/app/hero-detail.component.ts, toh-3/ts/app/app.component.ts, - toh-3/ts/app/hero.ts + toh-3/ts/app/hero.ts, + toh-3/ts/app/app.module.ts, `,'',` app/hero-detail.component.ts, app/app.component.ts, - app/hero.ts - `) + app/hero.ts, + app/app.module.ts + `) .l-main-section :marked @@ -240,8 +240,9 @@ code-example(format=".") * We created a reusable component * We learned how to make a component accept input + * We learned to declare the application directives we need in an Angular module. We + list the directives in the `NgModule` decorator's `declarations` array. * We learned to bind a parent component to a child component. - * We learned to declare the application directives we need in a `directives` array. Run the for this part. diff --git a/public/docs/ts/latest/tutorial/toh-pt4.jade b/public/docs/ts/latest/tutorial/toh-pt4.jade index 12660bf643..920be42806 100644 --- a/public/docs/ts/latest/tutorial/toh-pt4.jade +++ b/public/docs/ts/latest/tutorial/toh-pt4.jade @@ -29,6 +29,7 @@ include ../_util-fns .file app .children .file app.component.ts + .file app.module.ts .file hero.ts .file hero-detail.component.ts .file main.ts @@ -308,6 +309,7 @@ a#child-component .file app .children .file app.component.ts + .file app.module.ts .file hero.ts .file hero-detail.component.ts .file hero.service.ts diff --git a/public/docs/ts/latest/tutorial/toh-pt5.jade b/public/docs/ts/latest/tutorial/toh-pt5.jade index 243a92d7e1..60954ae6e1 100644 --- a/public/docs/ts/latest/tutorial/toh-pt5.jade +++ b/public/docs/ts/latest/tutorial/toh-pt5.jade @@ -39,6 +39,7 @@ figure.image-display .file app .children .file app.component.ts + .file app.module.ts .file hero.ts .file hero-detail.component.ts .file hero.service.ts @@ -109,23 +110,29 @@ code-example(language="bash"). * create a new file named `app.component.ts`. * define an `AppComponent` class. - * `export` it so we can reference it during bootstrapping in `main.ts`. + * `export` it so we can reference it during bootstrapping in `app.module.ts`. * expose an application `title` property. * add the `@Component` metadata decorator above the class with a `my-app` selector. * add a template with `

    ` tags surrounding a binding to the `title` property. * add the `` tags to the template so we still see the heroes. - * add the `HeroesComponent` to the `directives` array so Angular recognizes the `` tags. - * add the `HeroService` to the `providers` array because we'll need it in every other view. + * add the `HeroesComponent` to the root NgModule's `declarations` array so Angular recognizes the `` tags. + * add the `HeroService` to the root NgModule's `providers` array because we'll need it in every other view. * add the supporting `import` statements. Our first draft looks like this: -+makeExample('toh-5/ts/app/app.component.1.ts', null, 'app/app.component.ts (v1)') ++makeTabs( + `toh-5/ts/app/app.component.1.ts, + toh-5/ts/app/app.module.1.ts`, + ',', + `app/app.component.ts (v1), + app/app.module.ts (v1)`) + :marked .callout.is-critical header Remove HeroService from the HeroesComponent providers :marked Go back to the `HeroesComponent` and **remove the `HeroService`** from its `providers` array. - We are *promoting* this service from the `HeroesComponent` to the `AppComponent`. + We are *promoting* this service from the `HeroesComponent` to the root `NgModule`. We ***do not want two copies*** of this service at two different levels of our app. :marked The app still runs and still displays heroes. @@ -149,22 +156,22 @@ code-example(language="bash"). :marked See the *base href* section of the [Router](../guide/router.html#!#base-href) chapter to learn why this matters. :marked - The Angular router is a combination of multiple provided services (`provideRouter`), multiple directives (`ROUTER_DIRECTIVES`), - and a configuration (`RouterConfig`). We'll configure our routes first: + The Angular router is a combination of multiple provided services (`RouterModule`), multiple directives (`RouterOutlet, + RouterLink, RouterLinkActive`), and a configuration (`Routes`). We'll configure our routes first: - ### Configure and add the router + ### Configure the routes - Our application doesn't have a router yet. We'll create a configuration file for our routes that - does two things - (a) configure that router with *routes*. (b) provide an export to add the router to our bootstrap + The *Component Router* is an external, optional Angular NgModule called `RouterModule`. + Our application doesn't have any routes yet. + Start by creating a configuration file for the application routes. *Routes* tell the router which views to display when a user clicks a link or pastes a URL into the browser address bar. - Let's define our first route, a route to the `HeroesComponent`. -+makeExample('toh-5/ts/app/app.routes.2.ts', '', 'app/app.routes.ts')(format=".") + Our first route goes to the `HeroesComponent`. ++makeExample('toh-5/ts/app/app.routing.1.ts', 'routing-config', 'app/app.routing.ts (heroes route)')(format=".") :marked - The `RouterConfig` is an array of *route definitions*. + The `Routes` are an array of *route definitions*. We have only one route definition at the moment but rest assured, we'll add more. This *route definition* has two parts: @@ -174,24 +181,37 @@ code-example(language="bash"). .l-sub-section :marked - Learn more about defining routes with RouterConfig in the [Routing](../guide/router.html) chapter. + Learn more about defining routes with Routes in the [Routing](../guide/router.html) chapter. + +:marked + We'll export the `routing` constant using the **RouterModule.forRoot** method with our array of routes. + This returns a configured router module we'll add to our root NgModule, `AppModule`. + ++makeExample('toh-5/ts/app/app.routing.1.ts', 'routing-export', 'app/app.routing.ts (routing export)')(format=".") +.l-sub-section + :marked + We call the `forRoot` method because we're providing a configured router at the _root_ of the application. + The `forRoot` method gives us the Router service providers and directives needed for routing. :marked ### Make the router available. - The *Component Router* is a service. We have to import our `appRouterProviders` which - contains our configured router and make it available to the application by adding it to - the `bootstrap` array. -+makeExample('toh-5/ts/app/main.ts', '', 'app/main.ts')(format=".") + We've setup our initial routes in our `app.routing.ts` file. Now we'll add it to our root NgModule. + + We'll import our `routing` constant from our `app.routing.ts` file and add it root NgModule's `imports` array. + + We'll also import our `HeroesComponent` and add it to our `declarations` array. + ++makeExample('toh-5/ts/app/app.module.2.ts', '', 'app/app.module.ts')(format=".") :marked ### Router Outlet If we paste the path, `/heroes`, into the browser address bar, - the router should match it to the `'Heroes'` route and display the `HeroesComponent`. + the router should match it to the `heroes` route and display the `HeroesComponent`. But where? We have to ***tell it where*** by adding `` marker tags to the bottom of the template. - `RouterOutlet` is one of the `ROUTER_DIRECTIVES`. + `RouterOutlet` is one of the directives provided by the `RouterModule`. The router displays each component immediately below the `` as we navigate through the application. ### Router Links @@ -201,16 +221,15 @@ code-example(language="bash"). The revised template looks like this: +makeExample('toh-5/ts/app/app.component.2.ts', 'template', 'app/app.component.ts (template v1)')(format=".") :marked - Notice the `[routerLink]` binding in the anchor tag. - We bind the `RouterLink` directive (another of the `ROUTER_DIRECTIVES`) to an array + Notice the `routerLink` binding in the anchor tag. + We bind the `RouterLink` directive (another of the `RouterModule` directives) to a string that tells the router where to navigate when the user clicks the link. - We define a *routing instruction* with a *link parameters array*. - The array only has one element in our little sample, the quoted ***path* of the route** to follow. + Since our link is not dynamic, we define a *routing instruction* with a **one-time binding** to our route **path**. Looking back at the route configuration, we confirm that `'/heroes'` is the path of the route to the `HeroesComponent`. .l-sub-section :marked - Learn about the *link parameters array* in the [Routing](../guide/router.html#link-parameters-array) chapter. + For more dynamic router links, learn about the *link parameters array* in the [Routing](../guide/router.html#link-parameters-array) chapter. :marked Refresh the browser. We see only the app title. We don't see the heroes list. .l-sub-section @@ -241,12 +260,18 @@ code-example(language="bash"). We’ll come back and make it more useful later. ### Configure the dashboard route - Go back to `app.routes.ts` and teach it to navigate to the dashboard. + Go back to `app.routing.ts` and teach it to navigate to the dashboard. Import the `DashboardComponent` so we can reference it in the dashboard route definition. - Add the following `'Dashboard'` route definition to the `RouterConfig` array of definitions. -+makeExample('toh-5/ts/app/app.routes.1.ts','dashboard-route', 'app/app.routes.ts (Dashboard route)')(format=".") + Add the following `'Dashboard'` route definition to the `Routes` array of definitions. ++makeExample('toh-5/ts/app/app.routing.2.ts','dashboard-route', 'app/app.routing.ts (Dashboard route)')(format=".") + +:marked + We also need to add the `DashboardComponent` to our root NgModule's `declarations`. + ++makeExample('toh-5/ts/app/app.module.3.ts','dashboard-declaration', 'app/app.module.ts (Dashboard declaration)')(format=".") + .l-sub-section :marked **Redirect** @@ -256,7 +281,7 @@ code-example(language="bash"). Remember that the browser launches with `/` in the address bar. We can use a redirect route to make this happen. -+makeExample('toh-5/ts/app/app.routes.1.ts','redirect-route', 'app/app.routes.ts (Redirect route)')(format=".") ++makeExample('toh-5/ts/app/app.routing.2.ts','redirect-route', 'app/app.routing.ts (Redirect route)')(format=".") .l-sub-section :marked @@ -265,7 +290,7 @@ code-example(language="bash"). :marked Finally, add a dashboard navigation link to the template, just above the *Heroes* link. -+makeExample('toh-5/ts/app/app.component.ts','template', 'app/app.component.ts (template)')(format=".") ++makeExample('toh-5/ts/app/app.component.3.ts','template', 'app/app.component.ts (template)')(format=".") .l-sub-section :marked We nestled the two links within `