Compare commits

..

195 Commits

Author SHA1 Message Date
4064cbe945 docs: add changelog for 5.1.0-beta.1 2017-11-16 14:04:25 -08:00
a88306d671 release: cut the 5.1.0-beta.1 release 2017-11-16 14:03:03 -08:00
c702ffc471 docs: add changelog for 5.0.2 2017-11-16 13:41:55 -08:00
dcfffbf828 build: fix build failures (#20470) 2017-11-15 22:33:30 -08:00
336041aac9 docs(aio): Add instructions to fork angular/angular (#20462)
PR Close #20462
2017-11-15 18:26:27 -06:00
7e38f4fd1f fix(aio): fix window title on Home page (#20440)
Using `display: none` on the `<h1>` causes `innerText` to not work as expected
and include the icon ligature (`link`) in the title. This caused the window
title on the angular.io Home page to appear as "Angular - link".
This commit fixes it by not generating anchors at all for headings with the
`no-anchor` class.

Fixes #20427

PR Close #20440
2017-11-15 18:25:58 -06:00
c28b52187a docs(aio): add missing closing backtick (#20446)
PR Close #20446
2017-11-15 18:25:49 -06:00
5ec1717c58 fix: Update test code to type-check under TS 2.5 (#20175)
PR Close #20175
2017-11-15 18:12:16 -06:00
c2a24b4241 fix(bazel): adjust mock of tsconfig for ng_module rule unit test (#20175)
Due to 7f33f01b72

PR Close #20175
2017-11-15 18:12:16 -06:00
a9f3e2bd95 build: Upgrade to TypeScript 2.5 (#20175)
- update to TypeScript 2.5
- point the 2.4 typings test at the previous typescript version, so we
  don't break it accidentally
- widen the peerDeps from Angular packages that depend on TypeScript
- update to latest TypeScript 2.5 compatible Bazel rules
- move .bazelrc to tools/bazel.rc per https://docs.bazel.build/versions/master/best-practices.html#bazelrc

PR Close #20175
2017-11-15 18:12:16 -06:00
f8658cdc38 Revert "fix(animations): always fire inner trigger callbacks even if blocked by parent animations (#19753)"
This reverts commit d47b2a6f70.
2017-11-15 17:04:22 -06:00
043e408805 style(core): fix comment format for linter 2017-11-15 11:53:11 -08:00
c004d483ab style(core): fix max line length to pass linting (#20441)
Accidentally introduced in #19920, where other linting errors (unrelated to the
PR) prevented proper linting.

PR Close #20441
2017-11-15 11:44:52 -08:00
2586846ee2 Revert "fix(core): should support event.stopImmediatePropagation"
This reverts commit 5e0eb5e3d94bd7077c4d6657b89bfc8d900f2bc6.
2017-11-15 11:35:21 -08:00
d47b2a6f70 fix(animations): always fire inner trigger callbacks even if blocked by parent animations (#19753)
Closes #19100

PR Close #19753
2017-11-14 15:59:47 -08:00
1adbcda12e docs(aio): fix wrong copy (#20431)
PR Close #20431
2017-11-14 15:58:25 -08:00
12af6d356e docs(core): change from deprecated renderer to renderer2 (#19920)
We now show the proper class instead of the deprecated Renderer

Fixes #19806


PR Close #19920
2017-11-14 13:44:56 -08:00
20aafff092 fix(animations): ensure final state() styles are applied within @.disabled animations (#20267)
Closes #20266

PR Close #20267
2017-11-14 11:01:11 -08:00
a622e19df6 fix(router): 'merge' queryParamHandling strategy should be able to remove query params (#19733)
Closes #18463, #17202

PR Close #19733
2017-11-14 11:01:04 -08:00
1db7c0d139 ci: tighten package limits (#20364)
PR Close #20364
2017-11-14 11:00:58 -08:00
b9bd3204f2 docs(aio): fix broken link in guide/component-interaction (#20411)
add a blank line before line with markdown link
PR Close #20411
2017-11-14 10:00:46 -08:00
bf651a504f docs(aio): make it clear we are talking about <a> tags (#20410)
As is, it could be seen as a typo at first glance. Wrapping the "a" in carets and backticks for formatting adds some clarity.

PR Close #20410
2017-11-14 10:00:42 -08:00
e5c4371d72 docs(aio): fix typo in filename (packages.json --> package.json) (#20377)
PR Close #20377
2017-11-14 10:00:39 -08:00
3caae94261 docs(aio): fix not found schema (#20347)
Fixes #20338

PR Close #20347
2017-11-14 10:00:35 -08:00
e7a2b31472 docs(aio): fix rxjs import (#20350)
closes #20349

PR Close #20350
2017-11-14 10:00:31 -08:00
c03186013c docs: Dependency Injection guides for CLI (#19892)
PR Close #19892
2017-11-14 10:00:28 -08:00
5a2531ee45 docs(aio): update template forms to CLI (#20014)
PR Close #20014
2017-11-14 10:00:23 -08:00
6ca780178c docs(aio): update reactive-forms to CLI (#20019)
PR Close #20019
2017-11-14 10:00:17 -08:00
9608b0636d docs(aio): update displaying-data for CLI (#19574)
PR Close #19574
2017-11-14 10:00:13 -08:00
89187d9b6b docs(aio): update glossary for CLI (#20017)
PR Close #20017
2017-11-14 10:00:10 -08:00
335b72f301 docs(aio): update ajs-quick-reference for CLI (#19552)
PR Close #19552
2017-11-14 10:00:02 -08:00
74071210eb docs(aio): remove trailing underscore from provide_ (#20356)
_provide_ was already emphasized in the previous paragraph

PR Close #20356
2017-11-14 09:59:56 -08:00
fde966832b docs(aio): fix missed grave accent (#20379)
PR Close #20379
2017-11-14 09:59:51 -08:00
75d474e1d3 docs: AttributeDirectives guide for CLI (#19771)
PR Close #19771
2017-11-14 09:59:43 -08:00
24cf8b3269 fix(core): ensure init lifecycle events are called (#20258)
Throwing an exception in a lifecycle event will delay but not
prevent an Init method, such as `ngOnInit`, `ngAfterContentInit`,
or `ngAfterViewInit`, from being called. Also, calling `detectChanges()`
in a way that causes duplicate change detection (such as a
child component causing a parent to call `detectChanges()` on its
own `ChangeDetectorRef`, will no longer prevent change `ngOnInit`,
`ngAfterContentInit` and `ngAfterViewInit` from being called.

With this change lifecycle methods are still not guarenteed to be
called but the Init methods will be called if at least one change
detection pass on its view is completed.

Fixes: #17035

PR Close #20258
2017-11-10 13:47:39 -08:00
743651f5e8 refactor(animations): uses a loop instead Array.map() which creates and (#19910)
returns a new array that is discarded.

This pattern will become a compilation error in google3.

PR Close #19910
2017-11-10 13:47:02 -08:00
161f88fe6f build: update to google-closure-compiler@20171023.0.1 (#20321)
added externs

remove externs

PR Close #20321
2017-11-10 11:51:57 -08:00
c33a57666b fix(compiler): recognize @NgModule with a redundant @Injectable (#20320)
The compiler now, again, recognizes `@NgModule()` decorators on
classes with a redundant `@Injectable()` decorator.

Fixes: #19544

PR Close #20320
2017-11-10 11:51:52 -08:00
cf618c564c fix(aio): markdown typo in Tour of Heroes tutorial (#20288)
PR Close #20288
2017-11-10 11:51:18 -08:00
401ead07b8 docs(common): update default display value for CurrencyPipe (#20246)
PR Close #20246
2017-11-10 11:51:17 -08:00
b55c2ba342 refactor(core): remove prolyfill from error message (#20121)
PR Close #20121
2017-11-10 11:51:15 -08:00
d8db0f12a2 docs(changelog): remove unnecessary revert item (#20303)
PR Close #20303
2017-11-10 11:51:13 -08:00
eb8013e853 fix(language-service): pass compilerOptions.paths to ReflectorHost (#20222)
This commit fixes the options passed to ReflectorHost to include 'paths'
if it's specified in compiler options, so that dependency modules can
be loaded.

PR Close #20222
2017-11-10 11:51:11 -08:00
fb4b90a564 docs(aio): typo fix (#20318)
tutorial part 0 app component template file extention fix

PR Close #20318
2017-11-10 11:12:07 -08:00
7830d74615 docs(aio): empty line between HTML tag and content (#20341)
PR Close #20341
2017-11-10 10:59:46 -08:00
8e24c0fff4 docs(aio): fix typo (#20103)
PR Close #20103
2017-11-10 10:59:35 -08:00
cf0444b731 docs(aio): show correct path for mock-heroes code (#20323)
PR Close #20323
2017-11-10 10:59:25 -08:00
a7bbe9a1ff docs(aio): Fix typo in tutorial (#20295)
PR Close #20295
2017-11-10 10:59:10 -08:00
ffe323036e docs(aio): fix toh-pt3 typos (#20307)
PR Close #20307
2017-11-10 10:59:01 -08:00
b4a39f9c30 docs(aio): fix -mm- to -MM- for month in DatePipe (#20315)
PR Close #20315
2017-11-10 10:58:45 -08:00
3257fcdcee fix(compiler): show explanatory text in template errors (#20313)
Fixes: #20076

PR Close #20313
2017-11-09 15:46:30 -08:00
9bcd7097d0 test(compiler): do not use a as a content selector
As it might trigger false positive in the RegExp from `_makeScopeMatcher`

closes #20256
2017-11-08 15:52:21 -08:00
c32f5fd393 fix(compiler): fix corner cases in shadow CSS
`cmp:host {}` and `cmp:host some-other-selector {}` were not handled
consistently.

Note those should not match anything but are made equivalent to respectively
`:host(cmp)` and `:host(cmp) some-other-selector` to avoid breaking legacy apps.
2017-11-08 15:20:19 -08:00
78ba39bfe2 docs: add changelog for 5.1.0-beta.0 2017-11-08 11:32:48 -08:00
119034c642 docs: add changelog for 5.0.1 2017-11-08 10:30:17 -08:00
6e8e3bd248 refactor(core): misc changes and integrate review feedback on #19996
closes #20224
2017-11-06 14:13:02 -08:00
a460066972 feat(compiler): introduce TestBed.overrideTemplateUsingTestingModule
This allows to overwrite templates for JIT and AOT components alike.

In contrast to `TestBed.overrideTemplate`, the template is compiled
in the context of the testing module, allowing to use other testing
directives.

Closes #19815
2017-11-06 14:12:30 -08:00
05d96dc507 feat(core): allow to pass in aot summaries also to TestBed.configureTestingModule
Also adds caching for summaries.

Closes #19817.
2017-11-06 14:12:30 -08:00
b489259a34 docs(common): fix mis-ordered lines (#20221) 2017-11-06 11:28:39 -08:00
6b748835be build(aio): upgrade to @anglar/core@5.0.0 and cli@1.5.0
-rw-r--r--  1 iminar  eng   14880 Nov  4 11:54
dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  4 11:54
dist/inline.9183bfe0d60f43b6a772.bundle.js
-rw-r--r--  1 iminar  eng  486476 Nov  4 11:54
dist/main.f5445f99490330207c9c.bundle.js
-rw-r--r--  1 iminar  eng   37070 Nov  4 11:54
dist/polyfills.0dfca732c5a075c110d0.bundle.js

Closes #20184
2017-11-06 10:04:43 -08:00
d30ea61f0d build(aio): remove the build-optimizer flag
it's on by default now.
2017-11-06 10:04:02 -08:00
0c47ea704e build(aio): use aot compiler for development
since it was not turned on by default in the cli we
have to opt in.
2017-11-06 10:04:02 -08:00
049c89645b docs(aio): update ToH for CLI (#19811) 2017-11-06 10:02:17 -08:00
bf22f2df88 fix(compiler): report a reasonable error with invalid metadata (#20062)
The compiler would throw an internal exception if an import using
the `ngModule` syntax and the module as not a resolvable symbol.

Fixes: #20049
2017-11-06 10:01:27 -08:00
880201681f fix(aio): style correctly on Safari (#20157) 2017-11-06 10:00:37 -08:00
63d26a1777 ci(aio): move e2e tests to optional job (#20178) 2017-11-06 09:59:59 -08:00
8b50ed083c docs(aio): fixed case-typo for code (#20196) 2017-11-06 09:59:17 -08:00
3997d97806 revert: feat(elements): implement @angular/elements #19469 (#20152)
This PR was merged without API docs and general rollout plan.

We can't release this as is in 5.1 without a plan for documentation, cli integration, etc.
2017-11-03 15:54:54 -07:00
200d92d030 fix(core): should support event.stopImmediatePropagation (#19222) 2017-11-03 15:22:05 -07:00
dbec3ca716 docs(aio): update pipes for CLI (#19553) 2017-11-03 15:21:14 -07:00
f7c9b941cb ci(aio): Add payload size percentage changes check to payload size task (#19908) 2017-11-03 15:20:09 -07:00
f0764016f9 revert: feat(core): add source to StaticInjectorError message (#19482) (#20171)
This reverts commit faa621218e.
2017-11-03 14:51:06 -07:00
a99eb16320 test(aio): make e2e tests less flakey
closes #20117
2017-11-03 11:22:57 -07:00
e36bac9e90 build(aio): upgrade protractor to v5.2.0 2017-11-03 11:22:41 -07:00
196ce6d475 build(aio): use correct types for e2e tests 2017-11-03 11:22:41 -07:00
faa621218e feat(core): add source to StaticInjectorError message (#19482) 2017-11-03 11:14:05 -07:00
169cedd43b docs: Bootstrapping guide prose for CLI (#19777) 2017-11-03 11:13:24 -07:00
567cc26b8e docs: Component Styles guide for CLI (#19791) 2017-11-03 11:12:34 -07:00
1d19d61970 fix(platform-browser): support Symbols in custom jasmineToString() method (#19794)
It's illegal to coerce a Symbol to a string, and results in a TypeError:

TypeError: Cannot convert a Symbol value to a string

Previously, the custom jasmineToString() method monkey-patched onto Maps
in platform-browser/testing/src/matchers.ts would coerce keys and values
to strings. A change in a newer version of Jasmine calls this method more
often, resulting in calls against Maps which contain Symbols in some
applications, which causes crashes.

The fix is to explicitly convert keys and values to strings, which does
work on Symbols.
2017-11-03 11:11:47 -07:00
03f080b7da docs: Deployment guide for CLI (#19839) 2017-11-03 11:10:53 -07:00
26f82995f6 fix(compiler): don't overwrite missingTranslation's value in JIT (#19952) 2017-11-03 11:08:39 -07:00
f1da1419fa docs(aio): Fix typo in Architecture Overview. (#20073) 2017-11-03 11:00:22 -07:00
5079d2d37c docs(aio): Add v4 doc link to navigation (#20089)
Fixes #20090
2017-11-03 10:59:27 -07:00
c7fd172ba7 build(aio): make plunker works with rxjs operators (#20124) 2017-11-03 10:47:23 -07:00
dcf8840831 feat(elements): implement registerAsCustomElements()
closes #19469
2017-11-02 16:09:30 -07:00
60c0b178af feat(elements): implement NgElements 2017-11-02 16:09:09 -07:00
0899f4f8fc feat(elements): implement NgElementConstructor 2017-11-02 16:09:09 -07:00
aed4a11d01 feat(elements): implement NgElement 2017-11-02 16:09:09 -07:00
75cf70ae04 feat(elements): implement NgElementApplicationContext 2017-11-02 16:09:09 -07:00
6b30fbf94e feat(elements): implement extractProjectableNodes() 2017-11-02 16:09:09 -07:00
24f17f913a feat(elements): implement utils 2017-11-02 16:09:09 -07:00
ebfa204af0 feat(elements): set up the elements package 2017-11-02 16:09:09 -07:00
a28d616e10 fix(core): __symbol__ should return __zone_symbol__ without zone.js loaded (#19541) 2017-11-02 16:06:06 -07:00
613a9e3672 build: update Windows symlink scripts (#16761)
Fixes packages\upgrade\static\src symlink.

Closes #16760
2017-11-02 16:05:13 -07:00
65d57a07e0 build(aio): avoid building before running the local PWA tests
When this command is run on CI, `yarn build` has already been run, so
this was unnecessarily building angular.io again (adding ~4mins to the
`aio` job).
When this command is run locally, it is most often about testing a new
`lighthouse` version/config, so you don't need to build angular.io over
and over (and if necessary, one can always run `yarn build` manually).

Closes #19633
2017-11-02 16:02:52 -07:00
22946cfd40 build(aio): upgrade lighthouse to v2.5.0 2017-11-02 16:02:25 -07:00
9975486954 build: remove local yarn (#19981)
We use the globally installed yarn now. The local yarn was used in
`check-environment.js` only, which results in the `--integrity` check
always failing (if dependencies were installed with the global yarn).
2017-11-02 15:11:34 -07:00
068348e9b1 build(aio): do not fail the initial doc-gen when running docs-watch
Checks that cause the doc-gen to fail were already disabled in `docs-watch`
for the doc-gen that runs when a file is changed.
Now these checks are also disabled for the initial doc-gen run.
closes #20038
2017-11-02 15:03:59 -07:00
1beab0da6a build(aio): do not fail on bad examples when running docs-watch 2017-11-02 15:00:22 -07:00
3a03ff6b2d build(aio): allow render-examples to complete even if examples are broken
By setting `renderExamples.ignoreBrokenExamples = true` the doc-gen will
not fail if there is something wrong with an example. Instead it will
just log a warning.
2017-11-02 15:00:22 -07:00
feae55b264 docs(aio): fix badly formatted code-examples
closes #19970
2017-11-02 14:59:05 -07:00
0355142737 build(aio): fail doc-gen if a code-example is badly formatted.
This will catch the problem that was missed in
https://github.com/angular/angular/pull/19845#issuecomment-338626662
2017-11-02 14:58:38 -07:00
5b16ce9302 fix(compiler-cli): don't report emit diagnostics when --noEmitOnError is off (#20063) 2017-11-02 14:49:38 -07:00
17ed14faea docs: fix changelog for v5 (#20097) 2017-11-02 14:37:39 -07:00
d156e72ad7 docs: Add 0 in padding of 'dd' (#20107) 2017-11-02 14:36:22 -07:00
7186c9c839 docs: AOT guide for CLI #19510 (#19818) 2017-11-02 14:34:21 -07:00
a41558eb30 docs: BrowserSupport guide for CLI (#19843) 2017-11-02 14:29:59 -07:00
a91252a90c docs: Npm Packages guide for CLI (#19850) 2017-11-02 14:27:53 -07:00
132c0719dc docs(aio): updated i18n guide and example (#19975) 2017-11-02 14:22:09 -07:00
3db7112b89 docs(aio): fix linting for universal (#20086)
PR Close #20086
2017-11-02 00:51:29 +01:00
2ea76cce31 build: change the version in package.json to 5.1.0-beta
this is needed to reflect that the current branch will be yielding 5.1.0 releases

some of our scripts (e.g. aio deployment) relies on this identifier.
2017-11-02 00:17:55 +01:00
b8bb2dd0f5 build(aio): update lockfile for examples
Installing dependencies for the docs examples fails, because the
lockfile is out-of-sync with the corresponding `package.json`.
This commit brings the lockfile in sync with `package.json`.

(For reference, this was accidentally broken in #20039.)
2017-11-02 00:15:59 +01:00
27ae0f9475 build(aio): upgrade to @angular/material@2.0.0-beta.12 (#19702)
-rw-r--r--  1 iminar  eng   14880 Nov  1 12:25 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  1 12:25 dist/inline.2826385ad3e299c6d1c1.bundle.js
-rw-r--r--  1 iminar  eng  486476 Nov  1 12:25 dist/main.f0610805f4aad19da4be.bundle.js
-rw-r--r--  1 iminar  eng   37070 Nov  1 12:25 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:48 -07:00
171dceb010 build(aio): upgrade to @angular/core@5.0.0-rc.9 (#19702)
build fails - material upgrade required

PR Close #19702
2017-11-01 15:24:48 -07:00
1ebc0d1e33 revert: build(aio): remove cli patches (#19702)
This reverts commit f0d530b4de38f71c759e42afc8f3d7531eb1b1fb.

cli rc.8 reintroduces the node polyfill which causes size regression,
so I'm putting the patch back in.

-rw-r--r--  1 iminar  eng   14880 Nov  1 12:11 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  1 12:11 dist/inline.25600c3b48de18b97581.bundle.js
-rw-r--r--  1 iminar  eng  486476 Nov  1 12:11 dist/main.d1292a34401056535884.bundle.js
-rw-r--r--  1 iminar  eng   37070 Nov  1 12:11 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:47 -07:00
1d8e0758fa build(aio): upgrade to @angular/cli@1.5.0-rc.8 (#19702)
-rw-r--r--  1 iminar  eng   14880 Nov  1 11:57 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Nov  1 11:57 dist/inline.3574d1d784c09c507dbd.bundle.js
-rw-r--r--  1 iminar  eng  497812 Nov  1 11:57 dist/main.76bbb69df79eaefef54c.bundle.js
-rw-r--r--  1 iminar  eng   37259 Nov  1 11:57 dist/polyfills.fdb71956ccd13330fb47.bundle.js

PR Close #19702
2017-11-01 15:24:47 -07:00
cd55643f85 build(aio): upgrade to @angular/cli@1.5.0-rc.6 (#19702)
-rw-r--r--  1 iminar  eng   14880 Oct 30 11:29 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1533 Oct 30 11:29 dist/inline.25600c3b48de18b97581.bundle.js
-rw-r--r--  1 iminar  eng  486476 Oct 30 11:29 dist/main.d1292a34401056535884.bundle.js
-rw-r--r--  1 iminar  eng   37070 Oct 30 11:29 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:47 -07:00
486b8e6f69 ci(aio): decrease payload size limit for main file (#19702)
PR Close #19702
2017-11-01 15:24:47 -07:00
215d373ebd refactor(aio): rename CustomMdIconRegistry to CustomIconRegistry (#19702)
The change of Angular Material version means that the `md` prefix is
no longer appropriate.

PR Close #19702
2017-11-01 15:24:47 -07:00
4038b42396 build(aio): lock zone.js to 0.8.16 (#19702)
Later versions (before 0.8.19) had a size increase.

PR Close #19702
2017-11-01 15:24:46 -07:00
9ab1f4a9c9 style(aio): fix docs linting issues (#19702)
These issues appeared after upgrade of eslint jasmine plugin

PR Close #19702
2017-11-01 15:24:46 -07:00
e9afc59a81 ci(aio): increase payload size limit for polyfills.ts (#19702)
The latest builds have added ~2kB to the size of this file (500 bytes zipped).

PR Close #19702
2017-11-01 15:24:46 -07:00
83c826c3f9 build(aio): remove hack to modify CLI version (#19702)
PR Close #19702
2017-11-01 15:24:46 -07:00
abfc41d661 build(aio): upgrade to Angular@rc.5 and CLI@rc.3 (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
11fd7eaf63 build(aio): revert temporary increase in size limit (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
54eba606cb build(aio): fix tests to work with @angular/{material,cdk}@2.0.0-beta.12 (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
3337865913 build(aio): remove cli patches (#19702)
PR Close #19702
2017-11-01 15:24:45 -07:00
f24397c5d0 build(aio): revert to clean CLI test.ts file (#19702)
The use of `System.import()` in test.ts was causing the webpack build to fail
with a mysterious "Module build failed: Error: TypeScript compilation failed" error,
when running `yarn test`.

PR Close #19702
2017-11-01 15:24:44 -07:00
9d52bf27de build(aio): temporarily increaze the size limit until the regressions are fixed (#19702)
related issues:
https://github.com/angular/angular/issues/19857
https://github.com/angular/devkit/pull/231

PR Close #19702
2017-11-01 15:24:44 -07:00
19fbfbc371 build(aio): disable 'global' support in webpack (#19702)
This will be fixed in CLI once https://github.com/angular/angular-cli/pull/8130 lands.

-rw-r--r--  1 iminar  eng   14942 Oct 20 22:23 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 20 22:23 dist/inline.5d66b81ec9e01af9d28d.bundle.js
-rw-r--r--  1 iminar  eng  528395 Oct 20 22:23 dist/main.e36bb99245ca52ae546f.bundle.js
-rw-r--r--  1 iminar  eng   37205 Oct 20 22:23 dist/polyfills.0dfca732c5a075c110d0.bundle.js

PR Close #19702
2017-11-01 15:24:44 -07:00
6578b30b77 build(aio): upgrade to build-optimizer@0.0.29 (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 20 22:16 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 20 22:16 dist/inline.68ebcf831dc9c905804f.bundle.js
-rw-r--r--  1 iminar  eng  541291 Oct 20 22:16 dist/main.5ec6fb5f95fc0433d822.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 20 22:16 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:44 -07:00
10c1c2ba5a build(aio): upgrade to @angular/cli@1.5.0-rc.2 (#19702)
-rw-r--r--  1 iminar  eng   84219 Oct 19 21:37 dist/0.0f327734d18211139822.chunk.js
-rw-r--r--  1 iminar  eng   14942 Oct 19 21:37 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1560 Oct 19 21:37 dist/inline.887757679ff553e20b54.bundle.js
-rw-r--r--  1 iminar  eng  492354 Oct 19 21:37 dist/main.5e5dc9ed980c9f5dc2bd.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 19 21:37 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:44 -07:00
e18bfd1f3d build(aio): upgrade to @angular/cli@1.5.0-rc.1 (#19702)
-rw-r--r--  1 iminar  eng   84219 Oct 18 21:05 dist/0.0f327734d18211139822.chunk.js
-rw-r--r--  1 iminar  eng   14942 Oct 18 21:05 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1560 Oct 18 21:05 dist/inline.887757679ff553e20b54.bundle.js
-rw-r--r--  1 iminar  eng  492354 Oct 18 21:05 dist/main.5e5dc9ed980c9f5dc2bd.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 18 21:05 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
3c2ddbe9ab build(aio): upgrade to rxjs@5.5.0 (#19702)
-rw-r--r--  1 iminar  eng   84219 Oct 18 09:13 dist/0.8ef208c27531d5c6af63.chunk.js
-rw-r--r--  1 iminar  eng   14942 Oct 18 09:13 dist/4.c719ac5645940382cdce.chunk.js
-rw-r--r--  1 iminar  eng    1560 Oct 18 09:13 dist/inline.adc367eb50c706f3fd04.bundle.js
-rw-r--r--  1 iminar  eng  492354 Oct 18 09:13 dist/main.b9d9549455c74aff1480.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 18 09:13 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
a149605c9f build(aio): turn off preserveWhitespaces in compiler options (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 16:12 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 16:12 dist/inline.eede8140efeab4c45b22.bundle.js
-rw-r--r--  1 iminar  eng  559389 Oct 13 16:12 dist/main.20858f9aa7cf8741b6aa.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 16:12 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
b396029d39 build(aio): patch @angular/cli to use esm builds of rxjs (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 14:52 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 14:52 dist/inline.6ca24f1c3b848103b041.bundle.js
-rw-r--r--  1 iminar  eng  567802 Oct 13 14:52 dist/main.c8183a2c0116782ca366.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 14:52 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:43 -07:00
040b376052 build(aio): upgrade to rxjs@5.5.0-beta.7 (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 14:30 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 14:30 dist/inline.702d6ff5146ddc373f05.bundle.js
-rw-r--r--  1 iminar  eng  588943 Oct 13 14:30 dist/main.64c96d55a10c56cfd6cd.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 14:30 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:42 -07:00
175c872514 build(aio): upgrade to @angular/{material,cdk}@2.0.0-beta.12 (#19702)
-rw-r--r--  1 iminar  eng   14942 Oct 13 13:35 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 13:35 dist/inline.f005f1bd6803b72f5961.bundle.js
-rw-r--r--  1 iminar  eng  582527 Oct 13 13:35 dist/main.b9ef1abb785be8de15b8.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 13:35 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:42 -07:00
71291aa2c0 fix(aio): hand fix the renaming md->mat issues (#19702)
These are changes that the mat-switcher missed and I had to make them by hand.

PR Close #19702
2017-11-01 15:24:42 -07:00
415e75716a build(aio): upgrade to @angular/{material,cdk}@2.0.0-beta.11 + md->mat migration (#19702)
all the non-npm changes were made by the angular-material-prefix-updater tool.

the tool missed a few things, which I'll fix in a separate commit to preserve the diff.

-rw-r--r--  1 iminar  eng   14942 Oct 13 13:09 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 13:09 dist/inline.0592c25ceb544d6aca3d.bundle.js
-rw-r--r--  1 iminar  eng  578250 Oct 13 13:09 dist/main.45d4edca3facc6d621e7.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 13:09 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:42 -07:00
3216abee2e build(aio): upgrade to @angular/cli@1.5.0-rc.0 and typescript@2.5.3 (#19702)
the size regression has gotten worse:

-rw-r--r--  1 iminar  eng   14942 Oct 13 12:24 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 12:24 dist/inline.41e701c562960ede8ef5.bundle.js
-rw-r--r--  1 iminar  eng  865780 Oct 13 12:24 dist/main.6c4c605d461870b9c2d7.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 12:24 dist/polyfills.f8409a9eb69060ac1aa6.bundle.js

PR Close #19702
2017-11-01 15:24:41 -07:00
327ad02a28 build(aio): upgrade to @angular/cli@1.4.7 (#19702)
this causes the size regression to get only worse:

-rw-r--r--  1 iminar  eng   14942 Oct 13 10:37 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 10:37 dist/inline.3cc964095cb25e329dc0.bundle.js
-rw-r--r--  1 iminar  eng  846141 Oct 13 10:37 dist/main.5eb64df77b2877327a16.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 10:37 dist/polyfills.965a9a5ad3e11b17f85e.bundle.js

PR Close #19702
2017-11-01 15:24:41 -07:00
1df0c9e1b0 build(aio): update to @angular/core@5.0.0-rc.2 (#19702)
there is a size regression right now because the CLI is out of date:

-rw-r--r--  1 iminar  eng   14942 Oct 13 10:23 dist/0.b19e913fbdd6507d346b.chunk.js
-rw-r--r--  1 iminar  eng    1535 Oct 13 10:23 dist/inline.812a4af83ecce165c71c.bundle.js
-rw-r--r--  1 iminar  eng  643481 Oct 13 10:23 dist/main.74550bb35f9f5a57e78a.bundle.js
-rw-r--r--  1 iminar  eng   37402 Oct 13 10:23 dist/polyfills.965a9a5ad3e11b17f85e.bundle.js

PR Close #19702
2017-11-01 15:24:41 -07:00
280dadaaad docs(aio): fix typo in attribute-directives
Closes #20071
2017-11-01 15:21:09 -07:00
c30eff898a docs(aio): update universal for CLI
PR Close #20039
2017-11-01 15:18:43 -07:00
ca129ba549 docs(aio): upgrade to v5 and cli 1.5
PR Close #20077
2017-11-01 14:14:08 -07:00
171ae154c2 docs(packaging): fix typo
PR Close #18915
2017-11-01 15:45:58 -04:00
4426c0f14e fix(docs): Fix 404 on amcdnl image
PR Close #19120
2017-11-01 15:45:28 -04:00
901436e46f docs: fix link texts
Fixes #19701

PR Close #19709
2017-11-01 15:44:59 -04:00
6fbc2b3be0 docs(aio): update AngularJS Quick Reference guide
PR Close #19939
2017-11-01 15:44:35 -04:00
93d23ddcc8 docs(aio): fix typo
PR Close #20029
2017-11-01 15:42:25 -04:00
548a809a05 docs(aio): fix typo
PR Close #20042
2017-11-01 15:41:57 -04:00
a161b4ab6d docs(aio): fix typo
PR Close #20069
2017-11-01 15:41:27 -04:00
60b91656cd docs: add changelog for 5.0.0 2017-11-01 11:00:39 -07:00
1858d99559 release: cut the 5.0.0 release 2017-11-01 09:47:02 -07:00
3a8665409d build(aio): add i18n boilerplate type (#20004)
PR Close #20004
2017-10-31 01:06:25 -04:00
005a78bd83 refactor: allow compilation with TypeScript 2.5 (#19966)
A small number of types need to be adjusted. The changes seem to be
backwards compatible with TS 2.4.

PR Close #19966
2017-10-31 00:26:43 -04:00
7553ce9dfe docs: add changelog for 5.0.0-rc.9 2017-10-30 21:24:59 -07:00
42bfe4477f release: cut the 5.0.0-rc.9 release 2017-10-30 21:24:54 -07:00
3bdbb18c8b build: update rollup lint rule from bad merge (#20047)
PR Close #20047
2017-10-30 23:43:49 -04:00
13f8648a00 fix: add missing globals from each rollup configuration (#20028)
PR Close #20028
2017-10-30 23:09:17 -04:00
b6abcb2500 refactor: make all rollup config ES5 compatible (#20028)
So they can be required by other Node scripts.

PR Close #20028
2017-10-30 23:09:17 -04:00
f1a9e1e361 build: add lint rule for global flags in rollup config (#20028)
We now verify that every imports is part of the globals defined in the files rollup.config.js.

PR Close #20028
2017-10-30 23:09:16 -04:00
54480f7dfc fix(compiler): report errors properly in G3 in certain conditions (#20041)
Condition: static analysis error, given:
- noResolve:true
- generateCodeForLibraries: false
- CompilerHost.getSourceFile throws on non existent files

All of these are true in G3.
PR Close #20041
2017-10-30 21:24:30 -04:00
951bd33b09 build: add release as a valid commit message subject (#19955)
PR Close #19955
2017-10-30 21:23:18 -04:00
bf57df3e04 build: temporarily increase the commit msg limit to 120
Right now HEAD and 5.0.x have a branch deviation and therefore
all the commits between both branches are being compared. There
exists a problematic commit which has a commit message that is
longer than 100 commits. This patch will temporarily increase
the limit to 120 so that CI passes. Once master is resumed to
being the primary development branch (once 5.0.0 is out) then
the the msg limit will be set back to 100.
2017-10-30 21:20:43 -04:00
420852e2f5 fix(compiler): reexport less symbols in .ngfactory.ts files (#19884)
* don't reexport symbols that the user already reexported
* never reexport symbols that are part of arguments of non simple function calls

Fixes #19883

PR Close #19884
2017-10-30 20:11:29 -04:00
04eb80cc2b fix(compiler): always use relative paths to refer to generated code
Previously we generated imports like `@angular/material/index.ngfactory`,
which doesn’t make sense as we don’t ship generated code on npm

Closes #20031
2017-10-30 18:28:25 -04:00
308fc8e328 docs: add changelog for 5.0.0-rc.8 2017-10-27 23:46:02 -07:00
9bb2939d68 release: cut the 5.0.0-rc.8 release 2017-10-27 23:45:52 -07:00
2f63899be2 docs: ensure examples get rxjs ^5.5.0 (#19985)
This change coincidentally updates other packages that were in `package.json`
because it regenerates `yarn.lock`. This too should be fine.

PR Close #19985
2017-10-27 22:27:27 -07:00
22c66f0e02 fix(compiler-cli): avoid producing source mappings for host views (#19965)
The host view doesn't map back to user code so the template compiler
produces a blank `url` for them.

PR Close #19965
2017-10-27 22:26:57 -07:00
00bc80bb37 fix(platform-server): add missing packages to the UMD global rollup config
This adds the proper bindings for calling angular packages from platform-server in the UMD.
This was not a problem for universal apps that dont use UMD.

Fixes 19899
2017-10-27 22:26:25 -07:00
394d951883 docs: add changelog for 5.0.0-rc.7 2017-10-26 19:47:31 -07:00
98b26381f6 release: cut the 5.0.0-rc.7 release 2017-10-26 19:47:24 -07:00
31797d3b50 fix(compiler): make watch mode work on windows (#19953)
Fixes #19951
PR Close #19953
2017-10-26 18:43:00 -04:00
957be960d2 fix(compiler): recover from structural errors in watch mode (#19953)
This also changes the compiler so that we throw less often
on structural changes and produce a meaningful state
in the `ng.Program` in case of errors.

Related to #19951

PR Close #19953
2017-10-26 18:43:00 -04:00
18e9d86a3b fix(compiler): translate emit diagnostics with noEmitOnError: true. (#19953)
This prevents errors reported against `.ngfactory.ts` files show up
as the result of running `ngc`.

Closes #19935
PR Close #19953
2017-10-26 18:42:59 -04:00
a869aeecd2 fix(compiler): don’t store invalid state when using listLazyRoutes (#19953)
Previously, `listLazyRoute` would store invalid information in a compiler
internal cache, which lead to incorrect paths that were used during emit.
This commit fixes this.

PR Close #19953
2017-10-26 18:42:59 -04:00
eca822b756 fix(service-worker): fix improper call of Observable.merge (#19962)
Observable.merge was called using .call() as if it were an operator
and not an Observable factory. This removes the .call() and uses
the factory properly.

PR Close #19962
2017-10-26 18:09:53 -04:00
17142a778a fix(service-worker): don't block initialization on registration (#19936)
Importing ServiceWorkerModule.register() will schedule registration of
the Service Worker inside an APP_INITIALIZER. Previously, the Promise
returned by navigator.serviceWorker.register() was returned from the
initializer function. This has the unwanted side effect of blocking
initialization until the SW is registered. Even worse, if the SW script
fails to load, this can cause the app initialization to fail.

The solution is to not return the registration promise from the
initializer function, essentially decoupling registration from the rest
of the initialization flow.

This change is not unit testable as there are no mocks/adapters yet for
navigator.serviceWorker. A future integration test should cover this case
with better fidelity.

PR Close #19936
2017-10-26 16:05:07 -04:00
5adb7c9669 fix(service-worker): listen for messages on the right event source (#19954)
Currently, the SwUpdate service doesn't receive messages from the SW.
This is because it attempts to subscribe to the 'message' event on
ServiceWorkerRegistration, when really messages are emitted by the
ServiceWorkerContainer.

This change moves to listening on ServiceWorkerContainer and changes
the mocks to reflect the way the browser actually works.

PR Close #19954
2017-10-26 16:04:59 -04:00
703fcda590 docs: add changelog for 5.0.0-rc.6 2017-10-25 14:49:27 -07:00
f16a7cd7e3 release: cut the 5.0.0-rc.6 release 2017-10-25 14:48:41 -07:00
fde5f2fa14 build: update to rxjs@5.5.2 (#19931)
PR Close #19931
2017-10-25 14:27:05 -04:00
d56724659f fix(compiler): automatically set emitDecoratorMetadata when "annotationsAs": "static fields” (#19927)
This is a workaround for https://github.com/angular/tsickle/issues/635.

Fixes #19916
PR Close #19927
2017-10-25 13:38:39 -04:00
56b18ff063 fix(compiler-cli): produce correct paths for windows output (#19915)
The path mapping was broken for Windows by fc0b1d5b61.
Fixed the path mapping and put code in place to make such a problem
to sneek by again.

PR Close #19915
2017-10-24 17:54:58 -04:00
7bfeac746e fix(compiler-cli): only use error collector when needed. (#19912)
The error collector changes behavior of the metadata resolver
in ways that haven't been fully hardened. This changes limits
its use to the lazy route detection and the language service.

Issue: #19906

PR Close #19912
2017-10-24 17:06:41 -04:00
c92efc15fb fix(compiler): don’t type check templates with skipTemplateCodegen (#19909)
This change is needed to prevent users’ builds from breaking.

If a user sets `fullTemlateTypeCheck` to true, we will
continue to check the templates even when `skipTemplateCodegen` is true
as well.

Related to #19906

PR Close #19909
2017-10-24 17:06:34 -04:00
b51d57deb8 docs: add changelog for 5.0.0-rc.5 2017-10-23 23:50:31 -07:00
c357b40dca release: cut the 5.0.0-rc.5 release 2017-10-23 23:50:25 -07:00
a0ae120093 fix(compiler-cli): report all diagnostic error messages (#19886)
This fixes a problem introduced in 8d45fefc31
which modified how diagnostic error messages are reported for structural
metadata errors causing some of the diagnostics to be lost.

PR Close #19886
2017-10-23 22:41:10 -04:00
d3211a2468 feat(router): add "onSameUrlNavigation" router configuration option (#19463)
PR Close #19463
2017-10-23 20:56:53 -04:00
adab4f3e49 fix(router): do not call location.go when skipping a navigation (#19463)
Closes #18036

PR Close #19463
2017-10-23 20:56:52 -04:00
82fed62af2 fix(router): navigating to the current location works (#19463)
Closes #13340

PR Close #19463
2017-10-23 20:56:52 -04:00
188 changed files with 2550 additions and 2076 deletions

View File

@ -1,18 +0,0 @@
# Make compilation fast, by keeping a few copies of the compilers
# running as daemons, and cache SourceFile AST's to reduce parse time.
build --strategy=TypeScriptCompile=worker
build --strategy=AngularTemplateCompile=worker
# Don't create bazel-* symlinks in the WORKSPACE directory.
# These require .gitignore and may scare users.
# Also, it's a workaround for https://github.com/bazelbuild/rules_typescript/issues/12
# which affects the common case of having `tsconfig.json` in the WORKSPACE directory.
#
# Instead, you should run `bazel info bazel-bin` to find out where the outputs went.
build --symlink_prefix=/
# Performance: avoid stat'ing input files
build --watchfs
# Don't print all the .d.ts output locations after builds
build --show_result=0

View File

@ -1,3 +1,52 @@
<a name="5.1.0-beta.1"></a>
# [5.1.0-beta.1](https://github.com/angular/angular/compare/5.1.0-beta.0...5.1.0-beta.1) (2017-11-16)
### Bug Fixes
* **animations:** always fire inner trigger callbacks even if blocked by parent animations ([#19753](https://github.com/angular/angular/issues/19753)) ([d47b2a6](https://github.com/angular/angular/commit/d47b2a6)), closes [#19100](https://github.com/angular/angular/issues/19100)
* **animations:** ensure final state() styles are applied within @.disabled animations ([#20267](https://github.com/angular/angular/issues/20267)) ([20aafff](https://github.com/angular/angular/commit/20aafff)), closes [#20266](https://github.com/angular/angular/issues/20266)
* **bazel:** adjust mock of tsconfig for ng_module rule unit test ([#20175](https://github.com/angular/angular/issues/20175)) ([c2a24b4](https://github.com/angular/angular/commit/c2a24b4))
* **compiler:** fix corner cases in shadow CSS ([c32f5fd](https://github.com/angular/angular/commit/c32f5fd))
* **compiler:** recognize @NgModule with a redundant @Injectable ([#20320](https://github.com/angular/angular/issues/20320)) ([c33a576](https://github.com/angular/angular/commit/c33a576))
* **compiler:** show explanatory text in template errors ([#20313](https://github.com/angular/angular/issues/20313)) ([3257fcd](https://github.com/angular/angular/commit/3257fcd))
* **core:** ensure init lifecycle events are called ([#20258](https://github.com/angular/angular/issues/20258)) ([24cf8b3](https://github.com/angular/angular/commit/24cf8b3))
* **language-service:** pass compilerOptions.paths to ReflectorHost ([#20222](https://github.com/angular/angular/issues/20222)) ([eb8013e](https://github.com/angular/angular/commit/eb8013e))
* **router:** 'merge' queryParamHandling strategy should be able to remove query params ([#19733](https://github.com/angular/angular/issues/19733)) ([a622e19](https://github.com/angular/angular/commit/a622e19)), closes [#18463](https://github.com/angular/angular/issues/18463) [#17202](https://github.com/angular/angular/issues/17202)
* Update test code to type-check under TS 2.5 ([#20175](https://github.com/angular/angular/issues/20175)) ([5ec1717](https://github.com/angular/angular/commit/5ec1717))
<a name="5.0.2"></a>
## [5.0.2](https://github.com/angular/angular/compare/5.0.1...5.0.2) (2017-11-16)
### Bug Fixes
* **animations:** ensure final state() styles are applied within @.disabled animations ([#20267](https://github.com/angular/angular/issues/20267)) ([8b1a6b1](https://github.com/angular/angular/commit/8b1a6b1)), closes [#20266](https://github.com/angular/angular/issues/20266)
* **compiler:** fix corner cases in shadow CSS ([5d1cd57](https://github.com/angular/angular/commit/5d1cd57))
* **compiler:** recognize @NgModule with a redundant @Injectable ([#20320](https://github.com/angular/angular/issues/20320)) ([4cc6abb](https://github.com/angular/angular/commit/4cc6abb))
* **compiler:** show explanatory text in template errors ([#20313](https://github.com/angular/angular/issues/20313)) ([424a323](https://github.com/angular/angular/commit/424a323))
* **router:** 'merge' queryParamHandling strategy should be able to remove query params ([#19733](https://github.com/angular/angular/issues/19733)) ([b732fb9](https://github.com/angular/angular/commit/b732fb9)), closes [#18463](https://github.com/angular/angular/issues/18463) [#17202](https://github.com/angular/angular/issues/17202)
<a name="5.1.0-beta.0"></a>
# [5.1.0-beta.0](https://github.com/angular/angular/compare/5.0.0-rc.4...5.1.0-beta.0) (2017-11-08)
### Bug Fixes
* **compiler:** don't overwrite missingTranslation's value in JIT ([#19952](https://github.com/angular/angular/issues/19952)) ([799cbb9](https://github.com/angular/angular/commit/799cbb9))
* **compiler:** report a reasonable error with invalid metadata ([#20062](https://github.com/angular/angular/issues/20062)) ([da22c48](https://github.com/angular/angular/commit/da22c48))
* **compiler-cli:** don't report emit diagnostics when `--noEmitOnError` is off ([#20063](https://github.com/angular/angular/issues/20063)) ([8639995](https://github.com/angular/angular/commit/8639995))
* **core:** `__symbol__` should return `__zone_symbol__` without zone.js loaded ([#19541](https://github.com/angular/angular/issues/19541)) ([678d1cf](https://github.com/angular/angular/commit/678d1cf))
* **core:** should support event.stopImmediatePropagation ([#19222](https://github.com/angular/angular/issues/19222)) ([7083791](https://github.com/angular/angular/commit/7083791))
* **platform-browser:** support Symbols in custom `jasmineToString()` method ([#19794](https://github.com/angular/angular/issues/19794)) ([5a6efa7](https://github.com/angular/angular/commit/5a6efa7))
### Features
* **compiler:** introduce `TestBed.overrideTemplateUsingTestingModule` ([a460066](https://github.com/angular/angular/commit/a460066)), closes [#19815](https://github.com/angular/angular/issues/19815)
<a name="5.0.1"></a>
## [5.0.1](https://github.com/angular/angular/compare/5.0.0...5.0.1) (2017-11-08)

View File

@ -69,36 +69,37 @@ You can file new issues by filling out our [new issue form](https://github.com/a
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
Before you submit your Pull Request (PR) consider the following guidelines:
* Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
that relates to your submission. You don't want to duplicate effort.
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
We cannot accept code without this.
* Make your changes in a new git branch:
1. Fork the angular/angular repo.
1. Make your changes in a new git branch:
```shell
git checkout -b my-fix-branch master
```
* Create your patch, **including appropriate test cases**.
* Follow our [Coding Rules](#rules).
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
1. Create your patch, **including appropriate test cases**.
1. Follow our [Coding Rules](#rules).
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
and ensure that all tests pass.
* Commit your changes using a descriptive commit message that follows our
1. Commit your changes using a descriptive commit message that follows our
[commit message conventions](#commit). Adherence to these conventions
is necessary because release notes are automatically generated from these messages.
```shell
git commit -a
```
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
* Push your branch to GitHub:
1. Push your branch to GitHub:
```shell
git push origin my-fix-branch
```
* In GitHub, send a pull request to `angular:master`.
1. In GitHub, send a pull request to `angular:master`.
* If we suggest changes then:
* Make the required updates.
* Re-run the Angular test suites to ensure tests are still passing.

View File

@ -5,8 +5,7 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
git_repository(
name = "build_bazel_rules_nodejs",
remote = "https://github.com/bazelbuild/rules_nodejs.git",
# TODO(alexeagle): use the correct tag here.
commit = "2c6243df53fd33fdab283ebdd01582e4eb815db8",
commit = "0.2.1",
)
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")

View File

@ -0,0 +1,62 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"project": {
"name": "angular.io-example"
},
"apps": [
{
"root": "src",
"outDir": "dist",
"assets": [
"assets",
"favicon.ico"
],
"index": "index.html",
"main": "main.ts",
"polyfills": "polyfills.ts",
"test": "test.ts",
"tsconfig": "tsconfig.app.json",
"testTsconfig": "tsconfig.spec.json",
"prefix": "app",
// #docregion styles
"styles": [
"styles.css"
],
// #enddocregion styles
"scripts": [],
"environmentSource": "environments/environment.ts",
"environments": {
"dev": "environments/environment.ts",
"prod": "environments/environment.prod.ts"
}
}
],
"e2e": {
"protractor": {
"config": "./protractor.conf.js"
}
},
"lint": [
{
"project": "src/tsconfig.app.json",
"exclude": "**/node_modules/**"
},
{
"project": "src/tsconfig.spec.json",
"exclude": "**/node_modules/**"
},
{
"project": "e2e/tsconfig.e2e.json",
"exclude": "**/node_modules/**"
}
],
"test": {
"karma": {
"config": "./karma.conf.js"
}
},
"defaults": {
"styleExt": "css",
"component": {}
}
}

View File

@ -5,9 +5,6 @@
<meta charset="UTF-8">
<title>AngularJS to Angular Quick Reference</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- #docregion style -->
<link rel="stylesheet" href="styles.css">
<!-- #enddocregion style -->
</head>
<body>

View File

@ -4,7 +4,7 @@
"files":[
"!**/*.d.ts",
"!**/*.js",
"!app/*.[1,2,3].*"
"!app/*.[0,1,2,3].*"
],
"tags": ["attribute", "directive"]
}

View File

@ -0,0 +1,9 @@
// #docregion
import { Directive } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor() { }
}

View File

@ -1,8 +1,10 @@
/* tslint:disable:no-unused-variable */
// #docregion
import { Directive, ElementRef, Input } from '@angular/core';
import { Directive, ElementRef } from '@angular/core';
@Directive({ selector: '[appHighlight]' })
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = 'yellow';

View File

@ -1,7 +1,10 @@
/* tslint:disable:no-unused-variable member-ordering */
// #docplaster
// #docregion imports,
import { Directive, ElementRef, HostListener } from '@angular/core';
// #enddocregion imports,
import { Input } from '@angular/core';
// #docregion
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
@Directive({
selector: '[appHighlight]'
@ -35,7 +38,7 @@ export class HighlightDirective {
// #enddocregion color
// #docregion color-2
@Input() myHighlight: string;
@Input() appHighlight: string;
// #enddocregion color-2
}

View File

@ -1,6 +1,7 @@
/* tslint:disable:member-ordering */
// #docregion
// #docregion, imports
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
// #enddocregion imports
@Directive({
selector: '[appHighlight]'

View File

@ -1,7 +1,5 @@
/* tslint:disable:member-ordering */
// #docregion imports,
import { Directive, ElementRef, HostListener, Input } from '@angular/core';
// #enddocregion imports
@Directive({
selector: '[appHighlight]'

View File

@ -4,7 +4,8 @@
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1,2].*"
"!**/*.[0,1,2].*",
"**/dummy.module.ts"
],
"tags": ["dependency", "di"]
}

View File

@ -0,0 +1,10 @@
/*
Must put this interface in its own file instead of app.config.ts
or else TypeScript gives a (bogus) warning:
WARNING in ./src/app/... .ts
"export 'AppConfig' was not found in './app.config'
*/
export interface AppConfig {
apiEndpoint: string;
title: string;
}

View File

@ -1,7 +1,5 @@
// Early versions
// #docregion
import { Component } from '@angular/core';
import { Component } from '@angular/core';
@Component({
selector: 'app-root',

View File

@ -1,7 +1,6 @@
// #docregion
// #docregion imports
import { Component } from '@angular/core';
import { Inject } from '@angular/core';
import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
// #enddocregion imports
@ -23,3 +22,5 @@ export class AppComponent {
}
// #enddocregion ctor
}
// #enddocregion

View File

@ -4,7 +4,6 @@
import { Component, Inject } from '@angular/core';
import { APP_CONFIG, AppConfig } from './app.config';
import { Logger } from './logger.service';
import { UserService } from './user.service';
// #enddocregion imports
@ -23,8 +22,7 @@ import { UserService } from './user.service';
<app-heroes id="authorized" *ngIf="isAuthorized"></app-heroes>
<app-heroes id="unauthorized" *ngIf="!isAuthorized"></app-heroes>
<app-providers></app-providers>
`,
providers: [Logger]
`
})
export class AppComponent {
title: string;

View File

@ -1,15 +1,13 @@
import { AppConfig } from './app-config';
export { AppConfig } from './app-config';
// #docregion token
import { InjectionToken } from '@angular/core';
export let APP_CONFIG = new InjectionToken<AppConfig>('app.config');
export const APP_CONFIG = new InjectionToken<AppConfig>('app.config');
// #enddocregion token
// #docregion config
export interface AppConfig {
apiEndpoint: string;
title: string;
}
export const HERO_DI_CONFIG: AppConfig = {
apiEndpoint: 'api.heroes.com',
title: 'Dependency Injection'

View File

@ -1,32 +1,24 @@
// #docplaster
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
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 { Logger } from './logger.service';
import { TestComponent } from './test.component';
import { APP_CONFIG, HERO_DI_CONFIG } from './app.config';
import { UserService } from './user.service';
import {
ProvidersComponent,
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
} from './providers.component';
import { ProvidersModule } from './providers.module';
// #docregion ngmodule
@NgModule({
imports: [
BrowserModule
BrowserModule,
ProvidersModule
],
declarations: [
AppComponent,
@ -35,26 +27,19 @@ import {
// #enddocregion ngmodule
HeroListComponent,
InjectorComponent,
TestComponent,
ProvidersComponent,
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
TestComponent
// #docregion ngmodule
],
// #docregion ngmodule-providers
// #docregion providers, providers-2
providers: [
// #enddocregion providers
Logger,
// #docregion providers
UserService,
{ provide: APP_CONFIG, useValue: HERO_DI_CONFIG }
],
// #enddocregion ngmodule-providers
// #enddocregion providers, providers-2
exports: [ CarComponent, HeroesComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -0,0 +1,25 @@
/// Dummy modules to satisfy Angular Language Service
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AppModule } from './app.module';
////////
import { AppComponent as AppComponent1 } from './app.component.1';
@NgModule({
imports: [ CommonModule, AppModule ],
declarations: [ AppComponent1 ]
})
export class DummyModule1 {}
/////////
import { AppComponent as AppComponent2 } from './app.component.2';
@NgModule({
imports: [ CommonModule, AppModule ],
declarations: [ AppComponent2 ]
})
export class DummyModule2 {}

View File

@ -0,0 +1,35 @@
/// Dummy modules to satisfy Angular Language Service
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
////////
import { HeroListComponent as HeroListComponent1 } from './hero-list.component.1';
@NgModule({
imports: [ CommonModule ],
declarations: [ HeroListComponent1 ],
exports: [ HeroListComponent1 ]
})
export class DummyModule1 {}
/////////
import { HeroListComponent as HeroListComponent2 } from './hero-list.component.2';
@NgModule({
imports: [ CommonModule ],
declarations: [ HeroListComponent2 ]
})
export class DummyModule2 {}
/////////
import { HeroesComponent as HeroesComponent1 } from './heroes.component.1';
@NgModule({
imports: [ CommonModule, DummyModule1 ],
declarations: [ HeroesComponent1 ]
})
export class DummyModule3 {}

View File

@ -1,16 +1,17 @@
// #docregion
import { Component } from '@angular/core';
import { HEROES } from './mock-heroes';
@Component({
selector: 'app-hero-list',
template: `
<div *ngFor="let hero of heroes">
{{hero.id}} - {{hero.name}}
</div>
<div *ngFor="let hero of heroes">
{{hero.id}} - {{hero.name}}
</div>
`
})
// #docregion class
export class HeroListComponent {
heroes = HEROES;
}
// #enddocregion class

View File

@ -1,7 +1,6 @@
// #docplaster
// #docregion
import { Component } from '@angular/core';
import { Hero } from './hero';
// #enddocregion
import { HeroService } from './hero.service.1';
@ -15,9 +14,9 @@ import { HeroService } from './hero.service';
@Component({
selector: 'app-hero-list',
template: `
<div *ngFor="let hero of heroes">
{{hero.id}} - {{hero.name}}
</div>
<div *ngFor="let hero of heroes">
{{hero.id}} - {{hero.name}}
</div>
`
})
export class HeroListComponent {

View File

@ -1,17 +1,16 @@
/* tslint:disable:one-line */
// #docregion
import { Component } from '@angular/core';
import { Hero } from './hero';
import { HeroService } from './hero.service';
@Component({
selector: 'app-hero-list',
template: `
<div *ngFor="let hero of heroes">
{{hero.id}} - {{hero.name}}
({{hero.isSecret ? 'secret' : 'public'}})
</div>
<div *ngFor="let hero of heroes">
{{hero.id}} - {{hero.name}}
({{hero.isSecret ? 'secret' : 'public'}})
</div>
`,
})
export class HeroListComponent {

View File

@ -0,0 +1,6 @@
import { Injectable } from '@angular/core';
@Injectable()
export class HeroService {
constructor() { }
}

View File

@ -1,6 +1,5 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
@Injectable()

View File

@ -1,6 +1,5 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';

View File

@ -1,6 +1,5 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { Logger } from '../logger.service';

View File

@ -1,21 +1,18 @@
// #docplaster
// #docregion full, v1
import { Component } from '@angular/core';
// #docregion, v1
import { Component } from '@angular/core';
// #enddocregion v1
import { HeroService } from './hero.service';
import { HeroService } from './hero.service';
// #enddocregion full
// #docregion full, v1
// #docregion v1
@Component({
selector: 'app-heroes',
// #enddocregion v1
providers: [HeroService],
providers: [ HeroService ],
// #docregion v1
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesComponent { }

View File

@ -1,14 +1,13 @@
// #docregion
import { Component } from '@angular/core';
import { heroServiceProvider } from './hero.service.provider';
@Component({
selector: 'app-heroes',
providers: [ heroServiceProvider ],
template: `
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`,
providers: [heroServiceProvider]
<h2>Heroes</h2>
<app-hero-list></app-hero-list>
`
})
export class HeroesComponent { }

View File

@ -1,19 +1,21 @@
/* tslint:disable:one-line:check-open-brace*/
// Examples of provider arrays
// #docplaster
/*
* A collection of demo components showing different ways to provide services
* in @Component metadata
*/
import { Component, Inject, Injectable, OnInit } from '@angular/core';
import { APP_CONFIG, AppConfig,
HERO_DI_CONFIG } from './app.config';
import {
APP_CONFIG,
AppConfig,
HERO_DI_CONFIG } from './app.config';
import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
import { UserService } from './user.service';
import { HeroService } from './heroes/hero.service';
import { heroServiceProvider } from './heroes/hero.service.provider';
import { Logger } from './logger.service';
import { UserService } from './user.service';
let template = '{{log}}';
const template = '{{log}}';
//////////////////////////////////////////
@Component({
selector: 'provider-1',
template: template,
@ -30,6 +32,7 @@ export class Provider1Component {
}
//////////////////////////////////////////
@Component({
selector: 'provider-3',
template: template,
@ -47,7 +50,7 @@ export class Provider3Component {
}
//////////////////////////////////////////
class BetterLogger extends Logger {}
export class BetterLogger extends Logger {}
@Component({
selector: 'provider-4',
@ -66,9 +69,10 @@ export class Provider4Component {
}
//////////////////////////////////////////
// #docregion EvenBetterLogger
@Injectable()
class EvenBetterLogger extends Logger {
export class EvenBetterLogger extends Logger {
constructor(private userService: UserService) { super(); }
log(message: string) {
@ -96,8 +100,10 @@ export class Provider5Component {
}
//////////////////////////////////////////
class NewLogger extends Logger {}
class OldLogger {
export class NewLogger extends Logger {}
export class OldLogger {
logs: string[] = [];
log(message: string) {
throw new Error('Should not call the old logger!');
@ -149,11 +155,14 @@ export class Provider6bComponent {
}
//////////////////////////////////////////
// #docregion silent-logger
// An object in the shape of the logger service
let silentLogger = {
export function SilentLoggerFn() {}
const silentLogger = {
logs: ['Silent logger says "Shhhhh!". Provided via "useValue"'],
log: () => {}
log: SilentLoggerFn
};
// #enddocregion silent-logger
@ -172,6 +181,7 @@ export class Provider7Component {
this.log = logger.logs[0];
}
}
/////////////////
@Component({
@ -189,6 +199,7 @@ export class Provider8Component {
}
/////////////////
@Component({
selector: 'provider-9',
template: template,
@ -218,6 +229,7 @@ export class Provider9Component implements OnInit {
this.log = 'APP_CONFIG Application title is ' + this.config.title;
}
}
//////////////////////////////////////////
// Sample providers 1 to 7 illustrate a required logger dependency.
// Optional logger, can be null
@ -248,6 +260,7 @@ export class Provider10Component implements OnInit {
}
/////////////////
@Component({
selector: 'app-providers',
template: `

View File

@ -0,0 +1,33 @@
import { NgModule } from '@angular/core';
import {
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
ProvidersComponent,
} from './providers.component';
@NgModule({
declarations: [
Provider1Component,
Provider3Component,
Provider4Component,
Provider5Component,
Provider6aComponent,
Provider6bComponent,
Provider7Component,
Provider8Component,
Provider9Component,
Provider10Component,
ProvidersComponent,
],
exports: [ ProvidersComponent ]
})
export class ProvidersModule {}

View File

@ -2,10 +2,11 @@
// Simulate a simple test
// Reader should look to the testing chapter for the real thing
import { Component } from '@angular/core';
import { Component } from '@angular/core';
import { HeroService } from './heroes/hero.service';
import { HeroListComponent } from './heroes/hero-list.component';
import { Hero } from './heroes/hero';
import { HeroService } from './heroes/hero.service';
import { HeroListComponent } from './heroes/hero-list.component';
@Component({
selector: 'app-tests',
@ -22,12 +23,13 @@ export class TestComponent {
function runTests() {
// #docregion spec
let expectedHeroes = [{name: 'A'}, {name: 'B'}]
let mockService = <HeroService> {getHeroes: () => expectedHeroes }
const expectedHeroes = [{name: 'A'}, {name: 'B'}]
const mockService = <HeroService> {getHeroes: () => expectedHeroes }
it('should have heroes when HeroListComponent created', () => {
let hlc = new HeroListComponent(mockService);
expect(hlc.heroes.length).toEqual(expectedHeroes.length);
// Pass the mock to the constructor as the Angular injector would
const component = new HeroListComponent(mockService);
expect(component.heroes.length).toEqual(expectedHeroes.length);
});
// #enddocregion spec

View File

@ -0,0 +1 @@
<app-hero-form></app-hero-form>

View File

@ -3,6 +3,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: '<app-hero-form></app-hero-form>'
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { }

View File

@ -4,7 +4,7 @@ import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroFormComponent } from './hero-form.component';
import { HeroFormComponent } from './hero-form/hero-form.component';
@NgModule({
imports: [
@ -15,6 +15,7 @@ import { HeroFormComponent } from './hero-form.component';
AppComponent,
HeroFormComponent
],
providers: [],
bootstrap: [ AppComponent ]
})
export class AppModule { }

View File

@ -2,11 +2,12 @@
// #docregion , v1, final
import { Component } from '@angular/core';
import { Hero } from './hero';
import { Hero } from '../hero';
@Component({
selector: 'app-hero-form',
templateUrl: './hero-form.component.html'
templateUrl: './hero-form.component.html',
styleUrls: ['./hero-form.component.css']
})
export class HeroFormComponent {

View File

@ -0,0 +1 @@
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');

View File

@ -0,0 +1,3 @@
[1030/162525.401:ERROR:process_reader_win.cc(123)] NtOpenThread: {Acceso denegado} Un proceso ha solicitado acceso a un objeto, pero no se le han concedido esos derechos de acceso. (0xc0000022)
[1030/162525.402:ERROR:exception_snapshot_win.cc(87)] thread ID 26896 not found in process
[1030/162525.402:WARNING:crash_report_exception_handler.cc(62)] ProcessSnapshotWin::Initialize failed

View File

@ -0,0 +1,4 @@
<div class="container">
<h1>Reactive Forms</h1>
<app-hero-detail></app-hero-detail>
</div>

View File

@ -1,12 +0,0 @@
// #docregion
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="container">
<h1>Reactive Forms</h1>
<app-hero-detail></app-hero-detail>
</div>`
})
export class AppComponent { }

View File

@ -0,0 +1,4 @@
<div class="container">
<h1>Reactive Forms</h1>
<app-hero-list></app-hero-list>
</div>

View File

@ -3,10 +3,7 @@ import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<div class="container">
<h1>Reactive Forms</h1>
<app-hero-list></app-hero-list>
</div>`
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent { }

View File

@ -6,9 +6,9 @@ import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module
import { AppComponent } from './app.component';
import { HeroDetailComponent } from './hero-detail.component'; // <-- #1 import component
import { HeroDetailComponent } from './hero-detail/hero-detail.component'; // <-- #1 import component
// #enddocregion v1
import { HeroListComponent } from './hero-list.component';
import { HeroListComponent } from './hero-list/hero-list.component';
import { HeroService } from './hero.service'; // <-- #1 import service
// #docregion v1
@ -20,7 +20,7 @@ import { HeroService } from './hero.service'; // <-- #1 import service
],
declarations: [
AppComponent,
HeroDetailComponent, // <-- #3 declare app component
HeroDetailComponent,
// #enddocregion v1
HeroListComponent
// #docregion v1

View File

@ -4,14 +4,14 @@ import { ReactiveFormsModule } from '@angular/forms';
import { AppModule } from './app.module';
import { DemoComponent } from './demo.component';
import { HeroDetailComponent1 } from './hero-detail-1.component';
import { HeroDetailComponent2 } from './hero-detail-2.component';
import { HeroDetailComponent3 } from './hero-detail-3.component';
import { HeroDetailComponent4 } from './hero-detail-4.component';
import { HeroDetailComponent5 } from './hero-detail-5.component';
import { HeroDetailComponent6 } from './hero-detail-6.component';
import { HeroDetailComponent7 } from './hero-detail-7.component';
import { HeroDetailComponent8 } from './hero-detail-8.component';
import { HeroDetailComponent1 } from './hero-detail/hero-detail-1.component';
import { HeroDetailComponent2 } from './hero-detail/hero-detail-2.component';
import { HeroDetailComponent3 } from './hero-detail/hero-detail-3.component';
import { HeroDetailComponent4 } from './hero-detail/hero-detail-4.component';
import { HeroDetailComponent5 } from './hero-detail/hero-detail-5.component';
import { HeroDetailComponent6 } from './hero-detail/hero-detail-6.component';
import { HeroDetailComponent7 } from './hero-detail/hero-detail-7.component';
import { HeroDetailComponent8 } from './hero-detail/hero-detail-8.component';
@NgModule({
imports: [

View File

@ -1,8 +1,9 @@
/* tslint:disable:component-class-suffix */
// #docregion imports
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
// #enddocregion
import { Component } from '@angular/core';
// #docregion import
import { FormControl } from '@angular/forms';
// #enddocregion import
@Component({
selector: 'app-hero-detail-1',

View File

@ -3,7 +3,7 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { states } from './data-model';
import { states } from '../data-model';
// #enddocregion imports
@Component({

View File

@ -2,7 +2,7 @@
import { Component } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { states } from './data-model';
import { states } from '../data-model';
@Component({
selector: 'app-hero-detail-5',

View File

@ -5,7 +5,7 @@ import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
// #docregion import-hero
import { Hero, states } from './data-model';
import { Hero, states } from '../data-model';
// #enddocregion import-hero
////////// 6 ////////////////////

View File

@ -5,7 +5,7 @@ import { Component, Input, OnChanges } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
// #docregion import-address
import { Address, Hero, states } from './data-model';
import { Address, Hero, states } from '../data-model';
// #enddocregion import-address
// #enddocregion imports

View File

@ -3,7 +3,7 @@
import { Component, Input, OnChanges } from '@angular/core';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Address, Hero, states } from './data-model';
import { Address, Hero, states } from '../data-model';
// #enddocregion imports
@Component({

View File

@ -3,17 +3,16 @@
import { Component, Input, OnChanges } from '@angular/core';
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
import { Address, Hero, states } from './data-model';
import { Address, Hero, states } from '../data-model';
// #docregion import-service
import { HeroService } from './hero.service';
import { HeroService } from '../hero.service';
// #enddocregion import-service
// #docregion metadata
@Component({
selector: 'app-hero-detail',
templateUrl: './hero-detail.component.html'
templateUrl: './hero-detail.component.html',
styleUrls: ['./hero-detail.component.css']
})
// #enddocregion metadata
export class HeroDetailComponent implements OnChanges {
@Input() hero: Hero;

View File

@ -3,12 +3,13 @@ import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/finally';
import { Hero } from './data-model';
import { HeroService } from './hero.service';
import { Hero } from '../data-model';
import { HeroService } from '../hero.service';
@Component({
selector: 'app-hero-list',
templateUrl: './hero-list.component.html'
templateUrl: './hero-list.component.html',
styleUrls: ['./hero-list.component.css']
})
export class HeroListComponent implements OnInit {
heroes: Observable<Hero[]>;

View File

@ -5,10 +5,8 @@
<title>Hero Form</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="styles.css">
<!-- #docregion bootstrap -->
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<!-- #enddocregion bootstrap -->
<link rel="stylesheet" href="styles.css">
<script src="node_modules/core-js/client/shim.min.js"></script>

View File

@ -5,9 +5,7 @@
<title>Hero Form</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- #docregion bootstrap -->
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
<!-- #enddocregion bootstrap -->
</head>
<body>

View File

@ -0,0 +1 @@
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');

View File

@ -3,7 +3,7 @@
import { Injectable } from '@angular/core';
// #docregion import-observable
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
// #enddocregion import-observable

View File

@ -1,6 +1,6 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { Hero } from './hero';

View File

@ -1,7 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
// #docregion downgrade-injectable
declare var angular: angular.IAngularStatic;

View File

@ -3,7 +3,7 @@
import { ActivatedRoute } from '@angular/router';
// #enddocregion activatedroute
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { async, TestBed } from '@angular/core/testing';

View File

@ -2,7 +2,7 @@
// #docregion
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SpyLocation } from '@angular/common/testing';

View File

@ -1,7 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';

View File

@ -3,7 +3,7 @@
import { ActivatedRoute } from '@angular/router';
// #enddocregion activatedroute
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { async, TestBed } from '@angular/core/testing';

View File

@ -2,7 +2,7 @@
// #docregion routestuff
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Observable } from 'rxjs/Rx';
import { Observable } from 'rxjs/Observable';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { SpyLocation } from '@angular/common/testing';

View File

@ -212,8 +212,6 @@ The following are some of the key AngularJS built-in directives and their equiva
To launch the app in code, explicitly bootstrap the application's root module (`AppModule`)
in `main.ts`
and the application's root component (`AppComponent`) in `app.module.ts`.
For more information see the [Setup](guide/setup) page.
</td>
</tr>
@ -1254,13 +1252,12 @@ also encapsulate a style sheet within a specific component.
<td>
### Link tag
<code-example hideCopy path="ajs-quick-reference/src/index.html" region="style" linenums="false"></code-example>
### Styles configuration
<code-example hideCopy path="ajs-quick-reference/.angular-cli.1.json" region="styles" linenums="false">
In Angular, you can continue to use the link tag to define the styles for your application in the `index.html` file.
But now you can also encapsulate styles for your components.
With the Angular CLI, you can configure your global styles in the `.angular-cli.json` file.
You can rename the extension to `.scss` to use sass.
### StyleUrls
In Angular, you can use the `styles` or `styleUrls` property of the `@Component` metadata to define

View File

@ -127,7 +127,7 @@ You can think of `.metadata.json` as a diagram of the overall structure of a dec
<div class="l-sub-section">
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/tsc-wrapped/src/schema.ts)
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/compiler-cli/src/metadata/schema.ts)
describes the JSON format as a collection of TypeScript interfaces.
</div>

View File

@ -33,7 +33,7 @@ An attribute directive minimally requires building a controller class annotated
the attribute.
The controller class implements the desired directive behavior.
This page demonstrates building a simple _myHighlight_ attribute
This page demonstrates building a simple _appHighlight_ attribute
directive to set an element's background color
when the user hovers over that element. You can apply it like this:
@ -43,106 +43,84 @@ when the user hovers over that element. You can apply it like this:
### Write the directive code
Follow the [setup](guide/setup) instructions for creating a new local project
named <code>attribute-directives</code>.
Create the directive class file in a terminal window with this CLI command.
Create the following source file in the indicated folder:
<code-example language="sh" class="code-shell">
ng generate directive highlight
</code-example>
<code-example path="attribute-directives/src/app/highlight.directive.1.ts" title="src/app/highlight.directive.ts"></code-example>
The `import` statement specifies symbols from the Angular `core`:
1. `Directive` provides the functionality of the `@Directive` decorator.
1. `ElementRef` [injects](guide/dependency-injection) into the directive's constructor
so the code can access the DOM element.
1. `Input` allows data to flow from the binding expression into the directive.
Next, the `@Directive` decorator function contains the directive metadata in a configuration object
as an argument.
`@Directive` requires a CSS selector to identify
the HTML in the template that is associated with the directive.
The [CSS selector for an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
is the attribute name in square brackets.
Here, the directive's selector is `[myHighlight]`.
Angular locates all elements in the template that have an attribute named `myHighlight`.
The CLI creates `src/app/highlight.directive.ts`, a corresponding test file (`.../spec.ts`, and _declares_ the directive class in the root `AppModule`.
<div class="l-sub-section">
### Why not call it "highlight"?
_Directives_ must be declared in [Angular Modules](guide/ngmodule) in the same manner as _components_.
Though *highlight* is a more concise name than *myHighlight* and would work,
a best practice is to prefix selector names to ensure
</div >
The generated `src/app/highlight.directive.ts` is as follows:
<code-example path="attribute-directives/src/app/highlight.directive.0.ts" title="src/app/highlight.directive.ts"></code-example>
The imported `Directive` symbol provides the Angular the `@Directive` decorator.
The `@Directive` decorator's lone configuration property specifies the directive's
[CSS attribute selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors), `[appHighlight]`.
It's the brackets (`[]`) that make it an attribute selector.
Angular locates each element in the template that has an attribute named `appHighlight` and applies the logic of this directive to that element.
The _attribute selector_ pattern explains the name of this kind of directive.
<div class="l-sub-section">
#### Why not "highlight"?
Though *highlight* would be a more concise selector than *appHighlight* and it would work,
the best practice is to prefix selector names to ensure
they don't conflict with standard HTML attributes.
This also reduces the risk of colliding with third-party directive names.
The CLI added the `app` prefix for you.
Make sure you do **not** prefix the `highlight` directive name with **`ng`** because
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
For a simple demo, the short prefix, `my`, helps distinguish your custom directive.
</div>
After the `@Directive` metadata comes the directive's controller class,
called `HighlightDirective`, which contains the logic for the directive.
Exporting `HighlightDirective` makes it accessible to other components.
called `HighlightDirective`, which contains the (currently empty) logic for the directive.
Exporting `HighlightDirective` makes the directive accessible.
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 direct access to the DOM element
Now edit the generated `src/app/highlight.directive.ts` to look as follows:
<code-example path="attribute-directives/src/app/highlight.directive.1.ts" title="src/app/highlight.directive.ts"></code-example>
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
You use the `ElementRef`in the directive's constructor
to [inject](guide/dependency-injection) a reference to the host DOM element,
the element to which you applied `appHighlight`.
`ElementRef` grants direct access to the host DOM element
through its `nativeElement` property.
This first implementation sets the background color of the host element to yellow.
{@a apply-directive}
## Apply the attribute directive
To use the new `HighlightDirective`, create a template that
applies the directive as an attribute to a paragraph (`<p>`) element.
In Angular terms, the `<p>` element is the attribute **host**.
To use the new `HighlightDirective`, add a paragraph (`<p>`) element to the template of the root `AppComponent` and apply the directive as an attribute.
Put the template in its own <code>app.component.html</code>
file that looks like this:
<code-example path="attribute-directives/src/app/app.component.1.html" title="src/app/app.component.html" region="applied"></code-example>
<code-example path="attribute-directives/src/app/app.component.1.html" title="src/app/app.component.html"></code-example>
Now run the application to see the `HighlightDirective` in action.
Now reference this template in the `AppComponent`:
<code-example path="attribute-directives/src/app/app.component.ts" title="src/app/app.component.ts"></code-example>
Next, add an `import` statement to fetch the `Highlight` directive and
add that class to the `declarations` NgModule metadata. This way Angular
recognizes the directive when it encounters `myHighlight` in the template.
<code-example path="attribute-directives/src/app/app.module.ts" title="src/app/app.module.ts"></code-example>
Now when the app runs, the `myHighlight` directive highlights the paragraph text.
<figure>
<img src="generated/images/guide/attribute-directives/first-highlight.png" alt="First Highlight">
</figure>
<div class="l-sub-section">
<h3 class="no-toc">Your directive isn't working?</h3>
Did you remember to add the directive to 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 property of 'p'.
<code-example language="sh" class="code-shell">
ng serve
</code-example>
Angular detects that you're trying to bind to *something* but it can't find this directive
in the module's `declarations` array.
After specifying `HighlightDirective` in the `declarations` array,
Angular knows it can apply the directive to components declared in this module.
</div>
To summarize, Angular found the `myHighlight` attribute on the `<p>` element.
To summarize, Angular found the `appHighlight` attribute on the **host** `<p>` element.
It created an instance of the `HighlightDirective` class and
injected a reference to the `<p>` element into the directive's constructor
which sets the `<p>` element's background style to yellow.
@ -151,15 +129,14 @@ which sets the `<p>` element's background style to yellow.
## Respond to user-initiated events
Currently, `myHighlight` simply sets an element color.
Currently, `appHighlight` simply sets an element color.
The directive could be more dynamic.
It could detect when the user mouses into or out of the element
and respond by setting or clearing the highlight color.
Begin by adding `HostListener` to the list of imported symbols;
add the `Input` symbol as well because you'll need it soon.
Begin by adding `HostListener` to the list of imported symbols.
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Then add two eventhandlers that respond when the mouse enters or leaves,
each adorned by the `HostListener` decorator.
@ -180,8 +157,10 @@ There are at least three problems with _that_ approach:
</div>
The handlers delegate to a helper method that sets the color on the DOM element, `el`,
which you declare and initialize in the constructor.
The handlers delegate to a helper method that sets the color on the host DOM element, `el`.
The helper method, `highlight`, was extracted from the constructor.
The revised constructor simply declares the injected `el: ElementRef`.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
@ -203,7 +182,10 @@ the mouse hovers over the `p` and disappears as it moves out.
Currently the highlight color is hard-coded _within_ the directive. That's inflexible.
In this section, you give the developer the power to set the highlight color while applying the directive.
Start by adding a `highlightColor` property to the directive class like this:
Begin by adding `Input` to the list of symbols imported from `@angular/core`.
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
Add a `highlightColor` property to the directive class like this:
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
@ -232,16 +214,16 @@ That's good, but it would be nice to _simultaneously_ apply the directive and se
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"></code-example>
The `[myHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
The `[appHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
and sets the directive's highlight color with a property binding.
You're re-using the directive's attribute selector (`[myHighlight]`) to do both jobs.
You're re-using the directive's attribute selector (`[appHighlight]`) to do both jobs.
That's a crisp, compact syntax.
You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name.
You'll have to rename the directive's `highlightColor` property to `appHighlight` because that's now the color property binding name.
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn't convey the property's intent.
This is disagreeable. The word, `appHighlight`, is a terrible property name and it doesn't convey the property's intent.
{@a input-alias}
@ -254,14 +236,14 @@ Restore the original property name and specify the selector as the alias in the
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
_Inside_ the directive the property is known as `highlightColor`.
_Outside_ the directive, where you bind to it, it's known as `myHighlight`.
_Outside_ the directive, where you bind to it, it's known as `appHighlight`.
You get the best of both worlds: the property name you want and the binding syntax you want:
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" title="src/app/app.component.html (color)" region="color"></code-example>
Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method to use it.
If someone neglects to bind to `highlightColor`, highlight in red:
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
If someone neglects to bind to `appHighlightColor`, highlight the host element in red:
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" linenums="false" title="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
@ -308,7 +290,7 @@ then with the `defaultColor`, and falls back to "red" if both properties are und
<code-example path="attribute-directives/src/app/highlight.directive.ts" linenums="false" title="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
How do you bind to a second property when you're already binding to the `myHighlight` attribute name?
How do you bind to a second property when you're already binding to the `appHighlight` attribute name?
As with components, you can add as many directive property bindings as you need by stringing them along in the template.
The developer should be able to write the following template HTML to both bind to the `AppComponent.color`
@ -398,6 +380,6 @@ Now apply that reasoning to the following example:
The template and its component trust each other.
The `color` property doesn't require the `@Input` decorator.
* The `myHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`,
* The `appHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`,
not a property of the template's component. There are trust issues.
Therefore, the directive property must carry the `@Input` decorator.

View File

@ -356,6 +356,7 @@ Use `setTimeout()` to wait one tick and then revise the `seconds()` method so
that it takes future values from the timer component.
<h3 class="no-toc">Test it</h3>
Use [the same countdown timer tests](guide/component-interaction#countdown-tests) as before.
[Back to top](guide/component-interaction#top)

View File

@ -5,51 +5,6 @@ Dependency Injection is a powerful pattern for managing code dependencies.
This cookbook explores many of the features of Dependency Injection (DI) in Angular.
{@a toc}
<!--
# Contents
* [Application-wide dependencies](guide/dependency-injection-in-action#app-wide-dependencies)
* [External module configuration](guide/dependency-injection-in-action#external-module-configuration)
* [`@Injectable()` and nested service dependencies](guide/dependency-injection-in-action#nested-dependencies)
* [`@Injectable()`](guide/dependency-injection-in-action#injectable-1)
* [Limit service scope to a component subtree](guide/dependency-injection-in-action#service-scope)
* [Multiple service instances (sandboxing)](guide/dependency-injection-in-action#multiple-service-instances)
* [Qualify dependency lookup with `@Optional()` and `@Host()`](guide/dependency-injection-in-action#qualify-dependency-lookup)
* [Demonstration](guide/dependency-injection-in-action#demonstration)
* [Inject the component's DOM element](guide/dependency-injection-in-action#component-element)
* [Define dependencies with providers](guide/dependency-injection-in-action#providers)
* [Defining providers](guide/dependency-injection-in-action#defining-providers)
* [The *provide* object literal](guide/dependency-injection-in-action#provide)
* [`useValue`&mdash;the *value provider*](guide/dependency-injection-in-action#usevalue)
* [`useClass`&mdash;the *class provider*](guide/dependency-injection-in-action#useclass)
* [`useExisting`&mdash;the *alias provider*](guide/dependency-injection-in-action#useexisting)
* [`useFactory`&mdash;the *factory provider*](guide/dependency-injection-in-action#usefactory)
* [Provider token alternatives: the class-interface and `InjectionToken`](guide/dependency-injection-in-action#tokens)
* [class-interface](guide/dependency-injection-in-action#class-interface)
* [`InjectionToken`](guide/dependency-injection-in-action#injection-token)
* [Inject into a derived class](guide/dependency-injection-in-action#di-inheritance)
* [Find a parent component by injection](guide/dependency-injection-in-action#find-parent)
* [Find parent with a known component type](guide/dependency-injection-in-action#known-parent)
* [Cannot find a parent by its base class](guide/dependency-injection-in-action#base-parent)
* [Find a parent by its class-interface](guide/dependency-injection-in-action#class-interface-parent)
* [Find a parent in a tree of parents with `@SkipSelf()`](guide/dependency-injection-in-action#parent-tree)
* [The `Parent` class-interface](guide/dependency-injection-in-action#parent-token)
* [A `provideParent()` helper function](guide/dependency-injection-in-action#provideparent)
* [Break circularities with a forward class reference (*forwardRef*)](guide/dependency-injection-in-action#forwardref)
-->
See the <live-example name="dependency-injection-in-action"></live-example>
of the code in this cookbook.
@ -79,7 +34,7 @@ is all the registration you need.
A *provider* is something that can create or deliver a service.
Angular creates a service instance from a class provider by using `new`.
Read more about providers in the [Dependency Injection](guide/dependency-injection#injector-providers)
Read more about providers in the [Dependency Injection](guide/dependency-injection#register-providers-ngmodule)
guide.
</div>

View File

@ -0,0 +1,167 @@
# The Dependency Injection pattern
**Dependency injection** is an important application design pattern.
It's used so widely that almost everyone just calls it _DI_.
Angular has its own dependency injection framework, and
you really can't build an Angular application without it.
This page covers what DI is and why it's useful.
When you've learned the general pattern, you're ready to turn to
the [Angular Dependency Injection](guide/dependency-injection) guide to see how it works in an Angular app.
{@a why-di }
## Why dependency injection?
To understand why dependency injection is so important, consider an example without it.
Imagine writing the following code:
<code-example path="dependency-injection/src/app/car/car-no-di.ts" region="car" title="src/app/car/car.ts (without DI)">
</code-example>
The `Car` class creates everything it needs inside its constructor.
What's the problem?
The problem is that the `Car` class is brittle, inflexible, and hard to test.
This `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?
That would break the `Car` class and it would stay broken until you rewrote it along the lines of
`this.engine = new Engine(theNewParameter)`.
The `Engine` constructor parameters weren't even a consideration when you first wrote `Car`.
You may not anticipate them even now.
But you'll *have* to start caring because
when the definition of `Engine` changes, the `Car` class must change.
That makes `Car` brittle.
What if you want to put a different brand of tires on your `Car`? Too bad.
You're locked into whatever brand the `Tires` class creates. That makes the
`Car` class 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,
surely you can think of other dependencies that should be shared, such as the onboard
wireless connection to the manufacturer's service center. This `Car` lacks the flexibility
to share services that have been created previously for other consumers.
When you write tests for `Car` you're at the mercy of its hidden dependencies.
Is it even possible to create a new `Engine` in a test environment?
What does `Engine` depend upon? What does that dependency depend on?
Will a new instance of `Engine` make an asynchronous call to the server?
You certainly don't want that going on during tests.
What if the `Car` should flash a warning signal when tire pressure is low?
How do you confirm that it actually does flash a warning
if you can't swap in low-pressure tires during the test?
You have no control over the car's hidden dependencies.
When you can't control the dependencies, a class becomes difficult to test.
How can you make `Car` more robust, flexible, and testable?
{@a ctor-injection}
That's super easy. Change the `Car` constructor to a version with DI:
<code-tabs>
<code-pane title="src/app/car/car.ts (excerpt with DI)" path="dependency-injection/src/app/car/car.ts" region="car-ctor">
</code-pane>
<code-pane title="src/app/car/car.ts (excerpt without DI)" path="dependency-injection/src/app/car/car-no-di.ts" region="car-ctor">
</code-pane>
</code-tabs>
See what happened? The definition of the dependencies are
now in the constructor.
The `Car` class no longer creates an `engine` or `tires`.
It just consumes them.
<div class="l-sub-section">
This example leverages TypeScript's constructor syntax for declaring
parameters and properties simultaneously.
</div>
Now you can create a car by passing the engine and tires to the constructor.
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation" linenums="false">
</code-example>
How cool is that?
The definition of the `engine` and `tire` dependencies are
decoupled from the `Car` class.
You can pass in any kind of `engine` or `tires` you like, as long as they
conform to the general API requirements of an `engine` or `tires`.
Now, if someone extends the `Engine` class, that is not `Car`'s problem.
<div class="l-sub-section">
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
something like this:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-param" linenums="false">
</code-example>
The critical point is this: the `Car` class did not have to change.
You'll take care of the consumer's problem shortly.
</div>
The `Car` class is much easier to test now because you are in complete control
of its dependencies.
You can pass mocks to the constructor that do exactly what you want them to do
during each test:
<code-example path="dependency-injection/src/app/car/car-creations.ts" region="car-ctor-instantiation-with-mocks" linenums="false">
</code-example>
**You 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.
You need something that takes care of assembling these parts.
You _could_ write a giant class to do that:
<code-example path="dependency-injection/src/app/car/car-factory.ts" title="src/app/car/car-factory.ts">
</code-example>
It's not so bad now with only three creation methods.
But maintaining it will be hairy as the application grows.
This factory is going to become a huge spiderweb of
interdependent factory methods!
Wouldn't it be nice if you could simply list the things you 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_.
You register some classes with this injector, and it figures out how to create them.
When you need a `Car`, you simply ask the injector to get it for you and you're good to go.
<code-example path="dependency-injection/src/app/car/car-injector.ts" region="injector-call" title="src/app/car/car-injector.ts" linenums="false">
</code-example>
Everyone wins. The `Car` knows nothing about creating an `Engine` or `Tires`.
The consumer knows nothing about creating a `Car`.
You don't have a gigantic factory class to maintain.
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 you know what dependency injection is and appreciate its benefits,
turn to the [Angular Dependency Injection](guide/dependency-injection) guide to see how it is implemented in Angular.

File diff suppressed because it is too large Load Diff

View File

@ -31,9 +31,11 @@ The easiest way to display a component property
is to bind the property name through interpolation.
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
Follow the [setup](guide/setup) instructions for creating a new project
Follow the [quickstart](guide/quickstart) instructions for creating a new project
named <code>displaying-data</code>.
Delete the <code>app.component.html</code> file. It is not needed for this example.
Then modify the <code>app.component.ts</code> file by
changing the template and the body of the component.
@ -48,7 +50,7 @@ When you're done, it should look like this:
You added two properties to the formerly empty component: `title` and `myHero`.
The revised template displays the two component properties using double curly brace
The template displays the two component properties using double curly brace
interpolation:
@ -92,7 +94,7 @@ the view, such as a keystroke, a timer completion, or a response to an HTTP requ
Notice that you don't call **new** to create an instance of the `AppComponent` class.
Angular is creating an instance for you. How?
The CSS `selector` in the `@Component` decorator specifies an element named `<my-app>`.
The CSS `selector` in the `@Component` decorator specifies an element named `<app-root>`.
That element is a placeholder in the body of your `index.html` file:
@ -102,9 +104,9 @@ That element is a placeholder in the body of your `index.html` file:
When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angular looks for a `<my-app>`
When you bootstrap with the `AppComponent` class (in <code>main.ts</code>), Angular looks for a `<app-root>`
in the `index.html`, finds it, instantiates an instance of `AppComponent`, and renders it
inside the `<my-app>` tag.
inside the `<app-root>` tag.
Now run the app. It should display the title and hero name:
@ -131,13 +133,23 @@ is simpler without the additional HTML file.
In either style, the template data bindings have the same access to the component's properties.
<div class="alert is-helpful">
By default, the Angular CLI generates components with a template file. You can override that with:
<code-example hideCopy language="sh" class="code-shell">
ng generate component hero -it
</code-example>
</div>
## Constructor or variable initialization?
Although this example uses variable assignment to initialize the components, you can instead declare and initialize the properties using a constructor:
Although this example uses variable assignment to initialize the components, you could instead declare and initialize the properties using a constructor:
<code-example path="displaying-data/src/app/app-ctor.component.ts" linenums="false" title="src/app/app-ctor.component.ts (class)" region="class">
<code-example path="displaying-data/src/app/app-ctor.component.ts" linenums="false" region="class">
</code-example>
@ -231,12 +243,16 @@ At the moment, the binding is to an array of strings.
In real applications, most bindings are to more specialized objects.
To convert this binding to use specialized objects, turn the array
of hero names into an array of `Hero` objects. For that you'll need a `Hero` class.
of hero names into an array of `Hero` objects. For that you'll need a `Hero` class:
Create a new file in the `app` folder called `hero.ts` with the following code:
<code-example language="sh" class="code-shell">
ng generate class hero
</code-example>
With the following code:
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts (excerpt)">
<code-example path="displaying-data/src/app/hero.ts" linenums="false" title="src/app/hero.ts">
</code-example>

View File

@ -40,7 +40,7 @@ Note the following:
also carries a custom validator directive, `forbiddenName`. For more
information, see [Custom validators](guide/form-validation#custom-validators) section.
* `#name="ngModel"` exports `NgModel` into a local variable callled `name`. `NgModel` mirrors many of the properties of its underlying
* `#name="ngModel"` exports `NgModel` into a local variable called `name`. `NgModel` mirrors many of the properties of its underlying
`FormControl` instance, so you can use this in the template to check for control states such as `valid` and `dirty`. For a full list of control properties, see the [AbstractControl](api/forms/AbstractControl)
API reference.

View File

@ -29,19 +29,13 @@ You can run the <live-example></live-example> in Plunker and download the code f
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
the form-specific directives and techniques described in this page.
<div class="l-sub-section">
You can also use a reactive (or model-driven) approach to build forms.
However, this page focuses on template-driven forms.
You can also use a reactive (or model-driven) approach to build forms.
However, this page focuses on template-driven forms.
</div>
You can build almost any form with an Angular template&mdash;login forms, contact forms, and pretty much any business form.
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
@ -51,13 +45,10 @@ otherwise wrestle with yourself.
You'll learn to build a template-driven form that looks like this:
<figure>
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
</figure>
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
@ -65,27 +56,18 @@ Two of the three fields on this form are required. Required fields have a green
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
<figure>
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
</figure>
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
<div class="l-sub-section">
You can customize the colors and location of the "required" bar with standard CSS.
You can customize the colors and location of the "required" bar with standard CSS.
</div>
You'll build this form in small steps:
1. Create the `Hero` model class.
@ -98,11 +80,15 @@ You'll build this form in small steps:
1. Handle form submission with *ngSubmit*.
1. Disable the forms *Submit* button until the form is valid.
## Setup
Follow the [setup](guide/setup) instructions for creating a new project
named angular-forms.
Create a new project named <code>angular-forms</code>:
<code-example language="sh" class="code-shell">
ng new angular-forms
</code-example>
## Create the Hero model class
@ -113,15 +99,20 @@ A model can be as simple as a "property bag" that holds facts about a thing of a
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
and one optional field (`alterEgo`).
In the `app` directory, create the following file with the given content:
Using the Angular CLI, generate a new class named `Hero`:
<code-example language="sh" class="code-shell">
ng generate class Hero
</code-example>
With this content:
<code-example path="forms/src/app/hero.ts" title="src/app/hero.ts">
</code-example>
It's an anemic model with few requirements and no behavior. Perfect for the demo.
The TypeScript compiler generates a public field for each `public` constructor parameter and
@ -131,28 +122,29 @@ The `alterEgo` is optional, so the constructor lets you omit it; note the questi
You can create a new hero like this:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (SkyDog)" region="SkyDog">
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" region="SkyDog">
</code-example>
## Create a form component
An Angular form has two parts: an HTML-based _template_ and a component _class_
to handle data and user interactions programmatically.
Begin with the class because it states, in brief, what the hero editor can do.
Create the following file with the given content:
Using the Angular CLI, generate a new component named `HeroForm`:
<code-example language="sh" class="code-shell">
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (v1)" region="v1">
ng generate component HeroForm
</code-example>
With this content:
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" title="src/app/hero-form/hero-form.component.ts (v1)" region="v1">
</code-example>
Theres nothing special about this component, nothing form-specific,
nothing to distinguish it from any component you've written before.
@ -173,21 +165,6 @@ parent component. This is not a concern now and these future changes won't affec
* You added a `diagnostic` property to return a JSON representation of the model.
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
### Why the separate template file?
Why don't you write the template inline in the component file as you often do elsewhere?
There is no "right" answer for all occasions. Inline templates are useful when they are short.
Most form templates aren't short. TypeScript and JavaScript files generally aren't the best place to
write (or read) large stretches of HTML, and few editors help with files that have a mix of HTML and code.
Form templates tend to be large, even when displaying a small number of fields,
so it's usually best to put the HTML template in a separate file.
You'll write that template file in a moment. First,
revise the `app.module.ts` and `app.component.ts` to make use of the new `HeroFormComponent`.
## Revise *app.module.ts*
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
@ -196,89 +173,57 @@ and declare the components that belong to this module, such as the `HeroFormComp
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
`imports` for the application module before you can use forms.
Replace the contents of the "QuickStart" version with the following:
Update it with the following:
<code-example path="forms/src/app/app.module.ts" title="src/app/app.module.ts">
</code-example>
<div class="l-sub-section">
There are two changes:
1. You import `FormsModule`.
There are three changes:
1. You import `FormsModule` and the new `HeroFormComponent`.
1. You add the `FormsModule` to the list of `imports` defined in the `@NgModule` decorator. This gives the application
access to all of the template-driven forms features, including `ngModel`.
1. You add the `HeroFormComponent` to the list of `declarations` defined in the `@NgModule` decorator. This makes
the `HeroFormComponent` component visible throughout this module.
1. You add the `FormsModule` to the list of `imports` defined in the `@NgModule` decorator. This gives the application
access to all of the template-driven forms features, including `ngModel`.
</div>
<div class="alert is-important">
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
If you wrote it and it should belong to this module, _do_ declare it in the `declarations` array.
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
If you wrote it and it should belong to this module, _do_ declare it in the `declarations` array.
</div>
## Revise *app.component.ts*
## Revise *app.component.html*
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
Replace the contents of the "QuickStart" version with the following:
Replace the contents of its template with the following:
<code-example path="forms/src/app/app.component.ts" title="src/app/app.component.ts">
<code-example path="forms/src/app/app.component.html" title="src/app/app.component.html">
</code-example>
<div class="l-sub-section">
There are only two changes.
The `template` is simply the new element tag identified by the component's `selector` property.
This displays the hero form when the application component is loaded.
You've also dropped the `name` field from the class body.
There are only two changes.
The `template` is simply the new element tag identified by the component's `selector` property.
This displays the hero form when the application component is loaded.
Don't forget to remove the `name` field from the class body as well.
</div>
## Create an initial HTML form template
Create the template file with the following contents:
Update the template file with the following contents:
<code-example path="forms/src/app/hero-form.component.html" region="start" title="src/app/hero-form.component.html">
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" title="src/app/hero-form/hero-form.component.html">
</code-example>
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
opening them up for user input in input boxes.
@ -289,52 +234,34 @@ You added a *Submit* button at the bottom with some classes on it for styling.
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
<div class="l-sub-section">
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
</div>
The `container`, `form-group`, `form-control`, and `btn` classes
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
Bootstrap gives the form a little style.
<div class="callout is-important">
<header>
Angular forms don't require a style library
</header>
<header>
Angular forms don't require a style library
</header>
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
the styles of any external library. Angular apps can use any CSS library or none at all.
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
the styles of any external library. Angular apps can use any CSS library or none at all.
</div>
To add the stylesheet, open `styles.css` and add the following import line at the top:
To add the stylesheet, open `index.html` and add the following link to the `<head>`:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (bootstrap)" region="bootstrap">
<code-example path="forms/src/styles.1.css" linenums="false" title="src/styles.css">
</code-example>
## Add powers with _*ngFor_
The hero must choose one superpower from a fixed list of agency-approved powers.
@ -346,13 +273,10 @@ a technique seen previously in the [Displaying Data](guide/displaying-data) page
Add the following HTML *immediately below* the *Alter Ego* group:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (powers)" region="powers">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (powers)" region="powers">
</code-example>
This code repeats the `<option>` tag for each power in the list of powers.
The `pow` template input variable is a different power in each iteration;
you display its name using the interpolation syntax.
@ -363,13 +287,11 @@ you display its name using the interpolation syntax.
Running the app right now would be disappointing.
<figure>
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
</figure>
You don't see hero data because you're not binding to the `Hero` yet.
You know how to do that from earlier pages.
[Displaying Data](guide/displaying-data) teaches property binding.
@ -384,113 +306,83 @@ makes binding the form to the model easy.
Find the `<input>` tag for *Name* and update it like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-1">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1">
</code-example>
<div class="l-sub-section">
You added a diagnostic interpolation after the input tag
so you can see what you're doing.
You left yourself a note to throw it away when you're done.
You added a diagnostic interpolation after the input tag
so you can see what you're doing.
You left yourself a note to throw it away when you're done.
</div>
Focus on the binding syntax: `[(ngModel)]="..."`.
You need one more addition to display the data. Declare
a template variable for the form. Update the `<form>` tag with
`#heroForm="ngForm"` as follows:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="template-variable">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable">
</code-example>
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
<div class="l-sub-section">
{@a ngForm}
{@a ngForm}
### The _NgForm_ directive
### The _NgForm_ directive
What `NgForm` directive?
You didn't add an [NgForm](api/forms/NgForm) directive.
What `NgForm` directive?
You didn't add an [NgForm](api/forms/NgForm) directive.
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
The `NgForm` directive supplements the `form` element with additional features.
It holds the controls you created for the elements with an `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
control* is valid.
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
The `NgForm` directive supplements the `form` element with additional features.
It holds the controls you created for the elements with an `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
control* is valid.
</div>
If you ran the app now and started typing in the *Name* input box,
adding and deleting characters, you'd see them appear and disappear
from the interpolated text.
At some point it might look like this:
<figure>
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
</figure>
The diagnostic is evidence that values really are flowing from the input box to the model and
back again.
<div class="l-sub-section">
That's *two-way data binding*.
For more information, see
[Two-way binding with NgModel](guide/template-syntax#ngModel) on the
the [Template Syntax](guide/template-syntax) page.
That's *two-way data binding*.
For more information, see
[Two-way binding with NgModel](guide/template-syntax#ngModel) on the
the [Template Syntax](guide/template-syntax) page.
</div>
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
<div class="l-sub-section">
Internally, Angular creates `FormControl` instances and
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
Each `FormControl` is registered under the name you assigned to the `name` attribute.
Read more in the previous section, [The NgForm directive](guide/forms#ngForm).
Internally, Angular creates `FormControl` instances and
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
Each `FormControl` is registered under the name you assigned to the `name` attribute.
Read more in the previous section, [The NgForm directive](guide/forms#ngForm).
</div>
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
You'll ditch the input box binding message
and add a new binding (at the top) to the component's `diagnostic` property.
@ -498,42 +390,29 @@ Then you can confirm that two-way data binding works *for the entire hero model*
After revision, the core of the form should look like this:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModel-2">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2">
</code-example>
<div class="l-sub-section">
* Each input element has an `id` property that is used by the `label` element's `for` attribute
to match the label to its input control.
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
* Each input element has an `id` property that is used by the `label` element's `for` attribute
to match the label to its input control.
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
</div>
If you run the app now and change every hero model property, the form might display like this:
<figure>
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
</figure>
The diagnostic near the top of the form
confirms that all of your changes are reflected in the model.
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
## Track control state and validity with _ngModel_
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
@ -542,7 +421,6 @@ you if the user touched the control, if the value changed, or if the value becam
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
You can leverage those class names to change the appearance of the control.
<table>
<tr>
@ -611,18 +489,13 @@ You can leverage those class names to change the appearance of the control.
</table>
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="ngModelName-2">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2">
</code-example>
Now run the app and look at the _Name_ input box.
Follow these steps *precisely*:
@ -633,61 +506,44 @@ Follow these steps *precisely*:
The actions and effects are as follows:
<figure>
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
</figure>
You should see the following transitions and class names:
<figure>
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
</figure>
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
strong visual signal when the values are invalid. You also want to mark required fields.
To create such visual feedback, add definitions for the `ng-*` CSS classes.
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
## Add custom CSS for visual feedback
You can mark required fields and invalid data at the same time with a colored bar
on the left of the input box:
<figure>
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
</figure>
You achieve this effect by adding these class definitions to a new `forms.css` file
that you add to the project as a sibling to `index.html`:
<code-example path="forms/src/assets/forms.css" title="src/assets/forms.css">
</code-example>
Update the `<head>` of `index.html` to include this style sheet:
<code-example path="forms/src/index.html" linenums="false" title="src/index.html (styles)" region="styles">
</code-example>
## Show and hide validation error messages
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
@ -696,13 +552,10 @@ Leverage the control's state to reveal a helpful message.
When the user deletes the name, the form should look like this:
<figure>
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
</figure>
To achieve this effect, extend the `<input>` tag with the following:
* A [template reference variable](guide/template-syntax#ref-vars).
@ -710,41 +563,29 @@ To achieve this effect, extend the `<input>` tag with the following:
Here's an example of an error message added to the _name_ input box:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="name-with-error-msg">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg">
</code-example>
You need a template reference variable to access the input box's Angular control from within the template.
Here you created a variable called `name` and gave it the value "ngModel".
<div class="l-sub-section">
Why "ngModel"?
A directive's [exportAs](api/core/Directive) property
tells Angular how to link the reference variable to the directive.
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
Why "ngModel"?
A directive's [exportAs](api/core/Directive) property
tells Angular how to link the reference variable to the directive.
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
</div>
You control visibility of the name error message by binding properties of the `name`
control to the message `<div>` element's `hidden` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg">
</code-example>
In this example, you hide the message when the control is valid or pristine;
"pristine" means the user hasn't changed the value since it was displayed in this form.
@ -767,19 +608,14 @@ power to valid values.
Now you'll add a new hero in this form.
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-no-reset" title="src/app/hero-form.component.html (New Hero button)">
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" title="src/app/hero-form/hero-form.component.html (New Hero button)">
</code-example>
<code-example path="forms/src/app/hero-form.component.ts" region="new-hero" title="src/app/hero-form.component.ts (New Hero method)" linenums="false">
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" title="src/app/hero-form/hero-form.component.ts (New Hero method)" linenums="false">
</code-example>
Run the application again, click the *New Hero* button, and the form clears.
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
That's understandable as these are required fields.
@ -797,17 +633,12 @@ Replacing the hero object *did not restore the pristine state* of the form contr
You have to clear all of the flags imperatively, which you can do
by calling the form's `reset()` method after calling the `newHero()` method.
<code-example path="forms/src/app/hero-form.component.html" region="new-hero-button-form-reset" title="src/app/hero-form.component.html (Reset the form)">
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" title="src/app/hero-form/hero-form.component.html (Reset the form)">
</code-example>
Now clicking "New Hero" resets both the form and its control flags.
## Submit the form with _ngSubmit_
The user should be able to submit this form after filling it in.
@ -819,13 +650,10 @@ A "form submit" is useless at the moment.
To make it useful, bind the form's `ngSubmit` event property
to the hero form component's `onSubmit()` method:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (ngSubmit)" region="ngSubmit">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit">
</code-example>
You'd already defined a template reference variable,
`#heroForm`, and initialized it with the value "ngForm".
Now, use that variable to access the form with the Submit button.
@ -835,13 +663,10 @@ You'll bind the form's overall validity via
the `heroForm` variable to the button's `disabled` property
using an event binding. Here's the code:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (submit-button)" region="submit-button">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button">
</code-example>
If you run the application now, you find that the button is enabled&mdash;although
it doesn't do anything useful yet.
@ -857,65 +682,48 @@ For you, it was as simple as this:
1. Define a template reference variable on the (enhanced) form element.
2. Refer to that variable in a button many lines away.
## Toggle two form regions (extra credit)
Submitting the form isn't terribly dramatic at the moment.
<div class="l-sub-section">
An unsurprising observation for a demo. To be honest,
jazzing it up won't teach you anything new about forms.
But this is an opportunity to exercise some of your newly won
binding skills.
If you aren't interested, skip to this page's conclusion.
An unsurprising observation for a demo. To be honest,
jazzing it up won't teach you anything new about forms.
But this is an opportunity to exercise some of your newly won
binding skills.
If you aren't interested, skip to this page's conclusion.
</div>
For a more strikingly visual effect,
hide the data entry area and display something else.
Wrap the form in a `<div>` and bind
its `hidden` property to the `HeroFormComponent.submitted` property.
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="edit-div">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div">
</code-example>
The main form is visible from the start because the
`submitted` property is false until you submit the form,
as this fragment from the `HeroFormComponent` shows:
<code-example path="forms/src/app/hero-form.component.ts" linenums="false" title="src/app/hero-form.component.ts (submitted)" region="submitted">
<code-example path="forms/src/app/hero-form/hero-form.component.ts" linenums="false" title="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted">
</code-example>
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
as planned.
Now the app needs to show something else while the form is in the submitted state.
Add the following HTML below the `<div>` wrapper you just wrote:
<code-example path="forms/src/app/hero-form.component.html" linenums="false" title="src/app/hero-form.component.html (excerpt)" region="submitted">
<code-example path="forms/src/app/hero-form/hero-form.component.html" linenums="false" title="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted">
</code-example>
There's the hero again, displayed read-only with interpolation bindings.
This `<div>` appears only while the component is in the submitted state.
@ -924,9 +732,7 @@ that clears the `submitted` flag.
When you click the *Edit* button, this block disappears and the editable form reappears.
## Conclusion
## Summary
The Angular form discussed in this page takes advantage of the following
framework features to provide support for data modification, validation, and more:
@ -941,89 +747,15 @@ framework features to provide support for data modification, validation, and mor
* Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
* Custom CSS classes that provide visual feedback to users about invalid controls.
The final project folder structure should look like this:
<div class='filetree'>
<div class='file'>
angular-forms
</div>
<div class='children'>
<div class='file'>
src
</div>
<div class='children'>
<div class='file'>
app
</div>
<div class='children'>
<div class='file'>
app.component.ts
</div>
<div class='file'>
app.module.ts
</div>
<div class='file'>
hero.ts
</div>
<div class='file'>
hero-form.component.html
</div>
<div class='file'>
hero-form.component.ts
</div>
</div>
<div class='file'>
main.ts
</div>
<div class='file'>
tsconfig.json
</div>
<div class='file'>
index.html
</div>
</div>
<div class='file'>
node_modules ...
</div>
<div class='file'>
package.json
</div>
</div>
</div>
Heres the code for the final version of the application:
<code-tabs>
<code-pane title="hero-form.component.ts" path="forms/src/app/hero-form.component.ts" region="final">
<code-pane title="hero-form/hero-form.component.ts" path="forms/src/app/hero-form/hero-form.component.ts" region="final">
</code-pane>
<code-pane title="hero-form.component.html" path="forms/src/app/hero-form.component.html" region="final">
<code-pane title="hero-form/hero-form.component.html" path="forms/src/app/hero-form/hero-form.component.html" region="final">
</code-pane>
@ -1035,6 +767,10 @@ Heres the code for the final version of the application:
</code-pane>
<code-pane title="app.component.html" path="forms/src/app/app.component.html">
</code-pane>
<code-pane title="app.component.ts" path="forms/src/app/app.component.ts">
</code-pane>
@ -1043,10 +779,6 @@ Heres the code for the final version of the application:
</code-pane>
<code-pane title="index.html" path="forms/src/index.html">
</code-pane>
<code-pane title="forms.css" path="forms/src/assets/forms.css">
</code-pane>

View File

@ -132,7 +132,6 @@ You launch an Angular application by "bootstrapping" it using the application ro
Bootstrapping identifies an application's top level "root" [component](guide/glossary#component),
which is the first component that is loaded for the application.
For more information, see the [Setup](guide/setup) page.
You can bootstrap multiple apps in the same `index.html`, each app with its own top-level root.
@ -150,6 +149,12 @@ camelCase is also known as *lower camel case* to distinguish it from *upper came
In Angular documentation, "camelCase" always means *lower camel case*.
## CLI
The Angular CLI is a `command line interface` tool that can create a project, add files, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
Learn more in the [Getting Started](guide/quickstart) guide.
{@a component}
@ -496,7 +501,7 @@ is based on the [ES2015 module standard](http://www.2ality.com/2014/09/es6-modul
An application that adheres to this standard requires a module loader to
load modules on request and resolve inter-module dependencies.
Angular doesn't include a module loader and doesn't have a preference
for any particular third-party library (although most examples use SystemJS).
for any particular third-party library.
You can use any module library that conforms to the standard.
Modules are typically named after the file in which the exported thing is defined.

View File

@ -28,11 +28,11 @@ you already have projects running on your machine that use other versions of nod
Both `npm` and `yarn` install packages identified in a [**package.json**](https://docs.npmjs.com/files/package.json) file.
The CLI `ng new` command creates a default `packages.json` file for your project.
This `packages.json` specifies _a starter set of packages_ that work well together and
The CLI `ng new` command creates a default `package.json` file for your project.
This `package.json` specifies _a starter set of packages_ that work well together and
jointly support many common application scenarios.
You will add packages to `packages.json` as your application evolves.
You will add packages to `package.json` as your application evolves.
You may even remove some.
This guide focuses on the most important packages in the starter set.

View File

@ -128,11 +128,13 @@ Then you'll learn about the [Angular form classes](guide/reactive-forms#essentia
## Setup
Follow the steps in the [_Setup_ guide](guide/setup "Setup guide")
for creating a new project folder (perhaps called `reactive-forms`)
based on the _QuickStart seed_.
Create a new project named <code>angular-reactive-forms</code>:
<code-example language="sh" class="code-shell">
ng new angular-reactive-forms
</code-example>
{@a data-model}
@ -140,15 +142,21 @@ based on the _QuickStart seed_.
## Create a data model
The focus of this guide is a reactive forms component that edits a hero.
You'll need a `hero` class and some hero data.
Create a new `data-model.ts` file in the `app` directory and copy the content below into it.
Using the CLI, generate a new class named `data-model`:
<code-example language="sh" class="code-shell">
ng generate class data-model
</code-example>
And copy the content below:
<code-example path="reactive-forms/src/app/data-model.ts" title="src/app/data-model.ts" linenums="false">
</code-example>
The file exports two classes and two constants. The `Address`
and `Hero` classes define the application _data model_.
The `heroes` and `states` constants supply the test data.
@ -159,32 +167,26 @@ The `heroes` and `states` constants supply the test data.
## Create a _reactive forms_ component
Make a new file called
`hero-detail.component.ts` in the `app` directory and import these symbols:
Generate a new component named `HeroDetail`:
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="imports" title="src/app/hero-detail.component.ts" linenums="false">
<code-example language="sh" class="code-shell">
ng generate component HeroDetail
</code-example>
And import:
Now enter the `@Component` decorator that specifies the `HeroDetailComponent` metadata:
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="metadata" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-1.component.ts" region="import" title="src/app/hero-detail/hero-detail.component.ts" linenums="false">
</code-example>
Next, create an exported `HeroDetailComponent` class with a `FormControl`.
Next, update the `HeroDetailComponent` class with a `FormControl`.
`FormControl` is a directive that allows you to create and manage
a `FormControl` instance directly.
<code-example path="reactive-forms/src/app/hero-detail-1.component.ts" region="v1" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-1.component.ts" region="v1" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -218,10 +220,10 @@ read the [Form Validation](guide/form-validation) guide.
## Create the template
Now create the component's template, `src/app/hero-detail.component.html`, with the following markup.
Now update the component's template, with the following markup.
<code-example path="reactive-forms/src/app/hero-detail-1.component.html" region="simple-control" title="src/app/hero-detail.component.html" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-1.component.html" region="simple-control" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
@ -255,13 +257,11 @@ It _styles_ the form but in no way impacts the logic of the form.
The HeroDetailComponent template uses `formControlName`
directive from the `ReactiveFormsModule`.
In this sample, you declare the `HeroDetailComponent` in the `AppModule`.
Therefore, do the following three things in `app.module.ts`:
Do the following two things in `app.module.ts`:
1. Use a JavaScript `import` statement to access
the `ReactiveFormsModule` and the `HeroDetailComponent`.
the `ReactiveFormsModule`.
1. Add `ReactiveFormsModule` to the `AppModule`'s `imports` list.
1. Add `HeroDetailComponent` to the declarations array.
<code-example path="reactive-forms/src/app/app.module.ts" region="v1" title="src/app/app.module.ts (excerpt)" linenums="false">
@ -277,7 +277,7 @@ the `ReactiveFormsModule` and the `HeroDetailComponent`.
## Display the _HeroDetailComponent_
Revise the `AppComponent` template so it displays the `HeroDetailComponent`.
<code-example path="reactive-forms/src/app/app.component.1.ts" title="src/app/app.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/app.component.1.html" title="src/app/app.component.html" linenums="false">
</code-example>
@ -312,10 +312,10 @@ You'll learn more about these classes as you work through this guide.
### Style the app
You used bootstrap CSS classes in the template HTML of both the `AppComponent` and the `HeroDetailComponent`.
Add the `bootstrap` _CSS stylesheet_ to the head of `index.html`:
Add the `bootstrap` _CSS stylesheet_ to the head of `styles.css`:
<code-example path="reactive-forms/src/index.html" region="bootstrap" title="index.html" linenums="false">
<code-example path="reactive-forms/src/styles.1.css" title="styles.css" linenums="false">
</code-example>
@ -340,7 +340,7 @@ This is simple to do. To add a `FormGroup`, add it to the imports section
of `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="imports" title="src/app/hero-detail.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-2.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts" linenums="false">
</code-example>
@ -349,7 +349,7 @@ of `hero-detail.component.ts`:
In the class, wrap the `FormControl` in a `FormGroup` called `heroForm` as follows:
<code-example path="reactive-forms/src/app/hero-detail-2.component.ts" region="v2" title="src/app/hero-detail.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-2.component.ts" region="v2" title="src/app/hero-detail/hero-detail.component.ts" linenums="false">
</code-example>
@ -359,7 +359,7 @@ Now that you've made changes in the class, they need to be reflected in the
template. Update `hero-detail.component.html` by replacing it with the following.
<code-example path="reactive-forms/src/app/hero-detail-2.component.html" region="basic-form" title="src/app/hero-detail.component.html" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-2.component.html" region="basic-form" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
@ -418,7 +418,7 @@ To see the form model, add the following line after the
closing `form` tag in the `hero-detail.component.html`:
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" title="src/app/hero-detail.component.html" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3.component.html" region="form-value-json" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
@ -458,7 +458,7 @@ clutter by handling details of control creation for you.
To use `FormBuilder`, you need to import it into `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="imports" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3a.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -474,7 +474,7 @@ by following this plan:
The revised `HeroDetailComponent` looks like this:
<code-example path="reactive-forms/src/app/hero-detail-3a.component.ts" region="v3a" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3a.component.ts" region="v3a" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -497,7 +497,7 @@ demonstrates the simplicity of using `Validators.required` in reactive forms.
First, import the `Validators` symbol.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="imports" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -509,7 +509,7 @@ The first item is the initial value for `name`;
the second is the required validator, `Validators.required`.
<code-example path="reactive-forms/src/app/hero-detail-3.component.ts" region="required" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3.component.ts" region="required" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -529,7 +529,7 @@ Configuring validation is harder in template-driven forms where you must wrap va
Update the diagnostic message at the bottom of the template to display the form's validity status.
<code-example path="reactive-forms/src/app/hero-detail-3.component.html" region="form-value-json" title="src/app/hero-detail.component.html (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-3.component.html" region="form-value-json" title="src/app/hero-detail/hero-detail.component.html (excerpt)" linenums="false">
</code-example>
@ -564,7 +564,7 @@ A hero has an address, a super power and sometimes a sidekick too.
The address has a state property. The user will select a state with a `<select>` box and you'll populate
the `<option>` elements with states. So import `states` from `data-model.ts`.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="imports" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-4.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -573,7 +573,7 @@ the `<option>` elements with states. So import `states` from `data-model.ts`.
Declare the `states` property and add some address `FormControls` to the `heroForm` as follows.
<code-example path="reactive-forms/src/app/hero-detail-4.component.ts" region="v4" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-4.component.ts" region="v4" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -583,7 +583,7 @@ Then add corresponding markup in `hero-detail.component.html`
within the `form` element.
<code-example path="reactive-forms/src/app/hero-detail-4.component.html" title="src/app/hero-detail.component.html" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-4.component.html" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
@ -640,7 +640,7 @@ Let that be the parent `FormGroup`.
Use `FormBuilder` again to create a child `FormGroup` that encapsulates the address controls;
assign the result to a new `address` property of the parent `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-5.component.ts" region="v5" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-5.component.ts" region="v5" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -657,7 +657,7 @@ To make this change visually obvious, slip in an `<h4>` header near the top with
The new _address_ HTML looks like this:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="add-group" title="src/app/hero-detail.component.html (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-5.component.html" region="add-group" title="src/app/hero-detail/hero-detail.component.html (excerpt)" linenums="false">
</code-example>
@ -691,7 +691,7 @@ page by adding the following to the template,
immediately after the `{{form.value | json}}` interpolation as follows:
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-value" title="src/app/hero-detail.component.html" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-5.component.html" region="inspect-value" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
@ -700,7 +700,7 @@ immediately after the `{{form.value | json}}` interpolation as follows:
To get the state of a `FormControl` thats inside a `FormGroup`, use dot notation to path to the control.
<code-example path="reactive-forms/src/app/hero-detail-5.component.html" region="inspect-child-control" title="src/app/hero-detail.component.html" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-5.component.html" region="inspect-child-control" title="src/app/hero-detail/hero-detail.component.html" linenums="false">
</code-example>
@ -851,7 +851,7 @@ Recall the definition of `Hero` in `data-model.ts`:
Here, again, is the component's `FormGroup` definition.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero-form-model" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-6.component.ts" region="hero-form-model" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -870,7 +870,7 @@ to the _form model_ with the `patchValue` and `setValue` methods.
Take a moment to refactor the _address_ `FormGroup` definition for brevity and clarity as follows:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" title="src/app/hero-detail-7.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="address-form-group" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
</code-example>
@ -878,7 +878,7 @@ Take a moment to refactor the _address_ `FormGroup` definition for brevity and c
Also be sure to update the import from `data-model` so you can reference the `Hero` and `Address` classes:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="import-address" title="src/app/hero-detail-7.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="import-address" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
</code-example>
@ -898,7 +898,7 @@ With **`setValue`**, you assign _every_ form control value _at once_
by passing in a data object whose properties exactly match the _form model_ behind the `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="set-value" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -920,7 +920,7 @@ because its shape is similar to the component's `FormGroup` structure.
You can only show the hero's first address and you must account for the possibility that the `hero` has no addresses at all.
This explains the conditional setting of the `address` property in the data object argument:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="set-value-address" title="src/app/hero-detail-7.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="set-value-address" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
</code-example>
@ -932,7 +932,7 @@ by supplying an object of key/value pairs for just the controls of interest.
This example sets only the form's `name` control.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="patch-value" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-6.component.ts" region="patch-value" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -953,7 +953,7 @@ When the user clicks on a hero, the list component passes the selected hero into
by binding to its `hero` input property.
<code-example path="reactive-forms/src/app/hero-list.component.1.html" title="hero-list.component.html (simplified)" linenums="false">
<code-example path="reactive-forms/src/app/hero-list/hero-list.component.1.html" title="hero-list.component.html (simplified)" linenums="false">
</code-example>
@ -968,7 +968,7 @@ as the following steps demonstrate.
First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="import-input" title="src/app/hero-detail.component.ts (core imports)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-6.component.ts" region="import-input" title="src/app/hero-detail/hero-detail.component.ts (core imports)" linenums="false">
</code-example>
@ -976,7 +976,7 @@ First, import the `OnChanges` and `Input` symbols in `hero-detail.component.ts`.
Add the `hero` input property.
<code-example path="reactive-forms/src/app/hero-detail-6.component.ts" region="hero" title="src/app/hero-detail-6.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-6.component.ts" region="hero" title="src/app/hero-detail/hero-detail-6.component.ts" linenums="false">
</code-example>
@ -985,7 +985,7 @@ Add the `hero` input property.
Add the `ngOnChanges` method to the class as follows:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges-1" title="src/app/hero-detail.component.ts (ngOnchanges)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="ngOnChanges-1" title="src/app/hero-detail/hero-detail.component.ts (ngOnchanges)" linenums="false">
</code-example>
@ -998,7 +998,7 @@ control values from the previous hero are cleared and
status flags are restored to the _pristine_ state.
You could call `reset` at the top of `ngOnChanges` like this.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="reset" title="src/app/hero-detail-7.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="reset" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
</code-example>
@ -1008,7 +1008,7 @@ The `reset` method has an optional `state` value so you can reset the flags _and
Internally, `reset` passes the argument to `setValue`.
A little refactoring and `ngOnChanges` becomes this:
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="ngOnChanges" title="src/app/hero-detail.component.ts (ngOnchanges - revised)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="ngOnChanges" title="src/app/hero-detail/hero-detail.component.ts (ngOnchanges - revised)" linenums="false">
</code-example>
@ -1049,7 +1049,7 @@ The techniques involved are covered elsewhere in the documentation, including th
[here](tutorial/toh-pt3 "ToH: Multiple Components") and [here](tutorial/toh-pt4 "ToH: Services").
If you're coding along with the steps in this reactive forms tutorial,
create the pertinent files based on the
generate the pertinent files based on the
[source code displayed below](guide/reactive-forms#source-code "Reactive Forms source code").
Notice that `hero-list.component.ts` imports `Observable` and `finally` while `hero.service.ts` imports `Observable`, `of`,
and `delay` from `rxjs`.
@ -1073,7 +1073,7 @@ An Angular `FormArray` can display an array of _address_ `FormGroups`.
To get access to the `FormArray` class, import it into `hero-detail.component.ts`:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="imports" title="src/app/hero-detail.component.ts (excerpt)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.ts" region="imports" title="src/app/hero-detail/hero-detail.component.ts (excerpt)" linenums="false">
</code-example>
@ -1093,7 +1093,7 @@ let the user add or modify addresses (removing addresses is your homework).
Youll need to redefine the form model in the `HeroDetailComponent` constructor,
which currently only displays the first hero address in an _address_ `FormGroup`.
<code-example path="reactive-forms/src/app/hero-detail-7.component.ts" region="address-form-group" title="src/app/hero-detail-7.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-7.component.ts" region="address-form-group" title="src/app/hero-detail/hero-detail-7.component.ts" linenums="false">
</code-example>
@ -1105,7 +1105,7 @@ From the user's point of view, heroes don't have _addresses_.
_Addresses_ are for mere mortals. Heroes have _secret lairs_!
Replace the _address_ `FormGroup` definition with a _secretLairs_ `FormArray` definition:
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="secretLairs-form-array" title="src/app/hero-detail-8.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.ts" region="secretLairs-form-array" title="src/app/hero-detail/hero-detail-8.component.ts" linenums="false">
</code-example>
@ -1139,7 +1139,7 @@ the parent `HeroListComponent` sets the `HeroDetailComponent.hero` input propert
The following `setAddresses` method replaces the _secretLairs_ `FormArray` with a new `FormArray`,
initialized by an array of hero address `FormGroups`.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="set-addresses" title="src/app/hero-detail-8.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.ts" region="set-addresses" title="src/app/hero-detail/hero-detail-8.component.ts" linenums="false">
</code-example>
@ -1156,7 +1156,7 @@ The `HeroDetailComponent` should be able to display, add, and remove items from
Use the `FormGroup.get` method to acquire a reference to that `FormArray`.
Wrap the expression in a `secretLairs` convenience property for clarity and re-use.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="get-secret-lairs" title="src/app/hero-detail.component.ts (secretLayers property)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.ts" region="get-secret-lairs" title="src/app/hero-detail/hero-detail.component.ts (secretLayers property)" linenums="false">
</code-example>
@ -1184,7 +1184,7 @@ You'll re-use that index to compose a unique label for each address.
Here's the skeleton for the _secret lairs_ section of the HTML template:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array-skeleton" title="src/app/hero-detail.component.html (*ngFor)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.html" region="form-array-skeleton" title="src/app/hero-detail/hero-detail.component.html (*ngFor)" linenums="false">
</code-example>
@ -1192,7 +1192,7 @@ Here's the skeleton for the _secret lairs_ section of the HTML template:
Here's the complete template for the _secret lairs_ section:
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="form-array" title="src/app/hero-detail.component.html (excerpt)">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.html" region="form-array" title="src/app/hero-detail/hero-detail.component.html (excerpt)">
</code-example>
@ -1202,7 +1202,7 @@ Here's the complete template for the _secret lairs_ section:
Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a new _address_ `FormGroup` to it.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="add-lair" title="src/app/hero-detail.component.ts (addLair method)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.ts" region="add-lair" title="src/app/hero-detail/hero-detail.component.ts (addLair method)" linenums="false">
</code-example>
@ -1211,7 +1211,7 @@ Add an `addLair` method that gets the _secretLairs_ `FormArray` and appends a ne
Place a button on the form so the user can add a new _secret lair_ and wire it to the component's `addLair` method.
<code-example path="reactive-forms/src/app/hero-detail-8.component.html" region="add-lair" title="src/app/hero-detail.component.html (addLair button)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.html" region="add-lair" title="src/app/hero-detail/hero-detail.component.html (addLair button)" linenums="false">
</code-example>
@ -1272,7 +1272,7 @@ You don't need to know much about RxJS `Observable` to monitor form control valu
Add the following method to log changes to the value of the _name_ `FormControl`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="log-name-change" title="src/app/hero-detail.component.ts (logNameChange)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.ts" region="log-name-change" title="src/app/hero-detail/hero-detail.component.ts (logNameChange)" linenums="false">
</code-example>
@ -1280,7 +1280,7 @@ Add the following method to log changes to the value of the _name_ `FormControl`
Call it in the constructor, after creating the form.
<code-example path="reactive-forms/src/app/hero-detail-8.component.ts" region="ctor" title="src/app/hero-detail-8.component.ts" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail-8.component.ts" region="ctor" title="src/app/hero-detail/hero-detail-8.component.ts" linenums="false">
</code-example>
@ -1289,7 +1289,7 @@ Call it in the constructor, after creating the form.
The `logNameChange` method pushes name-change values into a `nameChangeLog` array.
Display that array at the bottom of the component template with this `*ngFor` binding:
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="name-change-log" title="src/app/hero-detail.component.html (Name change log)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.html" region="name-change-log" title="src/app/hero-detail/hero-detail.component.html (Name change log)" linenums="false">
</code-example>
@ -1328,7 +1328,7 @@ In this sample application, when the user submits the form,
the `HeroDetailComponent` will pass an instance of the hero _data model_
to a save method on the injected `HeroService`.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="on-submit" title="src/app/hero-detail.component.ts (onSubmit)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.ts" region="on-submit" title="src/app/hero-detail/hero-detail.component.ts (onSubmit)" linenums="false">
</code-example>
@ -1339,7 +1339,7 @@ So you create a new `hero` from a combination of original hero values (the `hero
and deep copies of the changed form model values, using the `prepareSaveHero` helper.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="prepare-save-hero" title="src/app/hero-detail.component.ts (prepareSaveHero)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.ts" region="prepare-save-hero" title="src/app/hero-detail/hero-detail.component.ts (prepareSaveHero)" linenums="false">
</code-example>
@ -1368,7 +1368,7 @@ The user cancels changes and reverts the form to the original state by pressing
Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _form model_ from the original, unchanged `hero` _data model_.
<code-example path="reactive-forms/src/app/hero-detail.component.ts" region="revert" title="src/app/hero-detail.component.ts (revert)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.ts" region="revert" title="src/app/hero-detail/hero-detail.component.ts (revert)" linenums="false">
</code-example>
@ -1377,7 +1377,7 @@ Reverting is easy. Simply re-execute the `ngOnChanges` method that built the _fo
### Buttons
Add the "Save" and "Revert" buttons near the top of the component's template:
<code-example path="reactive-forms/src/app/hero-detail.component.html" region="buttons" title="src/app/hero-detail.component.html (Save and Revert buttons)" linenums="false">
<code-example path="reactive-forms/src/app/hero-detail/hero-detail.component.html" region="buttons" title="src/app/hero-detail/hero-detail.component.html (Save and Revert buttons)" linenums="false">
</code-example>
@ -1395,9 +1395,7 @@ Try the <live-example plnkr="final" title="Reactive Forms (final) in Plunker"></
## Conclusion
This page covered:
## Summary
* How to create a reactive form component and its corresponding template.
* How to use `FormBuilder` to simplify coding a reactive form.
@ -1417,6 +1415,10 @@ The key files of the final version are as follows:
<code-tabs>
<code-pane title="src/app/app.component.html" path="reactive-forms/src/app/app.component.html">
</code-pane>
<code-pane title="src/app/app.component.ts" path="reactive-forms/src/app/app.component.ts">
</code-pane>
@ -1425,19 +1427,19 @@ The key files of the final version are as follows:
</code-pane>
<code-pane title="src/app/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail.component.ts">
<code-pane title="src/app/hero-detail/hero-detail.component.ts" path="reactive-forms/src/app/hero-detail/hero-detail.component.ts">
</code-pane>
<code-pane title="src/app/hero-detail.component.html" path="reactive-forms/src/app/hero-detail.component.html">
<code-pane title="src/app/hero-detail/hero-detail.component.html" path="reactive-forms/src/app/hero-detail/hero-detail.component.html">
</code-pane>
<code-pane title="src/app/hero-list.component.html" path="reactive-forms/src/app/hero-list.component.html">
<code-pane title="src/app/hero-list/hero-list.component.html" path="reactive-forms/src/app/hero-list/hero-list.component.html">
</code-pane>
<code-pane title="src/app/hero-list.component.ts" path="reactive-forms/src/app/hero-list.component.ts">
<code-pane title="src/app/hero-list/hero-list.component.ts" path="reactive-forms/src/app/hero-list/hero-list.component.ts">
</code-pane>

View File

@ -3492,7 +3492,7 @@ It will be there when the `CrisisDetailComponent` ask for it.
**Two critical points**
**Three critical points**
1. The router's `Resolve` interface is optional.
The `CrisisDetailResolver` doesn't inherit from a base class.

View File

@ -25,7 +25,7 @@ They shape or reshape the DOM's _structure_, typically by adding, removing, or m
elements.
As with other directives, you apply a structural directive to a _host element_.
The directive then does whatever it's supposed to do with that host element and its descendents.
The directive then does whatever it's supposed to do with that host element and its descendants.
Structural directives are easy to recognize.
An asterisk (*) precedes the directive attribute name as in this example.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 5.1 KiB

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