Compare commits

...

107 Commits
9.0.7 ... 6.1.3

Author SHA1 Message Date
ea83445149 release: cut the v6.1.3 release 2018-08-15 14:28:58 -07:00
1319ff4376 fix(service-worker): Cache-Control: no-cache on assets breaks service worker (#25408)
At the moment `cacheAge` can we undefined when having `Cache-Control` set to `no-cache` due the mapping method in `needToRevalidate`

Closes #25442

PR Close #25408
2018-08-14 16:40:15 -07:00
9c1311c801 docs(core): Correct spelling error in directives docs (#25377)
Link to life-cycle hooks was spelt as "life-cycle hoooks".
PR Close #25377
2018-08-14 16:39:33 -07:00
2ce93482b9 docs: enable debug tools with current versions of Angular (#25361)
Updating code snippet in docs that shows how to enable debug tools.
PR Close #25361
2018-08-14 16:38:26 -07:00
ed2a47f822 build(bazel): update to rules_typescript 0.16.0 & update to tagged rules_webtesting 0.2.1 (#25486)
PR Close #25486
2018-08-14 16:37:48 -07:00
cdee9add01 build(docs-infra): remove stability labels from API docs (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
2f85b1691a build(docs-infra): clean up API package template (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
bf441e8b9e build(docs-infra): include packages in API template breadcrumbs (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
1c86e9b3b2 build(docs-infra): change breadcrumb delimiter to > (#25453)
PR Close #25453
2018-08-14 13:17:15 -07:00
9d6e869899 docs: add api doc for programmatic animation classes (#24668)
PR Close #24668
2018-08-14 13:15:27 -07:00
e906bf4f31 docs: add Accelebrate to resources (#23204)
PR Close #23204
2018-08-14 11:58:04 -07:00
5f08bdf8b9 ci: github robot should enforce that all requested reviews are submitted (#25336)
See docs in the diff for justification.
PR Close #25336
2018-08-13 21:39:05 -07:00
f1ed022a4d docs: fix typo in Architecture overview page (#25438)
PR Close #25438
2018-08-13 21:38:22 -07:00
151e4b9fcc docs: add link to Yarn in README (#24856)
Remove the code markdown. It is not code, it is a name.

PR Close #24856
2018-08-13 21:36:13 -07:00
d0f089a55d docs(aio): add async validation chapter (#25189)
Closes #22881

PR Close #25189
2018-08-10 09:14:25 -07:00
cb05f9bbe9 docs: fix typo in testing guide (closes #25400) (#25418)
PR Close #25418
2018-08-10 09:11:35 -07:00
fda30cb3e3 build: stop printing source-map-support warning (#25339)
PR Close #25339
2018-08-08 19:02:58 -07:00
2951e721df build(bazel): update to rules_nodejs 0.11.2 and latest rules_typescript (#25365) 2018-08-08 13:19:37 -07:00
3449f1e256 docs: copy edit architecture guide (#25328)
PR Close #25328
2018-08-08 13:12:54 -07:00
6480d1b288 docs: make css multiline in styleguide for consistency (#25300)
PR Close #25300
2018-08-08 13:12:35 -07:00
e76211aa32 docs: add ngrx book to the docs (#23389)
PR Close #23389
2018-08-08 13:11:46 -07:00
a16de8f842 style: fix whitespace and indentation in the testing guide (#21669)
PR Close #21669
2018-08-08 13:11:17 -07:00
24f1dd3b81 docs: add docs for fakeAsync test with custom macroTask in aio (#21669)
PR Close #21669
2018-08-08 13:11:17 -07:00
f39551ce7e docs: Clarify breaking change in minor release (#25393)
The breaking change was in an experimental feature. Update to clarify the wording.

PR Close #25393
2018-08-08 13:06:59 -07:00
3beb7116af release: cut the v6.1.2 release 2018-08-08 11:00:23 -07:00
4b1a825efc Revert "build: update Bazel to 0.16 (#25316)" (#25391)
This reverts commit 4eb8ac6de9 because 0.16 is not
widely available yet (e.g. on Mac) and it is blocking the Angular release.

PR Close #25391
2018-08-08 10:52:25 -07:00
01e62551f5 build(bazel): update to rules_nodejs 0.11.2 and latest rules_typescript (#25365)
PR Close #25365
2018-08-07 21:01:46 -07:00
2f23533a25 docs(aio): Angular course in Portuguese #21836 2018-08-07 12:08:47 -07:00
054fbbe8b8 fix: add mappings for ngfactory & ngsummary files to their module names in aot summary resolver (#25335)
PR Close #25335
2018-08-07 11:13:29 -07:00
155d938e04 docs: refining code of tutorial 7 routing (#22151)
Removed the dead code from hero-detail.component.ts

Fixes #21908

PR Close #22151
2018-08-07 11:08:54 -07:00
94a2ac7884 docs: update resources to include UI-jar (#21200)
PR Close #21200
2018-08-07 11:07:40 -07:00
b75a98522a test(upgrade): reduce flaky-ness by increasing timeout (#24937)
PR Close #24937
2018-08-06 14:52:52 -07:00
d7dc1b5e44 refactor(upgrade): improve internal AngularJS typings (#24937)
PR Close #24937
2018-08-06 14:52:52 -07:00
e075ea7ae7 build(upgrade): use correct sources in BUILD.bazel (#24937)
PR Close #24937
2018-08-06 14:52:52 -07:00
415519acd3 docs: update to 2nd edition of Learning Angular (#20934)
PR Close #20934
2018-08-06 13:44:43 -07:00
8cbb836985 docs: clarify heroes example (#21216)
PR Close #21216
2018-08-06 13:44:18 -07:00
8d0f8bd657 docs: fix table in comparing observables guide (#22485)
PR Close #22485
2018-08-06 13:41:16 -07:00
66547d8fd0 docs(core): clarify supported ViewChild selectors (#22784)
PR Close #22784
2018-08-06 13:40:48 -07:00
6e7d5f0925 docs(core): fix tree-shakable spelling (#24057)
PR Close #24057
2018-08-06 13:40:17 -07:00
29dfa5570a docs: standardize spelling of tree-shakable (#24057)
PR Close #24057
2018-08-06 13:40:17 -07:00
0c028a03ec docs: remove code in universal hero detail component (#25215)
This reverts commit e9cc3dad8f39bc8dfabfb708a825f90fcd2ab697.

PR Close #25215
2018-08-06 13:39:24 -07:00
a54c049051 docs: replace npm with yarn in lockfile readme (#25309)
PR Close #25309
2018-08-06 13:38:15 -07:00
40904ce0c4 docs: add missing word in outputs description. (#25330)
PR Close #25330
2018-08-06 13:36:51 -07:00
88f01f5653 docs: fix typo (#25331)
PR Close #25331
2018-08-06 13:36:10 -07:00
c66794c265 docs: several fixes for NPM package guide (#20186)
PR Close #20186
2018-08-06 11:32:32 -07:00
e4acd83541 docs(http): fixed example unit test for error catching (#25306)
The example unit test should test the service when the backend
application is not available, by providing a mock error response.
Although, the test will
fail as the mock response from the server is valid (it does not simulate
a
error response, but valid response with an error status 404).
This merge request fix this issue by replacing MockResponse with
MockError

This PR resolves 19499 issue

PR Close #25306
2018-08-06 11:31:59 -07:00
a57f8a1301 build: update Bazel to 0.16 (#25316)
PR Close #25316
2018-08-06 11:30:25 -07:00
ae9b4e6fa7 fix(router): take base uri into account in setUpLocationSync() (#20244)
Normalize the full URL (including the base uri) before passing it to
`router.navigateByUrl()`.

Fixes #20061

PR Close #20244
2018-08-06 11:11:08 -07:00
478eca31c7 docs(aio): add Made with Angular (#21297)
PR Close #21297
2018-08-06 09:50:16 -07:00
2e1603938c build: skip ivy builds when not publishing (#25299)
PR Close #25299
2018-08-04 14:17:01 -07:00
0c9c2accc2 refactor(bazel): dont rely on language target to downlevel for loop (#24534)
PR Close #24534
2018-08-03 15:55:19 -07:00
0fb41e5ced test(docs-infra): log docs examples e2e spec paths to aid debugging (#25293)
It seems that occasionally the sharding of docs examples e2e tests gets
messed up resulting in some tests not being run. This can cause CI to be
green on a PR, when they shouldn't (because the failing tests didn't run
at all).

It is unclear under what circumstances this happens, so printing the
paths of found e2e specs will help debug the issue when it comes up
again.

PR Close #25293
2018-08-03 15:30:32 -07:00
3f43dbb642 style(common): fix short param names (#23667)
PR Close #23667
2018-08-03 14:09:29 -07:00
5069c06906 docs(common): fix content errors (#23667)
PR Close #23667
2018-08-03 14:09:29 -07:00
58698d7806 release: cut the v6.1.1 release 2018-08-02 14:02:59 -07:00
e26c25a062 Revert "docs: refactor http module import for style guide app.module (#25001)" (#25263)
This reverts commit 88da8f3d52.

PR Close #25263
2018-08-02 09:20:12 -07:00
0a6434b066 test(common): TokenExtractor should extend HttpXsrfTokenExtractor in xsrf spec (#24649)
PR Close #24649
2018-08-02 08:34:15 -07:00
ff3550c304 test(common): remove unused import in xsrf spec (#24649)
PR Close #24649
2018-08-02 08:34:15 -07:00
6d4a14082c docs(docs-infra): adds note according to Symlink problem (#24714)
docs: adds note according to Symlink problem

Closes #24709
docs(docs-infra): adds section "Developing on Windows"


Merge remote-tracking branch 'origin/aioREADME' into aioREADME


docs(docs-infra): adds information about admin rights


docs(docs-infra): adds hint


docs(docs-infra): Change to link


PR Close #24714
2018-08-02 08:33:24 -07:00
9ddf269c2c docs(elements): add section about custom element typings in elements guide (#25219)
PR Close #25219
2018-08-02 08:32:59 -07:00
25a76a1492 docs(elements): add link to full example in elements guide (#25219)
PR Close #25219
2018-08-02 08:32:59 -07:00
8439a6ec2a docs(elements): remove unnecessary whitespace in elements guide (#25219)
PR Close #25219
2018-08-02 08:32:59 -07:00
1ef2eae3aa feat(docs-infra): support sending Google Analytics events (#25042)
PR Close #25042
2018-08-01 17:04:19 -07:00
d5d034a0ff docs: update reactiveconf 2018 in events (#24739)
PR Close #24739
2018-08-01 16:15:18 -07:00
5ca35b3cd2 docs: Update the link to the Jasmine docs (#25175)
Solves #24462.

Also update the http part of the link to to https.

PR Close #25175
2018-08-01 16:12:44 -07:00
0a6a3f3163 ci: update pullapprove groups and add docs (#25257)
With this update to permissions the docs team can easily identify the technical reviewer for a particular doc, which should streamline the reviews.

I also added Jennifer into all groups that contain docs, so that she can approve changes that contain only editorial changes.

Closes #21692

PR Close #25257
2018-08-01 15:53:41 -07:00
3a601382e6 test(upgrade): run tests against AngularJS v1.7.x as well (#25231)
PR Close #25231
2018-08-01 14:10:21 -07:00
7a1fdde69e build(bazel): entry point file couldn't be resolved [ts-api-guardian] (#25052)
* When using `ts-api-guardian` on Windows, the input file can't be found due to wrong normalized path delimiters.

PR Close #25052
2018-08-01 13:29:27 -07:00
cbc2ea1b1a build: update hello_world__closure to google-closure-compiler 20180716.0.0 (#25236)
PR Close #25236
2018-08-01 13:25:40 -07:00
bdf801b0e8 build: revert yarn.lock rxjs version to 6.0.0 (#25236)
PR Close #25236
2018-08-01 13:23:35 -07:00
fe5e8b7177 docs(aio): update Kendo UI description in resource.json (#24845)
PR Close #24845
2018-08-01 10:59:16 -07:00
11f0f98ad8 docs: fix typos and missing word in tutorial (#20764)
PR Close #20764
2018-08-01 10:56:31 -07:00
801b534421 docs(core): remove experimental tag (#24032)
PR Close #24032
2018-08-01 10:56:07 -07:00
0fc83215e2 docs(core): remove experimental tag (#24032)
Remove experimental note on APP_INITIALIZER.

PR Close #24032
2018-08-01 10:56:07 -07:00
3d3a1a4642 docs(aio): add Kevin Yang to GDE resources (#24791)
Add files via upload
PR Close #24791
2018-08-01 10:55:41 -07:00
32a40ba5de docs: refactor http module import for style guide app.module (#25001)
PR Close #25001
2018-08-01 10:55:17 -07:00
045271230d docs: refactor lazy loading modules example (#25071)
PR Close #25071
2018-08-01 10:54:00 -07:00
ec31f6bf9a docs(router): clarify scroll position wording (#25077)
PR Close #25077
2018-08-01 10:53:35 -07:00
4798d77088 docs(core): replace ReflectiveInjector example with Static Injector example (#25162)
PR Close #25162
2018-08-01 10:52:32 -07:00
08c6762039 docs: replace ReflectiveInjector samples with Injector samples (#25162)
PR Close #25162
2018-08-01 10:52:32 -07:00
26516045e7 refactor(animations): do not use short parameter names (#25198)
PR Close #25198
2018-08-01 10:51:58 -07:00
a83b9f7911 docs(changelog): remove reverted feature entry (#25206)
PR Close #25206
2018-08-01 10:51:28 -07:00
1b7c77e49f docs(changelog): remove duplicate entries (#25206)
PR Close #25206
2018-08-01 10:51:28 -07:00
3ab31a4be6 docs: update to account for CLI changes (#25223)
This should help clarify the use of providedIn and correct the documentation where it was showing the use of a now depreciated CLI command flag.

I am openly looking for feedback on this change to figure out the best wording.

PR Close #25223
2018-08-01 10:51:05 -07:00
43dcf77123 build(bazel): fix typo in protractor test target definition (#25235)
PR Close #25235
2018-08-01 10:50:44 -07:00
d4bf2da3bd refactor(core): remove withBody from public testing API (#25171)
PR Close #25171
2018-07-31 15:09:33 -07:00
fa3882845a docs(aio): add short description for entryComponents (#21360)
PR Close #21360
2018-07-31 13:18:36 -07:00
fa59748e00 build: make postinstall script compatible with Windows (#25232)
PR Close #25232
2018-07-31 13:17:55 -07:00
c38ecb3b5b docs(forms): fix incorrect variables naming in the comments (#25150)
PR Close #25150
2018-07-31 11:42:16 -07:00
875efa8492 docs(docs-infra): fix topnav layout for smaller screens (#25181)
PR Close #25181
2018-07-31 11:41:22 -07:00
74964bde99 docs: fix link to "Override component providers" (#24967)
Closes #24966

PR Close #24967
2018-07-30 21:53:21 -07:00
785fb5cc5a docs(aio): add StrongBrew to the trainer list (#24891)
PR Close #24891
2018-07-30 16:48:17 -07:00
26d9f0278b ci: correctly encode quoted params passed as params to curl
Previously the auth token could have been split into three separate args in bash which resulted
in two bogus requests being sent out for each curl call. These requests had to time out before
the real request was made, but without the token.

I couldn't find a better way to quickly fix this without adding some duplication.
2018-07-30 16:46:50 -07:00
22ebd53c17 docs: update bootstrapping and entry component guide to use httpclient (#25178)
PR Close #25178
2018-07-30 16:00:19 -07:00
a972c039c3 docs: fix typo in dependency injection guide (#24972)
PR Close #24972
2018-07-30 15:56:36 -07:00
f5e18029fa docs: refactor pipe example to use the HttpClient (#22741)
PR Close #22741
2018-07-30 14:40:25 -07:00
317c7087c5 build(compiler-cli): update tsickle dependency to support TypeScript 2.9 (#25152)
The original range (`^0.30.0`) does not match `0.32.1`, which enables support for TypeScript 2.9.

Close #25141

PR Close #25152
2018-07-27 11:25:28 -07:00
39abe7b7c1 test: fix typings for DoneFn (#25163)
This also fixes CI tests, which were accidentally broken in #24663.

PR Close #25163
2018-07-27 11:13:33 -07:00
36a7705a44 feat(docs-infra): allow notification bar to show arbitrary content (#25020)
This change generalises the notification bar rendering to allow
more complex content to be displayed.

Now you must provide the full HTML of the notification message
when using `<aio-notification>`.

Also you can control whether clicking the content triggers the
notification to close or not.

This will support the new notification specified in "Other Items : 3" of
[#24140](https://github.com/angular/angular/issues/24140#issuecomment-397480410)

PR Close #25020
2018-07-27 09:29:40 -07:00
50a21885cf style(docs-infra): remove unnecessary call to console.log() (#25020)
PR Close #25020
2018-07-27 09:29:40 -07:00
e86f3d9a49 docs: refactor feature modules example (#25069)
PR Close #25069
2018-07-27 09:28:13 -07:00
738f2961ba docs: Change unnecessary step in ToH-Tutorial (#25059)
PR Close #25059
2018-07-27 09:25:59 -07:00
f2bf8287ba build(bazel): add comment about angular bazel rules API re-export from /index.bzl (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
9d5b34e1e7 build(bazel): add comment for patch-types work-around (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
d237f4014a build(bazel): show bazel progress in CircleCI to prevent 10m timeout with no output (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
8743a9bfd6 build(bazel): use bazel managed node_modules for downstream angular from source build support (#24663)
PR Close #24663
2018-07-26 17:02:21 -07:00
514d03f2d0 docs(router): Removed unneeded trailing text. (#24894)
PR Close #24894
2018-07-26 17:01:03 -07:00
178 changed files with 4839 additions and 2235 deletions

View File

@ -3,7 +3,10 @@
# See remote cache documentation in /docs/BAZEL.md
# Don't be spammy in the logs
build --noshow_progress
# TODO(gmagolan): Hide progress again once build performance improves
# Presently, CircleCI can timeout during bazel test ... with the following
# error: Too long with no output (exceeded 10m0s)
# build --noshow_progress
# Don't run manual tests
test --test_tag_filters=-manual

View File

@ -62,6 +62,13 @@ merge:
# list of checks that will determine if the merge label can be added
checks:
# require that the PR has reviews from all requested reviewers
#
# This enables us to request reviews from both eng and tech writers, or multiple eng folks, and prevents accidental merges.
# Rather than merging PRs with pending reviews, if all PullApprove requirements are satisfied and additional reviews are not needed pending reviewers should be removed via GitHub UI (this also leaves an audit trail behind these decisions).
requireReviews: true,
# whether the PR shouldn't have a conflict with the base branch
noConflict: true
# list of labels that a PR needs to have, checked with a regexp (e.g. "PR target:" will work for the label "PR target: master")

View File

@ -8,6 +8,7 @@
# alexeagle - Alex Eagle
# alxhub - Alex Rickabaugh
# andrewseguin - Andrew Seguin
# benlesh - Ben Lesh
# brandonroberts - Brandon Roberts
# brocco - Mike Brocchi
# filipesilva - Filipe Silva
@ -15,7 +16,7 @@
# hansl - Hans Larsen
# IgorMinar - Igor Minar
# jasonaden - Jason Aden
# kapunahelewong - Kapunahele Wong
# jenniferfell - Jennifer Fell
# kara - Kara Erickson
# kyliau - Keen Yee Liau
# matsko - Matias Niemelä
@ -91,6 +92,7 @@ groups:
- "*.bzl"
- "packages/bazel/*"
- "tools/bazel.rc"
- "/docs/BAZEL.md"
users:
- alexeagle #primary
- kyliau
@ -130,42 +132,113 @@ groups:
conditions:
files:
- "packages/core/*"
- "aio/content/guide/bootstrapping.md"
- "aio/content/examples/bootstrapping/*"
- "aio/content/guide/attribute-directives.md"
- "aio/content/examples/attribute-directives/*"
- "aio/content/images/guide/attribute-directives/*"
- "aio/content/guide/structural-directives.md"
- "aio/content/examples/structural-directives/*"
- "aio/content/images/guide/structural-directives/*"
- "aio/content/guide/dynamic-component-loader.md"
- "aio/content/examples/dynamic-component-loader/*"
- "aio/content/images/guide/dynamic-component-loader/*"
- "aio/content/guide/template-syntax.md"
- "aio/content/examples/template-syntax/*"
- "aio/content/images/guide/template-syntax/*"
- "aio/content/guide/dependency-injection.md"
- "aio/content/examples/dependency-injection/*"
- "aio/content/images/guide/dependency-injection/*"
- "aio/content/guide/dependency-injection-in-action.md"
- "aio/content/examples/dependency-injection-in-action/*"
- "aio/content/images/guide/dependency-injection-in-action/*"
- "aio/content/guide/hierarchical-dependency-injection.md"
- "aio/content/examples/hierarchical-dependency-injection/*"
- "aio/content/guide/singleton-services.md"
- "aio/content/guide/dependency-injection-pattern.md"
- "aio/content/guide/providers.md"
- "aio/content/examples/providers/*"
- "aio/content/guide/component-interaction.md"
- "aio/content/examples/component-interaction/*"
- "aio/content/images/guide/component-interaction/*"
- "aio/content/guide/component-styles.md"
- "aio/content/examples/component-styles/*"
- "aio/content/guide/lifecycle-hooks.md"
- "aio/content/examples/lifecycle-hooks/*"
- "aio/content/images/guide/lifecycle-hooks/*"
- "aio/content/examples/ngcontainer/*"
- "aio/content/images/guide/ngcontainer/*"
- "aio/content/guide/pipes.md"
- "aio/content/examples/pipes/*"
- "aio/content/images/guide/pipes/*"
- "aio/content/guide/entry-components.md"
- "aio/content/guide/set-document-title.md"
- "aio/content/examples/set-document-title/*"
- "aio/content/images/guide/set-document-title/*"
- "aio/content/guide/ngmodules.md"
- "aio/content/examples/ngmodules/*"
- "aio/content/examples/ngmodule/*"
- "aio/content/images/guide/ngmodule/*"
- "aio/content/guide/ngmodule-faq.md"
- "aio/content/examples/ngmodule-faq/*"
- "aio/content/guide/module-types.md"
- "aio/content/guide/sharing-ngmodules.md"
- "aio/content/guide/frequent-ngmodules.md"
- "aio/content/images/guide/frequent-ngmodules/*"
- "aio/content/guide/ngmodule-api.md"
- "aio/content/guide/ngmodule-vs-jsmodule.md"
- "aio/content/guide/feature-modules.md"
- "aio/content/examples/feature-modules/*"
- "aio/content/images/guide/feature-modules/*"
- "aio/content/guide/lazy-loading-ngmodules.md"
- "aio/content/examples/lazy-loading-ngmodules/*"
- "aio/content/images/guide/lazy-loading-ngmodules"
users:
- mhevery #primary
- jasonaden
- kara
- vicb
- IgorMinar #fallback
- IgorMinar
- jenniferfell #docs only
animations:
conditions:
files:
- "packages/animations/*"
- "packages/platform-browser/animations/*"
- "aio/content/guide/animations.md"
- "aio/content/examples/animations/*"
- "aio/content/images/guide/animations/*"
users:
- matsko #primary
- mhevery #fallback
- IgorMinar #fallback
- jenniferfell #docs only
compiler/i18n:
conditions:
files:
- "packages/compiler/src/i18n/*"
- "aio/content/guide/i18n.md"
- "aio/content/examples/i18n/*"
users:
- vicb #primary
- alxhub
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
compiler:
conditions:
files:
- "packages/compiler/*"
- "aio/content/guide/aot-compiler.md"
users:
- alxhub #primary
- vicb
- mhevery
- IgorMinar #fallback
- jenniferfell #docs only
compiler-cli/ngtools:
conditions:
@ -174,7 +247,6 @@ groups:
users:
- hansl
- filipesilva #fallback
- brocco #fallback
- IgorMinar #fallback
compiler-cli:
@ -210,56 +282,97 @@ groups:
files:
- "packages/forms/*"
- "aio/content/guide/forms.md"
- "aio/content/guide/form-validation.md"
- "aio/content/guide/reactive-forms.md"
- "aio/content/examples/forms/*"
- "aio/content/images/guide/forms/*"
- "aio/content/guide/form-validation.md"
- "aio/content/examples/form-validation/*"
- "aio/content/images/guide/form-validation/*"
- "aio/content/guide/dynamic-form.md"
- "aio/content/examples/dynamic-form/*"
- "aio/content/images/guide/dynamic-form/*"
- "aio/content/guide/reactive-forms.md"
- "aio/content/examples/reactive-forms/*"
- "aio/content/images/guide/reactive-forms/*"
users:
- kara #primary
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
http:
conditions:
files:
- "packages/common/http/*"
- "packages/http/*"
- "aio/content/guide/http.md"
- "aio/content/examples/http/*"
- "aio/content/images/guide/http/*"
users:
- alxhub #primary
- IgorMinar
- mhevery #fallback
- jenniferfell #docs only
language-service:
conditions:
files:
- "packages/language-service/*"
- "aio/content/guide/language-service.md"
- "aio/content/images/guide/language-service/*"
users:
- kyliau #primary
# needs secondary
- vicb
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
router:
conditions:
files:
- "packages/router/*"
- "aio/content/guide/router.md"
- "aio/content/examples/router/*"
- "aio/content/images/guide/router/*"
users:
- jasonaden #primary
- vicb
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
testing:
conditions:
files:
- "*/testing/*"
- "aio/content/guide/testing.md"
- "aio/content/examples/testing/*"
- "aio/content/images/guide/testing/*"
users:
- vikerman
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
upgrade:
conditions:
files:
- "packages/upgrade/*"
- "aio/content/guide/upgrade.md"
- "aio/content/examples/upgrade-module/*"
- "aio/content/images/guide/upgrade/*"
- "aio/content/examples/upgrade-phonecat-1-typescript/*"
- "aio/content/examples/upgrade-phonecat-2-hybrid/*"
- "aio/content/examples/upgrade-phonecat-3-final/*"
- "aio/content/guide/upgrade-performance.md"
- "aio/content/guide/ajs-quick-reference.md"
- "aio/content/examples/ajs-quick-reference/*"
users:
- petebacondarwin #primary
- gkalpak
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
platform-browser:
conditions:
@ -275,12 +388,15 @@ groups:
conditions:
files:
- "packages/platform-server/*"
- "aio/content/guide/universal.md"
- "aio/content/examples/universal/*"
users:
- vikerman #primary
- alxhub #secondary
- vicb
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
platform-webworker:
conditions:
@ -296,22 +412,34 @@ groups:
conditions:
files:
- "packages/service-worker/*"
- "aio/content/guide/service-worker-getting-started.md"
- "aio/content/examples/service-worker-getting-started/*"
- "aio/content/guide/service-worker-communications.md"
- "aio/content/guide/service-worker-config.md"
- "aio/content/guide/service-worker-devops.md"
- "aio/content/guide/service-worker-intro.md"
- "aio/content/images/guide/service-worker/*"
users:
- alxhub #primary
- gkalpak
- IgorMinar #fallback
- gkalpak #primary
- alxhub
- IgorMinar
- mhevery #fallback
- jenniferfell #docs only
elements:
conditions:
files:
- "packages/elements/*"
- "aio/content/examples/elements/*"
- "aio/content/images/guide/elements/*"
- "aio/content/guide/elements.md"
users:
- andrewseguin #primary
- gkalpak
- robwormald
- IgorMinar #fallback
- mhevery #fallback
- jenniferfell #docs only
benchpress:
conditions:
@ -323,7 +451,7 @@ groups:
- IgorMinar #fallback
- mhevery #fallback
angular.io:
docs-infra:
conditions:
files:
include:
@ -336,7 +464,7 @@ groups:
- gkalpak
- mhevery #fallback
angular.io-guide-and-tutorial:
docs/guide-and-tutorial:
conditions:
files:
include:
@ -346,19 +474,20 @@ groups:
- "aio/content/navigation.json"
- "aio/content/license.md"
users:
- kapunahelewong
- stephenfluin
- jenniferfell
- brandonroberts
- petebacondarwin
- gkalpak
- IgorMinar
- brandonroberts
- mhevery #fallback
angular.io-marketing:
docs/marketing:
conditions:
files:
include:
- "aio/content/marketing/*"
- "aio/content/images/marketing/*"
- "aio/content/navigation.json"
- "aio/content/license.md"
users:
@ -368,3 +497,43 @@ groups:
- IgorMinar
- robwormald
- mhevery #fallback
docs/observables:
conditions:
files:
- "aio/content/examples/observables/*"
- "aio/content/images/guide/observables/*"
- "aio/content/guide/observables.md"
- "aio/content/guide/comparing-observables.md"
- "aio/content/examples/observables-in-angular/*"
- "aio/content/images/guide/observables-in-angular/*"
- "aio/content/guide/observables-in-angular.md"
- "aio/content/examples/practical-observable-usage/*"
- "aio/content/guide/practical-observable-usage.md"
- "aio/content/examples/rx-library/*"
- "aio/content/guide/rx-library.md"
users:
- jasonaden
- benlesh
- IgorMinar
- mhevery
- jenniferfell #docs only
docs/packaging:
conditions:
files:
- "aio/content/guide/npm-packages.md"
- "aio/content/guide/browser-support.md"
- "aio/content/guide/typescript-configuration.md"
- "aio/content/guide/setup-systemjs-anatomy.md"
- "aio/content/examples/setup/*"
- "aio/content/guide/setup.md"
- "aio/content/guide/deployment.md"
- "aio/content/guide/releases.md"
- "aio/content/guide/updating.md"
users:
- IgorMinar #primary
- alexeagle
- hansl
- mhevery #fallback
- jenniferfell #docs only

View File

@ -15,220 +15,19 @@ alias(
actual = "@nodejs//:yarn",
)
node_modules_filegroup(
alias(
name = "node_modules",
packages = [
"adm-zip",
"ajv",
"angular",
"angular-1.5",
"angular-mocks",
"angular-mocks-1.5",
"anymatch",
"arr-diff",
"arr-flatten",
"arr-union",
"array-unique",
"asn1",
"assert-plus",
"assign-symbols",
"async-each",
"asynckit",
"atob",
"aws-sign2",
"aws4",
"balanced-match",
"base",
"base64-js",
"binary-extensions",
"blocking-proxy",
"brace-expansion",
"braces",
"bytebuffer",
"cache-base",
"caseless",
"chokidar",
"class-utils",
"co",
"collection-visit",
"combined-stream",
"component-emitter",
"concat-map",
"copy-descriptor",
"core-util-is",
"debug",
"decode-uri-component",
"define-property",
"delayed-stream",
"domino",
"expand-brackets",
"expand-range",
"extend",
"extend-shallow",
"extglob",
"extsprintf",
"fast-deep-equal",
"fast-json-stable-stringify",
"filename-regex",
"fill-range",
"for-in",
"for-own",
"forever-agent",
"form-data",
"fragment-cache",
"fs.realpath",
"get-value",
"glob",
"glob-base",
"glob-parent",
"graceful-fs",
"hammerjs",
"har-schema",
"har-validator",
"has-value",
"has-values",
"http-signature",
"https-proxy-agent",
"inflight",
"inherits",
"is-accessor-descriptor",
"is-binary-path",
"is-buffer",
"is-data-descriptor",
"is-descriptor",
"is-dotfile",
"is-equal-shallow",
"is-extendable",
"is-extglob",
"is-glob",
"is-number",
"is-plain-object",
"is-posix-bracket",
"is-primitive",
"is-typedarray",
"is-windows",
"isarray",
"isobject",
"isstream",
"jasmine",
"jasmine-core",
"jasminewd2",
"json-schema",
"json-schema-traverse",
"json-stable-stringify",
"json-stringify-safe",
"jsprim",
"kind-of",
"long",
"lru-cache",
"map-cache",
"map-visit",
"math-random",
"micromatch",
"mime-db",
"mime-types",
"minimatch",
"minimist",
"mixin-deep",
"nanomatch",
"normalize-path",
"oauth-sign",
"object.omit",
"object.pick",
"object-copy",
"object-visit",
"once",
"optimist",
"options",
"os-tmpdir",
"parse-glob",
"pascalcase",
"path-dirname",
"path-is-absolute",
"performance-now",
"posix-character-classes",
"preserve",
"process-nextick-args",
"protobufjs",
"protractor",
"qs",
"randomatic",
"readable-stream",
"readdirp",
"reflect-metadata",
"regex-cache",
"regex-not",
"remove-trailing-separator",
"repeat-element",
"repeat-string",
"request",
"ret",
"rimraf",
"safe-buffer",
"safe-regex",
"sax",
"semver",
"set-immediate-shim",
"set-value",
"shelljs",
"sigmund",
"snapdragon",
"snapdragon-node",
"snapdragon-util",
"source-map",
"source-map-resolve",
"source-map-support",
"source-map-url",
"split-string",
"sshpk",
"static-extend",
"stringstream",
"tmp",
"to-object-path",
"to-regex",
"to-regex-range",
"tough-cookie",
"tsickle",
"tslib",
"tsutils",
"tunnel-agent",
"typescript",
"union-value",
"unset-value",
"upath",
"uri-js",
"urix",
"use",
"util-deprecate",
"uuid",
"verror",
"webdriver-js-extender",
"webdriver-manager",
"wordwrap",
"wrappy",
"xhr2",
"xml2js",
"xmlbuilder",
"zone.js",
"@angular-devkit/core",
"@angular-devkit/schematics",
"@types",
"@webcomponents/custom-elements",
],
patterns = [
"node_modules/protractor/**",
"node_modules/@schematics/angular/**",
],
actual = "@angular_deps//:node_modules",
)
filegroup(
name = "web_test_bootstrap_scripts",
# do not sort
srcs = [
"//:node_modules/reflect-metadata/Reflect.js",
"//:node_modules/zone.js/dist/zone.js",
"//:node_modules/zone.js/dist/zone-testing.js",
"//:node_modules/zone.js/dist/task-tracking.js",
"@angular_deps//:node_modules/reflect-metadata/Reflect.js",
"@angular_deps//:node_modules/zone.js/dist/zone.js",
"@angular_deps//:node_modules/zone.js/dist/zone-testing.js",
"@angular_deps//:node_modules/zone.js/dist/task-tracking.js",
"//:test-events.js",
],
)
@ -236,9 +35,11 @@ filegroup(
filegroup(
name = "angularjs_scripts",
srcs = [
"//:node_modules/angular-1.5/angular.js",
"//:node_modules/angular-mocks-1.5/angular-mocks.js",
"//:node_modules/angular-mocks/angular-mocks.js",
"//:node_modules/angular/angular.js",
"@angular_deps//:node_modules/angular-1.5/angular.js",
"@angular_deps//:node_modules/angular-1.6/angular.js",
"@angular_deps//:node_modules/angular-mocks-1.5/angular-mocks.js",
"@angular_deps//:node_modules/angular-mocks-1.6/angular-mocks.js",
"@angular_deps//:node_modules/angular-mocks/angular-mocks.js",
"@angular_deps//:node_modules/angular/angular.js",
],
)

View File

@ -1,3 +1,34 @@
<a name="6.1.3"></a>
## [6.1.3](https://github.com/angular/angular/compare/6.1.2...6.1.3) (2018-08-15)
### Bug Fixes
* **service-worker:** `Cache-Control: no-cache` on assets breaks service worker ([#25408](https://github.com/angular/angular/issues/25408)) ([1319ff4](https://github.com/angular/angular/commit/1319ff4)), closes [#25442](https://github.com/angular/angular/issues/25442)
<a name="6.1.2"></a>
## [6.1.2](https://github.com/angular/angular/compare/6.1.1...6.1.2) (2018-08-08)
### Bug Fixes
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ae9b4e6](https://github.com/angular/angular/commit/ae9b4e6)), closes [#20061](https://github.com/angular/angular/issues/20061)
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([054fbbe](https://github.com/angular/angular/commit/054fbbe))
<a name="6.1.1"></a>
## [6.1.1](https://github.com/angular/angular/compare/6.1.0...6.1.1) (2018-08-02)
### Bug Fixes
* **compiler-cli:** correct tsickle dependency version to fix typescript 2.9 compatibility ([fec29fa](https://github.com/angular/angular/commit/317c7087c56b72aa74cd6d6a8f719e6e7fec29fa))
<a name="6.1.0"></a>
# [6.1.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0) (2018-07-25)
@ -16,26 +47,19 @@
* **common:** format fractional seconds ([#24844](https://github.com/angular/angular/issues/24844)) ([0b4d85e](https://github.com/angular/angular/commit/0b4d85e)), closes [#24831](https://github.com/angular/angular/issues/24831)
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([ff84c5c](https://github.com/angular/angular/commit/ff84c5c)), closes [#24155](https://github.com/angular/angular/issues/24155)
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([dcabb05](https://github.com/angular/angular/commit/dcabb05)), closes [#24609](https://github.com/angular/angular/issues/24609)
* **common:** do not round factional seconds ([#24831](https://github.com/angular/angular/issues/24831)) ([a527c69](https://github.com/angular/angular/commit/a527c69)), closes [#24384](https://github.com/angular/angular/issues/24384)
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([ff84c5c](https://github.com/angular/angular/commit/ff84c5c)), closes [#24155](https://github.com/angular/angular/issues/24155)
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([dcabb05](https://github.com/angular/angular/commit/dcabb05)), closes [#24609](https://github.com/angular/angular/issues/24609)
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
* **compiler:** fix a few non-tree-shakeable code patterns ([#24677](https://github.com/angular/angular/issues/24677)) ([50d4a4f](https://github.com/angular/angular/commit/50d4a4f))
* **compiler:** i18n_extractor now outputs the correct source file name ([#24885](https://github.com/angular/angular/issues/24885)) ([c8ad965](https://github.com/angular/angular/commit/c8ad965)), closes [#24884](https://github.com/angular/angular/issues/24884)
* **compiler:** fix a few non-tree-shakeable code patterns ([#24677](https://github.com/angular/angular/issues/24677)) ([50d4a4f](https://github.com/angular/angular/commit/50d4a4f))
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([d8f7b29](https://github.com/angular/angular/commit/d8f7b29)), closes [#20363](https://github.com/angular/angular/issues/20363)
* **compiler:** avoid a crash in ngc-wrapped. ([#23468](https://github.com/angular/angular/issues/23468)) ([e1c4930](https://github.com/angular/angular/commit/e1c4930))
* **compiler:** generate constant array for i18n attributes ([#23837](https://github.com/angular/angular/issues/23837)) ([cfde36d](https://github.com/angular/angular/commit/cfde36d))
* **compiler:** generate core-compliant hostBindings property ([#24087](https://github.com/angular/angular/issues/24087)) ([01b5acd](https://github.com/angular/angular/commit/01b5acd)), closes [#24013](https://github.com/angular/angular/issues/24013)
* **compiler:** handle undefined annotation metadata ([#23349](https://github.com/angular/angular/issues/23349)) ([ca776c5](https://github.com/angular/angular/commit/ca776c5))
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([0d5f2d3](https://github.com/angular/angular/commit/0d5f2d3))
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([0d5f2d3](https://github.com/angular/angular/commit/0d5f2d3))
* **compiler-cli:** don't rely on incompatible TS method ([#23550](https://github.com/angular/angular/issues/23550)) ([b1f040f](https://github.com/angular/angular/commit/b1f040f))
* **core:** stop reusing provider definitions across NgModuleRef instances ([#25022](https://github.com/angular/angular/issues/25022)) ([6b859da](https://github.com/angular/angular/commit/6b859da)), closes [#25018](https://github.com/angular/angular/issues/25018)
* **core:** mark NgModule as not the root if APP_ROOT is set to false ([#24814](https://github.com/angular/angular/issues/24814)) ([1089261](https://github.com/angular/angular/commit/1089261))
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([0922228](https://github.com/angular/angular/commit/0922228)), closes [#22939](https://github.com/angular/angular/issues/22939)
* **core:** mark NgModule as not the root if APP_ROOT is set to false ([#24814](https://github.com/angular/angular/issues/24814)) ([1089261](https://github.com/angular/angular/commit/1089261))
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([0922228](https://github.com/angular/angular/commit/0922228)), closes [#22939](https://github.com/angular/angular/issues/22939)
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([ccbda9d](https://github.com/angular/angular/commit/ccbda9d))
* **core:** avoid eager providers re-initialization ([#23559](https://github.com/angular/angular/issues/23559)) ([0c6dc45](https://github.com/angular/angular/commit/0c6dc45))
* **core:** call ngOnDestroy on all services that have it ([#23755](https://github.com/angular/angular/issues/23755)) ([fc03427](https://github.com/angular/angular/commit/fc03427)), closes [#22466](https://github.com/angular/angular/issues/22466) [#22240](https://github.com/angular/angular/issues/22240) [#14818](https://github.com/angular/angular/issues/14818)
@ -44,10 +68,6 @@
* **elements:** prevent closure renaming of platform properties ([#23843](https://github.com/angular/angular/issues/23843)) ([d4b8b24](https://github.com/angular/angular/commit/d4b8b24))
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([9367e91](https://github.com/angular/angular/commit/9367e91)), closes [#17195](https://github.com/angular/angular/issues/17195)
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([6881404](https://github.com/angular/angular/commit/6881404)), closes [#21420](https://github.com/angular/angular/issues/21420)
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([6881404](https://github.com/angular/angular/commit/6881404)), closes [#21420](https://github.com/angular/angular/issues/21420)
* **platform-browser:** add missing deps for HammerGesturesPlugin ([#24682](https://github.com/angular/angular/issues/24682)) ([13d60ea](https://github.com/angular/angular/commit/13d60ea))
* **platform-browser:** mark Meta and Title services as tree shakable providers ([#24815](https://github.com/angular/angular/issues/24815)) ([197387d](https://github.com/angular/angular/commit/197387d))
* **platform-browser:** workaround wrong import path generated by ngc for DOCUMENT ([#24830](https://github.com/angular/angular/issues/24830)) ([7d27ecc](https://github.com/angular/angular/commit/7d27ecc))
* **platform-browser:** add missing deps for HammerGesturesPlugin ([#24682](https://github.com/angular/angular/issues/24682)) ([13d60ea](https://github.com/angular/angular/commit/13d60ea))
* **platform-browser:** mark Meta and Title services as tree shakable providers ([#24815](https://github.com/angular/angular/issues/24815)) ([197387d](https://github.com/angular/angular/commit/197387d))
* **platform-browser:** workaround wrong import path generated by ngc for DOCUMENT ([#24830](https://github.com/angular/angular/issues/24830)) ([7d27ecc](https://github.com/angular/angular/commit/7d27ecc))
@ -57,14 +77,12 @@
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([c73196e](https://github.com/angular/angular/commit/c73196e)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
* **router:** Fix _lastPathIndex in deeply nested empty paths ([#22394](https://github.com/angular/angular/issues/22394)) ([968f153](https://github.com/angular/angular/commit/968f153))
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([86d254d](https://github.com/angular/angular/commit/86d254d)), closes [#21468](https://github.com/angular/angular/issues/21468)
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([86d254d](https://github.com/angular/angular/commit/86d254d)), closes [#21468](https://github.com/angular/angular/issues/21468)
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([5731d07](https://github.com/angular/angular/commit/5731d07)), closes [#10981](https://github.com/angular/angular/issues/10981)
* **router:** avoid freezing queryParams in-place ([#22663](https://github.com/angular/angular/issues/22663)) ([89f64e5](https://github.com/angular/angular/commit/89f64e5)), closes [#22617](https://github.com/angular/angular/issues/22617)
* **router:** cache route handle if found ([#22475](https://github.com/angular/angular/issues/22475)) ([4cfa571](https://github.com/angular/angular/commit/4cfa571)), closes [#22474](https://github.com/angular/angular/issues/22474)
* **router:** correct the segment parsing so it won't break on ampersand ([#23684](https://github.com/angular/angular/issues/23684)) ([553a680](https://github.com/angular/angular/commit/553a680))
* **service-worker:** don't include sourceMappingURL in ngsw-worker ([#24877](https://github.com/angular/angular/issues/24877)) ([8620373](https://github.com/angular/angular/commit/8620373)), closes [#23596](https://github.com/angular/angular/issues/23596)
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([52d43a9](https://github.com/angular/angular/commit/52d43a9))
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([52d43a9](https://github.com/angular/angular/commit/52d43a9))
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([3ed2d75](https://github.com/angular/angular/commit/3ed2d75)), closes [#24095](https://github.com/angular/angular/issues/24095)
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([fb59b2d](https://github.com/angular/angular/commit/fb59b2d)), closes [#23196](https://github.com/angular/angular/issues/23196)
* **service-worker:** check platformBrowser before accessing navigator.serviceWorker ([#21231](https://github.com/angular/angular/issues/21231)) ([0bdd30e](https://github.com/angular/angular/commit/0bdd30e))
@ -82,9 +100,6 @@
* **core:** expose a Compiler API for accessing module ids from NgModule types ([#24258](https://github.com/angular/angular/issues/24258)) ([bd02b27](https://github.com/angular/angular/commit/bd02b27))
* **core:** KeyValueDiffer#diff allows null values ([#24319](https://github.com/angular/angular/issues/24319)) ([52ce9d5](https://github.com/angular/angular/commit/52ce9d5))
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([3553977](https://github.com/angular/angular/commit/3553977))
* **core:** add support for using async/await with Jasmine ([#24637](https://github.com/angular/angular/issues/24637)) ([71100e6](https://github.com/angular/angular/commit/71100e6))
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([3553977](https://github.com/angular/angular/commit/3553977))
* **core:** add support for using async/await with Jasmine ([#24637](https://github.com/angular/angular/issues/24637)) ([71100e6](https://github.com/angular/angular/commit/71100e6))
(https://github.com/angular/angular/commit/328971f)), closes [#24616](https://github.com/angular/angular/issues/24616)
* **platform-browser:** add HammerJS lazy-loader symbols to public API ([#23943](https://github.com/angular/angular/issues/23943)) ([26fbf1d](https://github.com/angular/angular/commit/26fbf1d))
* **platform-browser:** allow lazy-loading HammerJS ([#23906](https://github.com/angular/angular/issues/23906)) ([313bdce](https://github.com/angular/angular/commit/313bdce))
@ -99,7 +114,7 @@
* **bazel:** turn on preserve-symlinks ([#24881](https://github.com/angular/angular/issues/24881)) ([c438b5e](https://github.com/angular/angular/commit/c438b5e))
### BREAKING CHANGES
### Angular Labs (experimental feature) breaking change
* **bazel:** Use of @angular/bazel rules now requires calling ng_setup_workspace() in your WORKSPACE file.
@ -247,7 +262,6 @@ To learn about the release highlights and our new CLI-powered update workflow fo
* **animations:** only use the WA-polyfill alongside AnimationBuilder ([#22143](https://github.com/angular/angular/issues/22143)) ([b2f366b](https://github.com/angular/angular/commit/b2f366b)), closes [#17496](https://github.com/angular/angular/issues/17496)
* **animations:** expose `element` and `params` within transition matchers ([#22693](https://github.com/angular/angular/issues/22693)) ([58b94e6](https://github.com/angular/angular/commit/58b94e6))
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
* **common:** export functions to format numbers, percents, currencies & dates ([#22423](https://github.com/angular/angular/issues/22423)) ([4180912](https://github.com/angular/angular/commit/4180912)), closes [#20536](https://github.com/angular/angular/issues/20536)
* **compiler:** lower @NgModule ids if needed ([#23031](https://github.com/angular/angular/issues/23031)) ([bd024c0](https://github.com/angular/angular/commit/bd024c0))
* **compiler:** implement "enableIvy" compiler option ([#21427](https://github.com/angular/angular/issues/21427)) ([64d16de](https://github.com/angular/angular/commit/64d16de))
@ -287,7 +301,6 @@ To learn about the release highlights and our new CLI-powered update workflow fo
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
* **animations:** avoid animation insertions during router back/refresh ([#21977](https://github.com/angular/angular/issues/21977)) ([f88fba0](https://github.com/angular/angular/commit/f88fba0)), closes [#19712](https://github.com/angular/angular/issues/19712)
* **animations:** treat numeric state name values as strings ([#22923](https://github.com/angular/angular/issues/22923)) ([e5e1b0d](https://github.com/angular/angular/commit/e5e1b0d))
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
* **animations:** fix increment/decrement aliases example ([#18323](https://github.com/angular/angular/issues/18323)) ([d2aa8ac](https://github.com/angular/angular/commit/d2aa8ac))
* **common:** NgClass should properly take className changes into account ([#21937](https://github.com/angular/angular/issues/21937)) ([4a42669](https://github.com/angular/angular/commit/4a42669)), closes [#21932](https://github.com/angular/angular/issues/21932)
* **common:** fix the titlecase pipe ([#22600](https://github.com/angular/angular/issues/22600)) ([7966744](https://github.com/angular/angular/commit/7966744))

View File

@ -6,23 +6,30 @@ workspace(name = "angular")
http_archive(
name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
strip_prefix = "rules_nodejs-0.10.1",
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.11.4.zip"],
strip_prefix = "rules_nodejs-0.11.4",
sha256 = "c31c4ead696944a50fad2b3ee9dfbbeffe31a8dcca0b21b9bf5b3e6c6b069801",
)
http_archive(
name = "bazel_skylib",
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip"],
strip_prefix = "bazel-skylib-0.3.1",
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
)
http_archive(
name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
strip_prefix = "rules_webtesting-0.2.1",
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
)
http_archive(
name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.3.zip",
strip_prefix = "rules_typescript-0.15.3",
sha256 = "a2b26ac3fc13036011196063db1bf7f1eae81334449201dc28087ebfa3708c99",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
strip_prefix = "rules_typescript-0.16.0",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
)
http_archive(
@ -71,6 +78,22 @@ http_archive(
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
)
#
# Point Bazel to WORKSPACEs that live in subdirectories
#
local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
# Point to the integration test workspace just so that Bazel doesn't descend into it
# when expanding the //... pattern
local_repository(
name = "bazel_integration_test",
path = "integration/bazel",
)
#
# Load and install our dependencies downloaded above.
#
@ -100,26 +123,10 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
ts_setup_workspace()
load("//packages/bazel/src:ng_setup_workspace.bzl", "ng_setup_workspace")
load("@angular//:index.bzl", "ng_setup_workspace")
ng_setup_workspace()
#
# Point Bazel to WORKSPACEs that live in subdirectories
#
local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
# Point to the integration test workspace just so that Bazel doesn't descend into it
# when expanding the //... pattern
local_repository(
name = "bazel_integration_test",
path = "integration/bazel",
)
#
# Ask Bazel to manage these toolchain dependencies for us.
# Bazel will run `yarn install` when one of these toolchains is requested during

View File

@ -8,7 +8,7 @@ Everything in this folder is part of the documentation project. This includes
## Developer tasks
We use `yarn` to manage the dependencies and to run build tasks.
We use [Yarn](https://yarnpkg.com) to manage the dependencies and to run build tasks.
You should run all these tasks from the `angular/aio` folder.
Here are the most important tasks you might need to use:
@ -43,6 +43,17 @@ Here are the most important tasks you might need to use:
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
## Developing on Windows
The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
These unresolved links cause the docs generation process to fail because it cannot locate certain files.
> Hint: The following steps require administration rights or [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) enabled!
To fix this problem, run `scripts/windows/create-symlinks.sh`. This command creates temporary files where the symlinks used to be. Make sure not to commit those files with your documentation changes.
When you are done making and testing your documentation changes, you can restore the original symlinks and delete the temporary files by running `scripts/windows/remove-symlinks.sh`.
It's necessary to remove the temporary files, because otherwise they're displayed as local changes in your git working copy and certain operations are blocked.
## Using ServiceWorker locally
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which

View File

@ -1,26 +1,38 @@
import { ReflectiveInjector } from '@angular/core';
import { Injector } from '@angular/core';
import { Car, Engine, Tires } from './car';
import { Logger } from '../logger.service';
// #docregion injector
export function useInjector() {
let injector: ReflectiveInjector;
let injector: Injector;
// #enddocregion injector
/*
// #docregion injector-no-new
// Cannot instantiate an ReflectiveInjector like this!
let injector = new ReflectiveInjector([Car, Engine, Tires]);
// Cannot instantiate an Injector like this!
let injector = new Injector([
{ provide: Car, deps: [Engine, Tires] },
{ provide: Engine, deps: [] },
{ provide: Tires, deps: [] }
]);
// #enddocregion injector-no-new
*/
// #docregion injector, injector-create-and-call
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
injector = Injector.create({
providers: [
{ provide: Car, deps: [Engine, Tires] },
{ provide: Engine, deps: [] },
{ provide: Tires, deps: [] }
]
});
// #docregion injector-call
let car = injector.get(Car);
// #enddocregion injector-call, injector-create-and-call
car.description = 'Injector';
injector = ReflectiveInjector.resolveAndCreate([Logger]);
injector = Injector.create({
providers: [{ provide: Logger, deps: [] }]
});
let logger = injector.get(Logger);
logger.log('Injector car.drive() said: ' + car.drive());
return car;

View File

@ -0,0 +1,9 @@
{
"description": "Angular Elements",
"files":[
"!**/*.d.ts",
"!**/*.js",
"!**/*.[1].*"
],
"tags":["cookbook"]
}

View File

@ -1,15 +1,14 @@
// #docplaster
// #docregion app-module
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
// import the feature module here so you can add it to the imports array below
import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard.module';
@NgModule({
declarations: [
AppComponent
@ -17,7 +16,7 @@ import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard
imports: [
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
CustomerDashboardModule // add the feature module here
],
providers: [],

View File

@ -16,6 +16,7 @@ describe('Form Validation Tests', function () {
tests('Template-Driven Form');
bobTests();
asyncValidationTests();
crossValidationTests();
});
@ -26,6 +27,7 @@ describe('Form Validation Tests', function () {
tests('Reactive Form');
bobTests();
asyncValidationTests();
crossValidationTests();
});
});
@ -45,6 +47,7 @@ let page: {
errorMessages: ElementArrayFinder,
heroFormButtons: ElementArrayFinder,
heroSubmitted: ElementFinder,
alterEgoErrors: ElementFinder,
crossValidationErrorMessage: ElementFinder,
};
@ -63,6 +66,7 @@ function getPage(sectionTag: string) {
errorMessages: section.all(by.css('div.alert')),
heroFormButtons: buttons,
heroSubmitted: section.element(by.css('.submitted-message')),
alterEgoErrors: section.element(by.css('.alter-ego-errors')),
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
};
}
@ -156,6 +160,16 @@ function expectFormIsInvalid() {
expect(page.form.getAttribute('class')).toMatch('ng-invalid');
}
function triggerAlterEgoValidation() {
// alterEgo has updateOn set to 'blur', click outside of the input to trigger the blur event
element(by.css('app-root')).click()
}
function waitForAlterEgoValidation() {
// alterEgo async validation will be performed in 400ms
browser.sleep(400);
}
function bobTests() {
const emsg = 'Name cannot be Bob.';
@ -177,6 +191,32 @@ function bobTests() {
});
}
function asyncValidationTests() {
const emsg = 'Alter ego is already taken.';
it(`should produce "${emsg}" error after setting alterEgo to Eric`, function () {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Eric');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsInvalid();
expect(page.alterEgoErrors.getText()).toBe(emsg);
});
it('should be ok again with different values', function () {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('John');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsValid();
expect(page.alterEgoErrors.isPresent()).toBe(false);
});
}
function crossValidationTests() {
const emsg = 'Name cannot match alter ego.';
@ -187,6 +227,9 @@ function crossValidationTests() {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Batman');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsInvalid();
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
});
@ -198,6 +241,9 @@ function crossValidationTests() {
page.alterEgoInput.clear();
page.alterEgoInput.sendKeys('Superman');
triggerAlterEgoValidation();
waitForAlterEgoValidation();
expectFormIsValid();
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
});

View File

@ -8,6 +8,7 @@ import { HeroFormTemplateComponent } from './template/hero-form-template.compone
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
import { UniqueAlterEgoValidatorDirective } from './shared/alter-ego.directive';
@NgModule({
imports: [
@ -20,7 +21,8 @@ import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.d
HeroFormTemplateComponent,
HeroFormReactiveComponent,
ForbiddenValidatorDirective,
IdentityRevealedValidatorDirective
IdentityRevealedValidatorDirective,
UniqueAlterEgoValidatorDirective
],
bootstrap: [ AppComponent ]
})

View File

@ -0,0 +1,48 @@
/* tslint:disable: member-ordering forin */
// #docplaster
// #docregion
import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
@Component({
selector: 'app-hero-form-reactive',
templateUrl: './hero-form-reactive.component.html',
styleUrls: ['./hero-form-reactive.component.css'],
})
export class HeroFormReactiveComponent implements OnInit {
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
heroForm: FormGroup;
ngOnInit(): void {
// #docregion async-validation
this.heroForm = new FormGroup({
'name': new FormControl(this.hero.name, [
Validators.required,
Validators.minLength(4),
forbiddenNameValidator(/bob/i)
]),
'alterEgo': new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur'
}),
'power': new FormControl(this.hero.power, Validators.required)
});
// #enddocregion async-validation
}
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
get alterEgo() { return this.heroForm.get('alterEgo'); }
// #docregion async-validation
constructor(private alterEgoValidator: UniqueAlterEgoValidator) {}
// #enddocregion async-validation
}

View File

@ -35,6 +35,13 @@
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
formControlName="alterEgo" >
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
Alter ego is already taken.
</div>
</div>
</div>
<!-- #docregion cross-validation-error-message -->

View File

@ -4,6 +4,7 @@ import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
import { UniqueAlterEgoValidator } from '../shared/alter-ego.directive';
@Component({
selector: 'app-hero-form-reactive',
@ -25,7 +26,10 @@ export class HeroFormReactiveComponent implements OnInit {
Validators.minLength(4),
forbiddenNameValidator(/bob/i)
]),
'alterEgo': new FormControl(this.hero.alterEgo),
'alterEgo': new FormControl(this.hero.alterEgo, {
asyncValidators: [this.alterEgoValidator.validate.bind(this.alterEgoValidator)],
updateOn: 'blur'
}),
'power': new FormControl(this.hero.power, Validators.required)
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
}
@ -33,4 +37,8 @@ export class HeroFormReactiveComponent implements OnInit {
get name() { return this.heroForm.get('name'); }
get power() { return this.heroForm.get('power'); }
get alterEgo() { return this.heroForm.get('alterEgo'); }
constructor(private alterEgoValidator: UniqueAlterEgoValidator) { }
}

View File

@ -0,0 +1,46 @@
import { Directive, forwardRef, Injectable } from '@angular/core';
import {
AsyncValidator,
AbstractControl,
NG_ASYNC_VALIDATORS,
ValidationErrors
} from '@angular/forms';
import { catchError, map } from 'rxjs/operators';
import { HeroesService } from './heroes.service';
import { Observable } from 'rxjs';
// #docregion async-validator
@Injectable({ providedIn: 'root' })
export class UniqueAlterEgoValidator implements AsyncValidator {
constructor(private heroesService: HeroesService) {}
validate(
ctrl: AbstractControl
): Promise<ValidationErrors | null> | Observable<ValidationErrors | null> {
return this.heroesService.isAlterEgoTaken(ctrl.value).pipe(
map(isTaken => (isTaken ? { uniqueAlterEgo: true } : null)),
catchError(() => null)
);
}
}
// #enddocregion async-validator
// #docregion async-validator-directive
@Directive({
selector: '[appUniqueAlterEgo]',
providers: [
{
provide: NG_ASYNC_VALIDATORS,
useExisting: forwardRef(() => UniqueAlterEgoValidator),
multi: true
}
]
})
export class UniqueAlterEgoValidatorDirective {
constructor(private validator: UniqueAlterEgoValidator) {}
validate(control: AbstractControl) {
this.validator.validate(control);
}
}
// #enddocregion async-validator-directive

View File

@ -0,0 +1,14 @@
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { delay } from 'rxjs/operators';
const ALTER_EGOS = ['Eric'];
@Injectable({ providedIn: 'root' })
export class HeroesService {
isAlterEgoTaken(alterEgo: string): Observable<boolean> {
const isTaken = ALTER_EGOS.includes(alterEgo);
return of(isTaken).pipe(delay(400));
}
}

View File

@ -11,7 +11,7 @@
<label for="name">Name</label>
<!-- #docregion name-with-error-msg -->
<!-- #docregion name-input -->
<input id="name" name="name" class="form-control"
<input id="name" name="name" class="form-control"
required minlength="4" appForbiddenName="bob"
[(ngModel)]="hero.name" #name="ngModel" >
<!-- #enddocregion name-input -->
@ -35,8 +35,20 @@
<div class="form-group">
<label for="alterEgo">Alter Ego</label>
<input id="alterEgo" class="form-control"
name="alterEgo" [(ngModel)]="hero.alterEgo" >
<!-- #docregion async-validation -->
<input id="alterEgo" class="form-control" name="alterEgo"
#alterEgo="ngModel"
[(ngModel)]="hero.alterEgo"
[ngModelOptions]="{ updateOn: 'blur' }"
appUniqueAlterEgo>
<!-- #enddocregion async-validation -->
<div *ngIf="alterEgo.pending">Validating...</div>
<div *ngIf="alterEgo.invalid" class="alert alert-danger alter-ego-errors">
<div *ngIf="alterEgo.errors?.uniqueAlterEgo">
Alter ego is already taken.
</div>
</div>
</div>
<!-- #docregion cross-validation-error-message -->

View File

@ -1,9 +1,9 @@
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { AppRoutingModule } from './app-routing.module';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
@NgModule({
@ -13,7 +13,7 @@ import { AppComponent } from './app.component';
imports: [
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
AppRoutingModule
],
providers: [],

View File

@ -1,26 +1,21 @@
// #docregion
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import {
FlyingHeroesComponent,
FlyingHeroesImpureComponent
} from './flying-heroes.component';
import { ExponentialStrengthPipe } from './exponential-strength.pipe';
import { FetchJsonPipe } from './fetch-json.pipe';
import { FlyingHeroesComponent, FlyingHeroesImpureComponent } from './flying-heroes.component';
import { FlyingHeroesImpurePipe, FlyingHeroesPipe } from './flying-heroes.pipe';
import { HeroAsyncMessageComponent } from './hero-async-message.component';
import { HeroBirthdayComponent } from './hero-birthday1.component';
import { HeroBirthday2Component } from './hero-birthday2.component';
import { HeroListComponent } from './hero-list.component';
import { PowerBoosterComponent } from './power-booster.component';
import { PowerBoostCalculatorComponent } from './power-boost-calculator.component';
import {
FlyingHeroesPipe,
FlyingHeroesImpurePipe
} from './flying-heroes.pipe';
import { FetchJsonPipe } from './fetch-json.pipe';
import { ExponentialStrengthPipe } from './exponential-strength.pipe';
import { PowerBoosterComponent } from './power-booster.component';
@NgModule({
imports: [
@ -43,6 +38,6 @@ import { ExponentialStrengthPipe } from './exponential-strength.pipe';
FetchJsonPipe,
ExponentialStrengthPipe
],
bootstrap: [ AppComponent ]
bootstrap: [AppComponent]
})
export class AppModule { }

View File

@ -1,13 +1,14 @@
// #docregion
import { Pipe, PipeTransform } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Pipe, PipeTransform } from '@angular/core';
// #docregion pipe-metadata
@Pipe({
name: 'fetch',
pure: false
})
// #enddocregion pipe-metadata
export class FetchJsonPipe implements PipeTransform {
export class FetchJsonPipe implements PipeTransform {
private cachedData: any = null;
private cachedUrl = '';
@ -17,7 +18,7 @@ export class FetchJsonPipe implements PipeTransform {
if (url !== this.cachedUrl) {
this.cachedData = null;
this.cachedUrl = url;
this.http.get(url).subscribe( result => this.cachedData = result );
this.http.get(url).subscribe(result => this.cachedData = result);
}
return this.cachedData;

View File

@ -23,7 +23,10 @@ import { Hero, HeroService } from './shared';
`,
styles: [`
.heroes {
margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em;
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;

View File

@ -1,6 +1,9 @@
/* #docregion */
.heroes {
margin: 0 0 2em 0; list-style-type: none; padding: 0; width: 15em;
margin: 0 0 2em 0;
list-style-type: none;
padding: 0;
width: 15em;
}
.heroes li {
cursor: pointer;

View File

@ -0,0 +1,27 @@
import { TestBed, async, tick, fakeAsync } from '@angular/core/testing';
import { CanvasComponent } from './canvas.component';
describe('CanvasComponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
CanvasComponent
],
}).compileComponents();
}));
beforeEach(() => {
window['__zone_symbol__FakeAsyncTestMacroTask'] = [
{
source: 'HTMLCanvasElement.toBlob',
callbackArgs: [{ size: 200 }]
}
];
});
it('should be able to generate blob data from canvas', fakeAsync(() => {
const fixture = TestBed.createComponent(CanvasComponent);
fixture.detectChanges();
tick();
const app = fixture.debugElement.componentInstance;
expect(app.blobSize).toBeGreaterThan(0);
}));
});

View File

@ -0,0 +1,25 @@
import { Component, AfterViewInit, ViewChild } from '@angular/core';
@Component({
selector: 'sample-canvas',
template: '<canvas #sampleCanvas width="200" height="200"></canvas>'
})
export class CanvasComponent implements AfterViewInit {
blobSize: number;
@ViewChild('sampleCanvas') sampleCanvas;
constructor() { }
ngAfterViewInit() {
const canvas = this.sampleCanvas.nativeElement;
const context = canvas.getContext('2d');
if (context) {
context.clearRect(0, 0, 200, 200);
context.fillStyle = '#FF1122';
context.fillRect(0, 0, 200, 200);
canvas.toBlob((blob: any) => {
this.blobSize = blob.size;
});
}
}
}

View File

@ -1,6 +1,6 @@
// #docplaster
// #docregion
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit } from '@angular/core';
// #docregion added-imports
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
@ -17,7 +17,7 @@ import { HeroService } from '../hero.service';
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
@Input() hero: Hero;
hero: Hero;
// #docregion ctor
constructor(

View File

@ -1,4 +1,4 @@
import { Component, OnInit, Input } from '@angular/core';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { Location } from '@angular/common';
@ -11,7 +11,7 @@ import { HeroService } from '../hero.service';
styleUrls: [ './hero-detail.component.css' ]
})
export class HeroDetailComponent implements OnInit {
@Input() hero: Hero;
hero: Hero;
constructor(
private route: ActivatedRoute,

View File

@ -1,8 +1,7 @@
# Introduction to components
<img src="generated/images/guide/architecture/hero-component.png" alt="Component" class="left">
A _component_ controls a patch of screen called a *view*. For example, individual components define and control each of the following views from the [Tutorial](tutorial/index):
A *component* controls a patch of screen called a *view*.
For example, individual components define and control each of the following views from the [Tutorial](tutorial):
* The app root with the navigation links.
* The list of heroes.
@ -11,38 +10,38 @@ A _component_ controls a patch of screen called a *view*. For example, individua
You define a component's application logic&mdash;what it does to support the view&mdash;inside a class.
The class interacts with the view through an API of properties and methods.
For example, the `HeroListComponent` has a `heroes` property that holds an array of heroes. It also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system.
For example, `HeroListComponent` has a `heroes` property that holds an array of heroes.
Its `selectHero()` method sets a `selectedHero` property when the user clicks to choose a hero from that list.
The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor.
The service is provided to the component through the dependency injection system.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"></code-example>
Angular creates, updates, and destroys components as the user moves through the application. Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()`.
<hr/>
## Component metadata
<img src="generated/images/guide/architecture/metadata.png" alt="Metadata" class="left">
The `@Component` decorator identifies the class immediately below it as a component class, and specifies its metadata. In the example code below, you can see that `HeroListComponent` is just a class, with no special Angular notation or syntax at all. It's not a component until you mark it as one with the `@Component` decorator.
The metadata for a component tells Angular where to get the major building blocks it needs to create and present the component and its view. In particular, it associates a _template_ with the component, either directly with inline code, or by reference. Together, the component and its template describe a _view_.
The metadata for a component tells Angular where to get the major building blocks that it needs to create and present the component and its view. In particular, it associates a *template* with the component, either directly with inline code, or by reference. Together, the component and its template describe a *view*.
In addition to containing or pointing to the template, the `@Component` metadata configures, for example, how the component can be referenced in HTML and what services it requires.
Here's an example of basic metadata for `HeroListComponent`:
Here's an example of basic metadata for `HeroListComponent`.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (metadata)" region="metadata"></code-example>
This example shows some of the most useful `@Component` configuration options:
This example shows some of the most useful `@Component` configuration options:
* `selector`: A CSS selector that tells Angular to create and insert an instance of this component wherever it finds the corresponding tag in template HTML. For example, if an app's HTML contains `<app-hero-list></app-hero-list>`, then
Angular inserts an instance of the `HeroListComponent` view between those tags.
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's _host view_.
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's *host view*.
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
* `providers`: An array of [providers](guide/glossary#provider) for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
<hr/>
## Templates and views
@ -50,7 +49,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
You define a component's view with its companion template. A template is a form of HTML that tells Angular how to render the component.
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's _host view_. The component can also define a _view hierarchy_, which contains _embedded views_, hosted by other components.
Views are typically arranged hierarchically, allowing you to modify or show and hide entire UI sections or pages as a unit. The template immediately associated with a component defines that component's *host view*. The component can also define a *view hierarchy*, which contains *embedded views*, hosted by other components.
<figure>
<img src="generated/images/guide/architecture/component-tree.png" alt="Component tree" class="left">
@ -60,43 +59,47 @@ A view hierarchy can include views from components in the same NgModule, but it
## Template syntax
A template looks like regular HTML, except that it also contains Angular [template syntax](guide/template-syntax), which alters the HTML based on your app's logic and the state of app and DOM data. Your template can use _data binding_ to coordinate the app and DOM data, _pipes_ to transform data before it is displayed, and _directives_ to apply app logic to what gets displayed.
A template looks like regular HTML, except that it also contains Angular [template syntax](guide/template-syntax), which alters the HTML based on your app's logic and the state of app and DOM data. Your template can use *data binding* to coordinate the app and DOM data, *pipes* to transform data before it is displayed, and *directives* to apply app logic to what gets displayed.
For example, here is a template for the Tutorial's `HeroListComponent`:
For example, here is a template for the Tutorial's `HeroListComponent`.
<code-example path="architecture/src/app/hero-list.component.html" title="src/app/hero-list.component.html"></code-example>
This template uses typical HTML elements like `<h2>` and `<p>`, and also includes Angular template-syntax elements, `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<app-hero-detail>`. The template-syntax elements tell Angular how to render the HTML to the screen, using program logic and data.
* The `*ngFor` directive tells Angular to iterate over a list.
* The `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`. The `HeroDetailComponent` (code not shown) is a child component of the `HeroListComponent` that defines the Hero-detail view. Notice how custom components like this mix seamlessly with native HTML in the same layouts.
* The `*ngFor` directive tells Angular to iterate over a list.
* `{{hero.name}}`, `(click)`, and `[hero]` bind program data to and from the DOM, responding to user input. See more about [data binding](#data-binding) below.
* The `<app-hero-detail>` tag in the example is an element that represents a new component, `HeroDetailComponent`.
`HeroDetailComponent` (code not shown) defines the hero-detail child view of `HeroListComponent`.
Notice how custom components like this mix seamlessly with native HTML in the same layouts.
### Data binding
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push/pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
Without a framework, you would be responsible for pushing data values into the HTML controls and turning user responses into actions and value updates. Writing such push and pull logic by hand is tedious, error-prone, and a nightmare to read, as any experienced jQuery programmer can attest.
Angular supports *two-way data binding*, a mechanism for coordinating parts of a template with parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
Angular supports *two-way data binding*, a mechanism for coordinating the parts of a template with the parts of a component. Add binding markup to the template HTML to tell Angular how to connect both sides.
The following diagram shows the four forms of data binding markup. Each form has a direction&mdash;to the DOM, from the DOM, or in both directions.
The following diagram shows the four forms of data binding markup. Each form has a direction: to the DOM, from the DOM, or both.
<figure>
<img src="generated/images/guide/architecture/databinding.png" alt="Data Binding" class="left">
</figure>
This example from the `HeroListComponent` template uses three of these forms:
This example from the `HeroListComponent` template uses three of these forms.
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (binding)" region="binding"></code-example>
* The `{{hero.name}}` [*interpolation*](guide/displaying-data#interpolation)
displays the component's `hero.name` property value within the `<li>` element.
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of `selectedHero` from
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* The `[hero]` [*property binding*](guide/template-syntax#property-binding) passes the value of
`selectedHero` from the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
* The `(click)` [*event binding*](guide/user-input#binding-to-user-input-events) calls the component's `selectHero` method when the user clicks a hero's name.
**Two-way data binding** is an important fourth form that combines property and event binding in a single notation. Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive:
Two-way data binding (used mainly in [template-driven forms](guide/forms))
combines property and event binding in a single notation.
Here's an example from the `HeroDetailComponent` template that uses two-way data binding with the `ngModel` directive.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
@ -104,7 +107,7 @@ In two-way binding, a data property value flows to the input box from the compon
The user's changes also flow back to the component, resetting the property to the latest value,
as with event binding.
Angular processes *all* data bindings once per JavaScript event cycle,
Angular processes *all* data bindings once for each JavaScript event cycle,
from the root of the application component tree through all child components.
<figure>
@ -119,17 +122,17 @@ Data binding plays an important role in communication between a template and its
### Pipes
Angular pipes let you declare display-value transformations in your template HTML. A class with the `@Pipe` decorator defines a function that transforms input values to output values for display in a view.
Angular pipes let you declare display-value transformations in your template HTML. A class with the `@Pipe` decorator defines a function that transforms input values to output values for display in a view.
Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes.
Angular defines various pipes, such as the [date](https://angular.io/api/common/DatePipe) pipe and [currency](https://angular.io/api/common/CurrencyPipe) pipe; for a complete list, see the [Pipes API list](https://angular.io/api?type=pipe). You can also define new pipes.
To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe):
To specify a value transformation in an HTML template, use the [pipe operator (|)](https://angular.io/guide/template-syntax#pipe).
`{{interpolated_value | pipe_name}}`
`{{interpolated_value | pipe_name}}`
You can chain pipes, sending the output of one pipe function to be transformed by another pipe function. A pipe can also take arguments that control how it performs its transformation. For example, you can pass the desired format to the `date` pipe:
You can chain pipes, sending the output of one pipe function to be transformed by another pipe function. A pipe can also take arguments that control how it performs its transformation. For example, you can pass the desired format to the `date` pipe.
```
```
<!-- Default format: output 'Jun 15, 2015'-->
<p>Today is {{today | date}}</p>
@ -140,33 +143,38 @@ Data binding plays an important role in communication between a template and its
<p>The time is {{today | date:'shortTime'}}</p>
```
<hr/>
### Directives
<img src="generated/images/guide/architecture/directive.png" alt="Directives" class="left">
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM according to the instructions given by *directives*. A directive is a class with a `@Directive` decorator.
Angular templates are *dynamic*. When Angular renders them, it transforms the DOM according to the instructions given by *directives*. A directive is a class with a `@Directive()` decorator.
A component is technically a directive - but components are so distinctive and central to Angular applications that Angular defines the `@Component` decorator, which extends the `@Directive` decorator with template-oriented features.
A component is technically a directive.
However, components are so distinctive and central to Angular applications that Angular
defines the `@Component()` decorator, which extends the `@Directive()` decorator with
template-oriented features.
There are two kinds of directives besides components: _structural_ and _attribute_ directives. Just as for components, the metadata for a directive associates the class with a `selector` that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
In addition to components, there are two other kinds of directives: *structural* and *attribute*.
Angular defines a number of directives of both kinds, and you can define your own using the `@Directive()` decorator.
Just as for components, the metadata for a directive associates the decorated class with a `selector` element that you use to insert it into HTML. In templates, directives typically appear within an element tag as attributes, either by name or as the target of an assignment or a binding.
#### Structural directives
Structural directives alter layout by adding, removing, and replacing elements in DOM. The example template uses two built-in structural directives to add application logic to how the view is rendered:
*Structural directives* alter layout by adding, removing, and replacing elements in the DOM.
The example template uses two built-in structural directives to add application logic to how the view is rendered.
<code-example path="architecture/src/app/hero-list.component.1.html" linenums="false" title="src/app/hero-list.component.html (structural)" region="structural"></code-example>
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
* [`*ngFor`](guide/displaying-data#ngFor) is an iterative; it tells Angular to stamp out one `<li>` per hero in the `heroes` list.
* [`*ngIf`](guide/displaying-data#ngIf) is a conditional; it includes the `HeroDetail` component only if a selected hero exists.
#### Attribute directives
Attribute directives alter the appearance or behavior of an existing element.
*Attribute directives* alter the appearance or behavior of an existing element.
In templates they look like regular HTML attributes, hence the name.
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically an `<input>`) by setting its display value property and responding to change events.
The `ngModel` directive, which implements two-way data binding, is an example of an attribute directive. `ngModel` modifies the behavior of an existing element (typically `<input>`) by setting its display value property and responding to change events.
<code-example path="architecture/src/app/hero-detail.component.html" linenums="false" title="src/app/hero-detail.component.html (ngModel)" region="ngModel"></code-example>
@ -175,6 +183,8 @@ Angular has more pre-defined directives that either alter the layout structure
or modify aspects of DOM elements and components
(for example, [ngStyle](guide/template-syntax#ngStyle) and [ngClass](guide/template-syntax#ngClass)).
You can also write your own directives. Components such as `HeroListComponent` are one kind of custom directive. You can also create custom structural and attribute directives.
<div class="alert is-helpful">
<!-- PENDING: link to where to learn more about other kinds! -->
Learn more in the [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives) guides.
</div>

View File

@ -1,40 +1,39 @@
# Introduction to modules
<img src="generated/images/guide/architecture/module.png" alt="Module" class="left">
Angular apps are modular and Angular has its own modularity system called *NgModules*.
NgModules are containers for a cohesive block of code dedicated to an application domain, a workflow, or a closely related set of capabilities. They can contain components, service providers, and other code files whose scope is defined by the containing NgModule. They can import functionality that is exported from other NgModules, and export selected functionality for use by other NgModules.
Angular apps are modular and Angular has its own modularity system called _NgModules_. An NgModule is a container for a cohesive block of code dedicated to an application domain, a workflow, or a closely related set of capabilities. It can contain components, service providers, and other code files whose scope is defined by the containing NgModule. It can import functionality that is exported from other NgModules, and export selected functionality for use by other NgModules.
Every Angular app has at least one NgModule class, [the *root module*](guide/bootstrapping), which is conventionally named `AppModule` and resides in a file named `app.module.ts`. You launch your app by *bootstrapping* the root NgModule.
Every Angular app has at least one NgModule class, [the _root module_](guide/bootstrapping), which is conventionally named `AppModule` and resides in a file named `app.module.ts`. You launch your app by *bootstrapping* the root NgModule.
While a small application might have only one NgModule, most apps have many more _feature modules_. The _root_ NgModule for an app is so named because it can include child NgModules in a hierarchy of any depth.
While a small application might have only one NgModule, most apps have many more *feature modules*. The *root* NgModule for an app is so named because it can include child NgModules in a hierarchy of any depth.
## NgModule metadata
An NgModule is defined as a class decorated with `@NgModule`. The `@NgModule` decorator is a function that takes a single metadata object, whose properties describe the module. The most important properties are as follows.
An NgModule is defined by a class decorated with `@NgModule()`. The `@NgModule()` decorator is a function that takes a single metadata object, whose properties describe the module. The most important properties are as follows.
* `declarations`&mdash;The [components](guide/architecture-components), _directives_, and _pipes_ that belong to this NgModule.
* `declarations`: The [components](guide/architecture-components), *directives*, and *pipes* that belong to this NgModule.
* `exports`&mdash;The subset of declarations that should be visible and usable in the _component templates_ of other NgModules.
* `exports`: The subset of declarations that should be visible and usable in the *component templates* of other NgModules.
* `imports`&mdash;Other modules whose exported classes are needed by component templates declared in _this_ NgModule.
* `imports`: Other modules whose exported classes are needed by component templates declared in *this* NgModule.
* `providers`&mdash;Creators of [services](guide/architecture-services) that this NgModule contributes to the global collection of services; they become accessible in all parts of the app. (You can also specify providers at the component level, which is often preferred.)
* `providers`: Creators of [services](guide/architecture-services) that this NgModule contributes to the global collection of services; they become accessible in all parts of the app. (You can also specify providers at the component level, which is often preferred.)
* `bootstrap`&mdash;The main application view, called the _root component_, which hosts all other app views. Only the _root NgModule_ should set this `bootstrap` property.
* `bootstrap`: The main application view, called the *root component*, which hosts all other app views. Only the *root NgModule* should set the `bootstrap` property.
Here's a simple root NgModule definition:
Here's a simple root NgModule definition.
<code-example path="architecture/src/app/mini-app.ts" region="module" title="src/app/app.module.ts" linenums="false"></code-example>
<div class="alert is-helpful">
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root NgModule has no reason to _export_ anything because other modules don't need to _import_ the root NgModule.
The `export` property of `AppComponent` is included here for illustration; it isn't actually necessary in this example. A root NgModule has no reason to *export* anything because other modules don't need to *import* the root NgModule.
</div>
## NgModules and components
NgModules provide a _compilation context_ for their components. A root NgModule always has a root component that is created during bootstrap, but any NgModule can include any number of additional components, which can be loaded through the router or created through the template. The components that belong to an NgModule share a compilation context.
NgModules provide a *compilation context* for their components. A root NgModule always has a root component that is created during bootstrap, but any NgModule can include any number of additional components, which can be loaded through the router or created through the template. The components that belong to an NgModule share a compilation context.
<figure>
@ -44,7 +43,7 @@ NgModules provide a _compilation context_ for their components. A root NgModule
<br class="clear">
A component and its template together define a _view_. A component can contain a _view hierarchy_, which allows you to define arbitrarily complex areas of the screen that can be created, modified, and destroyed as a unit. A view hierarchy can mix views defined in components that belong to different NgModules. This is often the case, especially for UI libraries.
A component and its template together define a *view*. A component can contain a *view hierarchy*, which allows you to define arbitrarily complex areas of the screen that can be created, modified, and destroyed as a unit. A view hierarchy can mix views defined in components that belong to different NgModules. This is often the case, especially for UI libraries.
<figure>
@ -54,17 +53,17 @@ A component and its template together define a _view_. A component can contain a
<br class="clear">
When you create a component, it is associated directly with a single view, called the _host view_. The host view can be the root of a view hierarchy, which can contain _embedded views_, which are in turn the host views of other components. Those components can be in the same NgModule, or can be imported from other NgModules. Views in the tree can be nested to any depth.
When you create a component, it's associated directly with a single view, called the *host view*. The host view can be the root of a view hierarchy, which can contain *embedded views*, which are in turn the host views of other components. Those components can be in the same NgModule, or can be imported from other NgModules. Views in the tree can be nested to any depth.
<div class="alert is-helpful">
The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
**Note:** The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
</div>
## NgModules and JavaScript modules
The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are two different and _complementary_ module systems. You can use them both to write your apps.
The NgModule system is different from and unrelated to the JavaScript (ES2015) module system for managing collections of JavaScript objects. These are *complementary* module systems that you can use together to write your apps.
In JavaScript each _file_ is a module and all objects defined in the file belong to that module.
In JavaScript each *file* is a module and all objects defined in the file belong to that module.
The module declares some objects to be public by marking them with the `export` key word.
Other JavaScript modules use *import statements* to access public objects from other modules.
@ -80,29 +79,28 @@ Other JavaScript modules use *import statements* to access public objects from o
<img src="generated/images/guide/architecture/library-module.png" alt="Component" class="left">
Angular ships as a collection of JavaScript modules. You can think of them as library modules. Each Angular library name begins with the `@angular` prefix. Install them with the `npm` package manager and import parts of them with JavaScript `import` statements.
Angular loads as a collection of JavaScript modules. You can think of them as library modules. Each Angular library name begins with the `@angular` prefix. Install them with the `npm` package manager and import parts of them with JavaScript `import` statements.
<br class="clear">
For example, import Angular's `Component` decorator from the `@angular/core` library like this:
For example, import Angular's `Component` decorator from the `@angular/core` library like this.
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
You also import NgModules from Angular _libraries_ using JavaScript import statements.
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library:
You also import NgModules from Angular *libraries* using JavaScript import statements.
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library.
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
In the example of the simple root module above, the application module needs material from within the `BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
In the example of the simple root module above, the application module needs material from within
`BrowserModule`. To access that material, add it to the `@NgModule` metadata `imports` like this.
<code-example path="architecture/src/app/mini-app.ts" region="ngmodule-imports" linenums="false"></code-example>
In this way you're using both the Angular and JavaScript module systems _together_. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.
In this way you're using the Angular and JavaScript module systems *together*. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.
<div class="alert is-helpful">
Learn more from the [NgModules](guide/ngmodules) page.
Learn more from the [NgModules](guide/ngmodules) guide.
</div>
<hr/>

View File

@ -1,48 +1,50 @@
# Next steps: tools and techniques
Once you have understood the basic building blocks, you can begin to learn more about the features and tools that are available to help you develop and deliver Angular applications. Angular provides a lot more features and services that are covered in this documentation.
After you understand the basic Angular building blocks, you can begin to learn more
about the features and tools that are available to help you develop and deliver Angular applications.
Here are some key features.
#### Responsive programming tools
## Responsive programming tools
* [Lifecycle hooks](guide/lifecycle-hooks): Tap into key moments in the lifetime of a component, from its creation to its destruction, by implementing the lifecycle hook interfaces.
* [Lifecycle hooks](guide/lifecycle-hooks): Tap into key moments in the lifetime of a component, from its creation to its destruction, by implementing the lifecycle hook interfaces.
* [Observables and event processing](guide/observables): How to use observables with components and services to publish and subscribe to messages of any type, such as user-interaction events and asynchronous operation results.
* [Observables and event processing](guide/observables): How to use observables with components and services to publish and subscribe to messages of any type, such as user-interaction events and asynchronous operation results.
#### Client-server interaction tools
## Client-server interaction tools
* [HTTP](guide/http): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
* [HTTP](guide/http): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
* [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers.
* [Server-side Rendering](guide/universal): Angular Universal generates static application pages on the server through server-side rendering (SSR). This allows you to run your Angular app on the server in order to improve performance and show the first page quickly on mobile and low-powered devices, and also facilitate web crawlers.
* [Service Workers](guide/service-worker-intro): A service worker is a script that runs in the web browser and manages caching for an application. Service workers function as a network proxy. They intercept outgoing HTTP requests and can, for example, deliver a cached response if one is available. You can significantly improve the user experience by using a service worker to reduce dependency on the network.
* [Service Workers](guide/service-worker-intro): Use a service worker to reduce dependency on the network
significantly improving the use experience.
#### Domain-specific libraries
## Domain-specific libraries
* [Animations](guide/animations): Animate component behavior
without deep knowledge of animation techniques or CSS with Angular's animation library.
* [Animations](guide/animations): Use Angular's animation library to animate component behavior
without deep knowledge of animation techniques or CSS.
* [Forms](guide/forms): Support complex data entry scenarios with HTML-based validation and dirty checking.
* [Forms](guide/forms): Support complex data entry scenarios with HTML-based validation and dirty checking.
#### Support for the development cycle
## Support for the development cycle
* [Testing Platform](guide/testing): Run unit tests on your application parts as they interact with the Angular framework.
* [Testing platform](guide/testing): Run unit tests on your application parts as they interact with the Angular framework.
* [Internationalization](guide/i18n): Angular's internationalization (i18n) tools can help you make your app available in multiple languages.
* [Internationalization](guide/i18n): Make your app available in multiple languages with Angular's internationalization (i18n) tools.
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
* [Compilation](guide/aot-compiler): Angular provides just-in-time (JIT) compilation for the development environment, and ahead-of-time (AOT) compilation for the production environment.
* [Security guidelines](guide/security): Learn about Angular's built-in protections against common web-app vulnerabilities and attacks such as cross-site scripting attacks.
* [Security guidelines](guide/security): Learn about Angular's built-in protections against common web-app vulnerabilities and attacks such as cross-site scripting attacks.
#### Setup and deployment tools
## Setup and deployment tools
* [Setup for local development](guide/setup): Learn how to set up a new project for development with QuickStart.
* [Setup for local development](guide/setup): Set up a new project for development with QuickStart.
* [Installation](guide/npm-packages): The [Angular CLI](https://cli.angular.io/), Angular applications, and Angular itself depend on features and functionality provided by libraries that are available as [npm](https://docs.npmjs.com/) packages.
* [Installation](guide/npm-packages): The [Angular CLI](https://cli.angular.io/), Angular applications, and Angular itself depend on features and functionality provided by libraries that are available as [npm](https://docs.npmjs.com/) packages.
* [Typescript Configuration](guide/typescript-configuration): TypeScript is the primary language for Angular application development.
* [TypeScript configuration](guide/typescript-configuration): TypeScript is the primary language for Angular application development.
* [Browser support](guide/browser-support): Learn how to make your apps compatible across a wide range of browsers.
* [Browser support](guide/browser-support): Make your apps compatible across a wide range of browsers.
* [Deployment](guide/deployment): Learn techniques for deploying your Angular application to a remote server.
* [Deployment](guide/deployment): Learn techniques for deploying your Angular application to a remote server.
<hr/>

View File

@ -1,22 +1,32 @@
# Introduction to services and dependency injection
<img src="generated/images/guide/architecture/service.png" alt="Service" class="left">
*Service* is a broad category encompassing any value, function, or feature that an app needs.
A service is typically a class with a narrow, well-defined purpose.
It should do something specific and do it well.
_Service_ is a broad category encompassing any value, function, or feature that an app needs. A service is typically a class with a narrow, well-defined purpose. It should do something specific and do it well.
<br class="clear">
Angular distinguishes components from services to increase modularity and reusability.
By separating a component's view-related functionality from other kinds of processing,
you can make your component classes lean and efficient.
Angular distinguishes components from services in order to increase modularity and reusability.
Ideally, a component's job is to enable the user experience and nothing more.
A component should present properties and methods for data binding,
in order to mediate between the view (rendered by the template)
and the application logic (which often includes some notion of a *model*).
* By separating a component's view-related functionality from other kinds of processing, you can make your component classes lean and efficient. Ideally, a component's job is to enable the user experience and nothing more. It should present properties and methods for data binding, in order to mediate between the view (rendered by the template) and the application logic (which often includes some notion of a _model_).
A component can delegate certain tasks to services, such as fetching data from the server,
validating user input, or logging directly to the console.
By defining such processing tasks in an *injectable service class*, you make those tasks
available to any component.
You can also make your app more adaptable by injecting different providers of the same kind of service,
as appropriate in different circumstances.
* A component should not need to define things like how to fetch data from the server, validate user input, or log directly to the console. Instead, it can delegate such tasks to services. By defining that kind of processing task in an injectable service class, you make it available to any component. You can also make your app more adaptable by injecting different providers of the same kind of service, as appropriate in different circumstances.
Angular doesn't *enforce* these principles. Angular does help you *follow* these principles by making it easy to factor your
application logic into services and make those services available to components through *dependency injection*.
Angular doesn't *enforce* these principles. Angular does help you *follow* these principles
by making it easy to factor your application logic into services and make those services
available to components through *dependency injection*.
## Service examples
Here's an example of a service class that logs to the browser console:
Here's an example of a service class that logs to the browser console.
<code-example path="architecture/src/app/logger.service.ts" linenums="false" title="src/app/logger.service.ts (class)" region="class"></code-example>
@ -24,35 +34,41 @@ Services can depend on other services. For example, here's a `HeroService` that
<code-example path="architecture/src/app/hero.service.ts" linenums="false" title="src/app/hero.service.ts (class)" region="class"></code-example>
<hr/>
## Dependency injection
## Dependency injection (DI)
<img src="generated/images/guide/architecture/dependency-injection.png" alt="Service" class="left">
DI is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
Components consume services; that is, you can *inject* a service into a component, giving the component access to that service class.
To define a class as a service in Angular, use the `@Injectable` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
To define a class as a service in Angular, use the `@Injectable()` decorator to provide the metadata that allows Angular to inject it into a component as a *dependency*.
Similarly, use the `@Injectable()` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) *has* a dependency.
Similarly, use the `@Injectable` decorator to indicate that a component or other class (such as another service, a pipe, or an NgModule) _has_ a dependency. A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
* The *injector* is the main mechanism. Angular creates an application-wide injector for you during the bootstrap process, and additional injectors as needed. You don't have to create injectors.
*Dependency injection* (often called DI) is wired into the Angular framework and used everywhere to provide new components with the services or other things they need.
* An injector creates dependencies, and maintains a *container* of dependency instances that it reuses if possible.
* The *injector* is the main mechanism. You don't have to create an Angular injector. Angular creates an application-wide injector for you during the bootstrap process.
* A *provider* is an object that tell an injector how to obtain or create a dependency.
* The injector maintains a *container* of dependency instances that it has already created, and reuses them if possible.
For any dependency that you need in your app, you must register a provider with the app's injector,
so that the injector can use the provider to create new instances.
For a service, the provider is typically the service class itself.
* A *provider* is a recipe for creating a dependency. For a service, this is typically the service class itself. For any dependency you need in your app, you must register a provider with the app's injector, so that the injector can use it to create new instances.
<div class="alert is-helpful">
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the types of its constructor parameters. For example, the constructor of `HeroListComponent` needs a `HeroService`:
A dependency doesn't have to be a service&mdash;it could be a function, for example, or a value.
</div>
When Angular creates a new instance of a component class, it determines which services or other dependencies that component needs by looking at the constructor parameter types. For example, the constructor of `HeroListComponent` needs `HeroService`.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (constructor)" region="ctor"></code-example>
When Angular discovers that a component depends on a service, it first checks if the injector already has any existing instances of that service. If a requested service instance does not yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
When Angular discovers that a component depends on a service, it first checks if the injector has any existing instances of that service. If a requested service instance doesn't yet exist, the injector makes one using the registered provider, and adds it to the injector before returning the service to Angular.
When all requested services have been resolved and returned, Angular can call the component's constructor with those services as arguments.
The process of `HeroService` injection looks something like this:
The process of `HeroService` injection looks something like this.
<figure>
<img src="generated/images/guide/architecture/injector-injects.png" alt="Service" class="left">
@ -60,35 +76,41 @@ The process of `HeroService` injection looks something like this:
### Providing services
You must register at least one *provider* of any service you are going to use. A service can register providers itself, making it available everywhere, or you can register providers with specific modules or components. You register providers in the metadata of the service (in the `@Injectable` decorator), or in the `@NgModule` or `@Component` metadata
You must register at least one *provider* of any service you are going to use.
The provider can be part of the service's own metadata, making that service available everywhere,
or you can register providers with specific modules or components.
You register providers in the metadata of the service (in the `@Injectable()` decorator),
or in the `@NgModule()` or `@Component()` metadata
* By default, the Angular CLI command `ng generate service` registers a provider with the root injector for your service by including provider metadata in the `@Injectable` decorator. The tutorial uses this method to register the provider of HeroService class definition:
* By default, the Angular CLI command `ng generate service` registers a provider with the root injector for your service by including provider metadata in the `@Injectable()` decorator. The tutorial uses this method to register the provider of HeroService class definition.
```
@Injectable({
providedIn: 'root',
})
```
```
@Injectable({
providedIn: 'root',
})
```
When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects into any class that asks for it. Registering the provider in the `@Injectable` metadata also allows Angular to optimize an app by removing the service if it turns out not to be used after all.
When you provide the service at the root level, Angular creates a single, shared instance of `HeroService`
and injects it into any class that asks for it.
Registering the provider in the `@Injectable()` metadata also allows Angular to optimize an app
by removing the service from the compiled app if it isn't used.
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule` decorator:
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule()` decorator,
```
@NgModule({
providers: [
BackendService,
Logger
],
...
})
```
```
@NgModule({
providers: [
BackendService,
Logger
],
...
})
```
* When you register a provider at the component level, you get a new instance of the
service with each new instance of that component. At the component level, register a service provider in the `providers` property of the `@Component` metadata:
service with each new instance of that component.
At the component level, register a service provider in the `providers` property of the `@Component()` metadata.
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (component providers)" region="providers"></code-example>
For more detailed information, see the [Dependency Injection](guide/dependency-injection) section.
<hr/>

View File

@ -1,11 +1,11 @@
# Architecture overview
Angular is a platform and framework for building client applications in HTML and TypeScript.
Angular is itself written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.
Angular is written in TypeScript. It implements core and optional functionality as a set of TypeScript libraries that you import into your apps.
The basic building blocks of an Angular application are _NgModules_, which provide a compilation context for _components_. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a _root module_ that enables bootstrapping, and typically has many more _feature modules_.
The basic building blocks of an Angular application are *NgModules*, which provide a compilation context for *components*. NgModules collect related code into functional sets; an Angular app is defined by a set of NgModules. An app always has at least a *root module* that enables bootstrapping, and typically has many more *feature modules*.
* Components define *views*, which are sets of screen elements that Angular can choose among and modify according to your program logic and data. Every app has at least a root component.
* Components define *views*, which are sets of screen elements that Angular can choose among and modify according to your program logic and data.
* Components use *services*, which provide specific functionality not directly related to views. Service providers can be *injected* into components as *dependencies*, making your code modular, reusable, and efficient.
@ -13,19 +13,19 @@ Both components and services are simply classes, with *decorators* that mark the
* The metadata for a component class associates it with a *template* that defines a view. A template combines ordinary HTML with Angular *directives* and *binding markup* that allow Angular to modify the HTML before rendering it for display.
* The metadata for a service class provides the information Angular needs to make it available to components through *Dependency Injection (DI)*.
* The metadata for a service class provides the information Angular needs to make it available to components through *dependency injection (DI)*.
An app's components typically define many views, arranged hierarchically. Angular provides the `Router` service to help you define navigation paths among views. The router provides sophisticated in-browser navigational capabilities.
## Modules
Angular defines the `NgModule`, which differs from and complements the JavaScript (ES2015) module. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.
Angular *NgModules* differ from and complement JavaScript (ES2015) modules. An NgModule declares a compilation context for a set of components that is dedicated to an application domain, a workflow, or a closely related set of capabilities. An NgModule can associate its components with related code, such as services, to form functional units.
Every Angular app has a _root module_, conventionally named `AppModule`, which provides the bootstrap mechanism that launches the application. An app typically contains many functional modules.
Every Angular app has a *root module*, conventionally named `AppModule`, which provides the bootstrap mechanism that launches the application. An app typically contains many functional modules.
Like JavaScript modules, NgModules can import functionality from other NgModules, and allow their own functionality to be exported and used by other NgModules. For example, to use the router service in your app, you import the `Router` NgModule.
Organizing your code into distinct functional modules helps in managing development of complex applications, and in designing for reusability. In addition, this technique lets you take advantage of _lazy-loading_&mdash;that is, loading modules on demand&mdash;in order to minimize the amount of code that needs to be loaded at startup.
Organizing your code into distinct functional modules helps in managing development of complex applications, and in designing for reusability. In addition, this technique lets you take advantage of *lazy-loading*&mdash;that is, loading modules on demand&mdash;to minimize the amount of code that needs to be loaded at startup.
<div class="alert is-helpful">
@ -35,13 +35,13 @@ Organizing your code into distinct functional modules helps in managing developm
## Components
Every Angular application has at least one component, the *root component* that connects a component hierarchy with the page DOM. Each component defines a class that contains application data and logic, and is associated with an HTML *template* that defines a view to be displayed in a target environment.
Every Angular application has at least one component, the *root component* that connects a component hierarchy with the page document object model (DOM). Each component defines a class that contains application data and logic, and is associated with an HTML *template* that defines a view to be displayed in a target environment.
The `@Component` decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
The `@Component()` decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
<div class="alert is-helpful">
Decorators are functions that modify JavaScript classes. Angular defines a number of such decorators that attach specific kinds of metadata to classes, so that it knows what those classes mean and how they should work.
Decorators are functions that modify JavaScript classes. Angular defines a number of decorators that attach specific kinds of metadata to classes, so that the system knows what those classes mean and how they should work.
<a href="https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841#.x5c2ndtx0">Learn more about decorators on the web.</a>
@ -49,15 +49,18 @@ The `@Component` decorator identifies the class immediately below it as a compon
### Templates, directives, and data binding
A template combines HTML with Angular markup that can modify the HTML elements before they are displayed.
Template *directives* provide program logic, and *binding markup* connects your application data and the document object model (DOM).
A template combines HTML with Angular markup that can modify HTML elements before they are displayed.
Template *directives* provide program logic, and *binding markup* connects your application data and the DOM.
There are two types of data binding:
* *Event binding* lets your app respond to user input in the target environment by updating your application data.
* *Property binding* lets you interpolate values that are computed from your application data into the HTML.
Before a view is displayed, Angular evaluates the directives and resolves the binding syntax in the template to modify the HTML elements and the DOM, according to your program data and logic. Angular supports *two-way data binding*, meaning that changes in the DOM, such as user choices, can also be reflected back into your program data.
Before a view is displayed, Angular evaluates the directives and resolves the binding syntax in the template to modify the HTML elements and the DOM, according to your program data and logic. Angular supports *two-way data binding*, meaning that changes in the DOM, such as user choices, are also reflected in your program data.
Your templates can also use *pipes* to improve the user experience by transforming values for display. Use pipes to display, for example, dates and currency values in a way appropriate to the user's locale. Angular provides predefined pipes for common transformations, and you can also define your own.
Your templates can use *pipes* to improve the user experience by transforming values for display.
For example, use pipes to display dates and currency values that are appropriate for a user's locale.
Angular provides predefined pipes for common transformations, and you can also define your own pipes.
<div class="alert is-helpful">
@ -70,13 +73,13 @@ Your templates can also use *pipes* to improve the user experience by transformi
## Services and dependency injection
For data or logic that is not associated with a specific view, and that you want to share across components, you create a *service* class. A service class definition is immediately preceded by the `@Injectable` decorator. The decorator provides the metadata that allows your service to be *injected* into client components as a dependency.
For data or logic that isn't associated with a specific view, and that you want to share across components, you create a *service* class. A service class definition is immediately preceded by the `@Injectable()` decorator. The decorator provides the metadata that allows your service to be *injected* into client components as a dependency.
*Dependency injection* (or DI) lets you keep your component classes lean and efficient. They don't fetch data from the server, validate user input, or log directly to the console; they delegate such tasks to services.
*Dependency injection* (DI) lets you keep your component classes lean and efficient. They don't fetch data from the server, validate user input, or log directly to the console; they delegate such tasks to services.
<div class="alert is-helpful">
For a more detailed discusssion, see [Introduction to services and DI](guide/architecture-services).
For a more detailed discussion, see [Introduction to services and DI](guide/architecture-services).
</div>
@ -85,14 +88,16 @@ For data or logic that is not associated with a specific view, and that you want
The Angular `Router` NgModule provides a service that lets you define a navigation path among the different application states and view hierarchies in your app. It is modeled on the familiar browser navigation conventions:
* Enter a URL in the address bar and the browser navigates to a corresponding page.
* Click links on the page and the browser navigates to a new page.
* Click the browser's back and forward buttons and the browser navigates backward and forward through the history of pages you've seen.
The router maps URL-like paths to views instead of pages. When a user performs an action, such as clicking a link, that would load a new page in the browser, the router intercepts the browser's behavior, and shows or hides view hierarchies.
If the router determines that the current application state requires particular functionality, and the module that defines it has not been loaded, the router can _lazy-load_ the module on demand.
If the router determines that the current application state requires particular functionality, and the module that defines it hasn't been loaded, the router can *lazy-load* the module on demand.
The router interprets a link URL according to your app's view navigation rules and data state. You can navigate to new views when the user clicks a button, selects from a drop box, or in response to some other stimulus from any source. The Router logs activity in the browser's history journal, so the back and forward buttons work as well.
The router interprets a link URL according to your app's view navigation rules and data state. You can navigate to new views when the user clicks a button or selects from a drop box, or in response to some other stimulus from any source. The router logs activity in the browser's history, so the back and forward buttons work as well.
To define navigation rules, you associate *navigation paths* with your components. A path uses a URL-like syntax that integrates your program data, in much the same way that template syntax integrates your views with your program data. You can then apply program logic to choose which views to show or to hide, in response to user input and your own access rules.
@ -119,19 +124,26 @@ You've learned the basics about the main building blocks of an Angular applicati
Each of these subjects is introduced in more detail in the following pages.
* [Modules](guide/architecture-modules)
* [Components](guide/architecture-components)
* [Templates](guide/architecture-components#templates-and-views)
* [Metadata](guide/architecture-components#component-metadata)
* [Introduction to Modules](guide/architecture-modules)
* [Introduction to Components](guide/architecture-components)
* [Templates and views](guide/architecture-components#templates-and-views)
* [Component metadata](guide/architecture-components#component-metadata)
* [Data binding](guide/architecture-components#data-binding)
* [Directives](guide/architecture-components#directives)
* [Pipes](guide/architecture-components#pipes)
* [Services and dependency injection](guide/architecture-services)
* [Introduction to services and dependency injection](guide/architecture-services)
<div class="alert is-helpful">
Note that the code referenced on these pages is available as a <live-example></live-example>.
</div>
When you are familiar with these fundamental building blocks, you can explore them in more detail in the documentation. To learn about more tools and techniques that are available to help you build and deploy Angular applications, see [Next steps](guide/architecture-next-steps).
When you're familiar with these fundamental building blocks, you can explore them in more detail in the documentation. To learn about more tools and techniques that are available to help you build and deploy Angular applications, see [Next steps: tools and techniques](guide/architecture-next-steps).
</div>

View File

@ -19,7 +19,7 @@ If you use the CLI to generate an app, the default `AppModule` is as follows:
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
import { AppComponent } from './app.component';
@ -31,7 +31,7 @@ import { AppComponent } from './app.component';
imports: [
BrowserModule,
FormsModule,
HttpModule
HttpClientModule
],
providers: [],
bootstrap: [AppComponent]
@ -138,7 +138,7 @@ It tells Angular about other NgModules that this particular module needs to func
This list of modules are those that export components, directives, or pipes
that the component templates in this module reference. In this case, the component is
`AppComponent`, which references components, directives, or pipes in `BrowserModule`,
`FormsModule`, or `HttpModule`.
`FormsModule`, or `HttpClientModule`.
A component template can reference another component, directive,
or pipe when the referenced class is declared in this module or
the class was imported from another module.

View File

@ -44,6 +44,9 @@ is available to <code>declarations</code> of this module.</p>
<td><p>List of dependency injection providers visible both to the contents of this module and to importers of this module.</p>
</td>
</tr><tr>
<td><code><b>entryComponents:</b> [SomeComponent, OtherComponent]</code></td>
<td><p>List of components not referenced in any reachable template, for example dynamically created from code.</p></td>
</tr><tr>
<td><code><b>bootstrap:</b> [MyAppComponent]</code></td>
<td><p>List of components to bootstrap when this module is bootstrapped.</p>
</td>

View File

@ -89,47 +89,51 @@ promise.then(() => {
The following code snippets illustrate how the same kind of operation is defined using observables and promises.
<table>
<tr>
<th>Operation</th>
<th>Observable</th>
<th>Promise</th>
</tr>
<tr>
<td>Creation</td>
<td>
<pre>new Observable((observer) => {
observer.next(123);
});</pre>
</td>
<td>
<pre>new Promise((resolve, reject) => {
resolve(123);
});</pre>
</td>
</tr>
<tr>
<td>Transform</td>
<td><pre>obs.map((value) => value * 2 );</pre></td>
<td><pre>promise.then((value) => value * 2);</pre></td>
</tr>
<tr>
<td>Subscribe</td>
<td>
<pre>sub = obs.subscribe((value) => {
console.log(value)
});</pre>
</td>
<td>
<pre>promise.then((value) => {
console.log(value);
});</pre>
</td>
</tr>
<tr>
<td>Unsubscribe</td>
<td><pre>sub.unsubscribe();</pre></td>
<td>Implied by promise resolution.</td>
</tr>
<thead>
<tr>
<th>Operation</th>
<th>Observable</th>
<th>Promise</th>
</tr>
</thead>
<tbody>
<tr>
<td>Creation</td>
<td>
<pre>new Observable((observer) => {
observer.next(123);
});</pre>
</td>
<td>
<pre>new Promise((resolve, reject) => {
resolve(123);
});</pre>
</td>
</tr>
<tr>
<td>Transform</td>
<td><pre>obs.map((value) => value * 2 );</pre></td>
<td><pre>promise.then((value) => value * 2);</pre></td>
</tr>
<tr>
<td>Subscribe</td>
<td>
<pre>sub = obs.subscribe((value) => {
console.log(value)
});</pre>
</td>
<td>
<pre>promise.then((value) => {
console.log(value);
});</pre>
</td>
</tr>
<tr>
<td>Unsubscribe</td>
<td><pre>sub.unsubscribe();</pre></td>
<td>Implied by promise resolution.</td>
</tr>
</tbody>
</table>
## Observables compared to events API

View File

@ -410,7 +410,7 @@ Here you see the new and the old implementation side-by-side:
Tree shaking is the ability to remove code that is not referenced in an application from the final bundle. Tree-shakable providers give Angular the ability to remove services that are not used in your application from the final output. This significantly reduces the size of your bundles.
Ideally, if an application is not injecting a service, it should not be included in the final output. However, it turns out that the Angular compiler cannot identify at build time if the service will be required or not. Because it's always possible to inject a service directly using `injector.get(Service)`, Angular cannot identify all of the places in your code where this injection could happen, so it has no choice but to include the service in the injector regardless. Thus, services provided in modules are not tree-shakeable.
Ideally, if an application is not injecting a service, it should not be included in the final output. However, it turns out that the Angular compiler cannot identify at build time if the service will be required or not. Because it's always possible to inject a service directly using `injector.get(Service)`, Angular cannot identify all of the places in your code where this injection could happen, so it has no choice but to include the service in the injector regardless. Thus, services provided in modules are not tree-shakable.
Let us consider an example of non-tree-shakable providers in Angular.
@ -424,13 +424,13 @@ This module can then be imported into your application module, to make the servi
When `ngc` runs, it compiles AppModule into a module factory, which contains definitions for all the providers declared in all the modules it includes. At runtime, this factory becomes an injector that instantiates these services.
Tree-shaking doesn't work in the method above because Angular cannot decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakeable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself.
Tree-shaking doesn't work in the method above because Angular cannot decide to exclude one chunk of code (the provider definition for the service within the module factory) based on whether another chunk of code (the service class) is used. To make services tree-shakable, the information about how to construct an instance of the service (the provider definition) needs to be a part of the service class itself.
#### Creating tree-shakable providers
To create providers that are tree-shakable, the information that used to be specified in the module should be specified in the `@Injectable` decorator on the service itself.
The following example shows the tree-shakeable equivalent to the `ServiceModule` example above:
The following example shows the tree-shakable equivalent to the `ServiceModule` example above:
<code-example path="dependency-injection/src/app/tree-shaking/service.ts" title="src/app/tree-shaking/service.ts" linenums="false"> </code-example>
@ -690,9 +690,9 @@ If the factory function needs access to other DI tokens, it can use the inject f
<code-example>
const TOKEN =
new InjectionToken('tree-shakeable token',
new InjectionToken('tree-shakable token',
{ providedIn: 'root', factory: () =>
new AppConfig(inject(Parameter1), inject(Paremeter2)), });
new AppConfig(inject(Parameter1), inject(Parameter2)), });
</code-example>
{@a optional}

View File

@ -3,25 +3,25 @@
_Angular elements_ are Angular components packaged as _custom elements_, a web standard for defining new HTML elements in a framework-agnostic way.
[Custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) are a Web Platform feature currently supported by Chrome, Opera, and Safari, and available in other browsers through polyfills (see [Browser Support](#browser-support)).
A custom element extends HTML by allowing you to define a tag whose content is created and controlled by JavaScript code.
A custom element extends HTML by allowing you to define a tag whose content is created and controlled by JavaScript code.
The browser maintains a `CustomElementRegistry` of defined custom elements (also called Web Components), which maps an instantiable JavaScript class to an HTML tag.
The `@angular/elements` package exports a `createCustomElement()` API that provides a bridge from Angular's component interface and change detection functionality to the built-in DOM API.
The `@angular/elements` package exports a `createCustomElement()` API that provides a bridge from Angular's component interface and change detection functionality to the built-in DOM API.
Transforming a component to a custom element makes all of the required Angular infrastructure available to the browser.
Creating a custom element is simple and straightforward, and automatically connects your component-defined view with change detection and data binding, mapping Angular functionality to the corresponding native HTML equivalents.
Transforming a component to a custom element makes all of the required Angular infrastructure available to the browser.
Creating a custom element is simple and straightforward, and automatically connects your component-defined view with change detection and data binding, mapping Angular functionality to the corresponding native HTML equivalents.
<div class="alert is-helpful">
We are working on custom elements that can be used by web apps built on other frameworks.
A minimal, self-contained version of the Angular framework will be injected as a service to support the component's change-detection and data-binding functionality.
We are working on custom elements that can be used by web apps built on other frameworks.
A minimal, self-contained version of the Angular framework will be injected as a service to support the component's change-detection and data-binding functionality.
For more about the direction of development, check out this [video presentation](https://www.youtube.com/watch?v=Z1gLFPLVJjY&t=4s).
</div>
## Using custom elements
Custom elements bootstrap themselves - they start automatically when they are added to the DOM, and are automatically destroyed when removed from the DOM. Once a custom element is added to the DOM for any page, it looks and behaves like any other HTML element, and does not require any special knowledge of Angular terms or usage conventions.
Custom elements bootstrap themselves - they start automatically when they are added to the DOM, and are automatically destroyed when removed from the DOM. Once a custom element is added to the DOM for any page, it looks and behaves like any other HTML element, and does not require any special knowledge of Angular terms or usage conventions.
- <b>Easy dynamic content in an Angular app</b>
@ -33,14 +33,14 @@ Custom elements bootstrap themselves - they start automatically when they are ad
### How it works
Use the `createCustomElement()` function to convert a component into a class that can be registered with the browser as a custom element.
After you register your configured class with the browser's custom-element registry, you can use the new element just like a built-in HTML element in content that you add directly into the DOM:
Use the `createCustomElement()` function to convert a component into a class that can be registered with the browser as a custom element.
After you register your configured class with the browser's custom-element registry, you can use the new element just like a built-in HTML element in content that you add directly into the DOM:
```
<my-popup message="Use Angular!"></my-popup>
```
When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM. The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data. Input properties in the component correspond to input attributes for the element.
When your custom element is placed on a page, the browser creates an instance of the registered class and adds it to the DOM. The content is provided by the component's template, which uses Angular template syntax, and is rendered using the component and DOM data. Input properties in the component correspond to input attributes for the element.
<figure>
@ -52,25 +52,25 @@ When your custom element is placed on a page, the browser creates an instance of
## Transforming components to custom elements
Angular provides the `createCustomElement()` function for converting an Angular component,
together with its dependencies, to a custom element. The function collects the component's
observable properties, along with the Angular functionality the browser needs to
create and destroy instances, and to detect and respond to changes.
Angular provides the `createCustomElement()` function for converting an Angular component,
together with its dependencies, to a custom element. The function collects the component's
observable properties, along with the Angular functionality the browser needs to
create and destroy instances, and to detect and respond to changes.
The conversion process implements the `NgElementConstructor` interface, and creates a
constructor class that is configured to produce a self-bootstrapping instance of your component.
The conversion process implements the `NgElementConstructor` interface, and creates a
constructor class that is configured to produce a self-bootstrapping instance of your component.
Use a JavaScript function, `customElements.define()`, to register the configured constructor
and its associated custom-element tag with the browser's `CustomElementRegistry`.
Use a JavaScript function, `customElements.define()`, to register the configured constructor
and its associated custom-element tag with the browser's `CustomElementRegistry`.
When the browser encounters the tag for the registered element, it uses the constructor to create a custom-element instance.
<figure>
<img src="generated/images/guide/elements/createElement.png" alt="Transform a component to a custom element" class="left">
<img src="generated/images/guide/elements/createElement.png" alt="Transform a component to a custom element" class="left">
</figure>
### Mapping
### Mapping
A custom element _hosts_ an Angular component, providing a bridge between the data and logic defined in the component and standard DOM APIs. Component properties and logic maps directly into HTML attributes and the browser's event system.
@ -80,13 +80,13 @@ A custom element _hosts_ an Angular component, providing a bridge between the da
For more information, see Web Component documentation for [Creating custom events](https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events#Creating_custom_events).
{@a browser-support}
## Browser support for custom elements
The recently-developed [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) Web Platform feature is currently supported natively in a number of browsers. Support is pending or planned in other browsers.
The recently-developed [custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements) Web Platform feature is currently supported natively in a number of browsers. Support is pending or planned in other browsers.
<table>
<tr>
@ -111,7 +111,7 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
</tr>
<tr>
<td>Edge</td>
<td>Working on an implementation. <br>
<td>Working on an implementation. <br>
</td>
</tr>
@ -120,7 +120,7 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's `tsconfig.json`. As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
Use the [Angular CLI](https://cli.angular.io/) to automatically set up your project with the correct polyfill: `ng add @angular/elements --name=*your_project_name*`.
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
- For more information about Angular browser support, see [Browser Support](guide/browser-support).
@ -131,12 +131,12 @@ Previously, when you wanted to add a component to an app at runtime, you had to
Using an Angular custom element makes the process much simpler and more transparent, by providing all of the infrastructure and framework automatically&mdash;all you have to do is define the kind of event handling you want. (You do still have to exclude the component from compilation, if you are not going to use it in your app.)
The Popup Service example app defines a component that you can either load dynamically or convert to a custom element.
The Popup Service example app (shown below) defines a component that you can either load dynamically or convert to a custom element.
- `popup.component.ts` defines a simple pop-up element that displays an input message, with some animation and styling.
- `popup.component.ts` defines a simple pop-up element that displays an input message, with some animation and styling.
- `popup.service.ts` creates an injectable service that provides two different ways to invoke the PopupComponent; as a dynamic component, or as a custom element. Notice how much more setup is required for the dynamic-loading method.
- `app.module.ts` adds the PopupComponent in the module's `entryComponents` list, to exclude it from compilation and avoid startup warnings or errors.
- `app.component.ts` defines the app's root component, which uses the PopupService to add the pop-up to the DOM at run time. When the app runs, the root component's constructor converts PopupComponent to a custom element.
- `app.component.ts` defines the app's root component, which uses the PopupService to add the pop-up to the DOM at run time. When the app runs, the root component's constructor converts PopupComponent to a custom element.
For comparison, the demo shows both methods. One button adds the popup using the dynamic-loading method, and the other uses the custom element. You can see that the result is the same; only the preparation is different.
@ -158,3 +158,59 @@ For comparison, the demo shows both methods. One button adds the popup using the
</code-pane>
</code-tabs>
<!--
StackBlitz transpiles code to ES5. The live example will not work without a polyfill.
Only offer a `.zip` to download for now.
-->
You can download the full code for the example <live-example downloadOnly>here</live-example>.
## Typings for custom elements
Generic DOM APIs, such as `document.createElement()` or `document.querySelector()`, return an element type that is appropriate for the specified arguments. For example, calling `document.createElement('a')` will return an `HTMLAnchorElement`, which TypeScript knows has an `href` property. Similarly, `document.createElement('div')` will return an `HTMLDivElement`, which TypeScript knows has no `href` property.
When called with unknown elements, such as a custom element name (`popup-element` in our example), the methods will return a generic type, such as `HTMLELement`, since TypeScript can't infer the correct type of the returned element.
Custom elements created with Angular extend `NgElement` (which in turn extends `HTMLElement`). Additionally, these custom elements will have a property for each input of the corresponding component. For example, our `popup-element` will have a `message` property of type `string`.
There are a few options if you want to get correct types for your custom elements. Let's assume you create a `my-dialog` custom element based on the following component:
```ts
@Component(...)
class MyDialog {
@Input() content: string;
}
```
The most straight forward way to get accurate typings is to cast the return value of the relevant DOM methods to the correct type. For that, you can use the `NgElement` and `WithProperties` types (both exported from `@angular/elements`):
```ts
const aDialog = document.createElement('my-dialog') as NgElement & WithProperties<{content: string}>;
aDialog.content = 'Hello, world!';
aDialog.content = 123; // <-- ERROR: TypeScript knows this should be a string.
aDialog.body = 'News'; // <-- ERROR: TypeScript knows there is no `body` property on `aDialog`.
```
This is a good way to quickly get TypeScript features, such as type checking and autocomplete support, for you custom element. But it can get cumbersome if you need it in several places, because you have to cast the return type on every occurrence.
An alternative way, that only requires defining each custom element's type once, is augmenting the `HTMLELementTagNameMap`, which TypeScript uses to infer the type of a returned element based on its tag name (for DOM methods such as `document.createElement()`, `document.querySelector()`, etc.):
```ts
declare global {
interface HTMLElementTagNameMap {
'my-dialog': NgElement & WithProperties<{content: string}>;
'my-other-element': NgElement & WithProperties<{foo: 'bar'}>;
...
}
}
```
Now, TypeScript can infer the correct type the same way it does for built-in elements:
```ts
document.createElement('div') //--> HTMLDivElement (built-in element)
document.querySelector('foo') //--> Element (unknown element)
document.createElement('my-dialog') //--> NgElement & WithProperties<{content: string}> (custom element)
document.querySelector('my-other-element') //--> NgElement & WithProperties<{foo: 'bar'}> (custom element)
```

View File

@ -36,7 +36,7 @@ The following is an example of specifying a bootstrapped component,
imports: [
BrowserModule,
FormsModule,
HttpModule,
HttpClientModule,
AppRoutingModule
],
providers: [],

View File

@ -6,12 +6,12 @@
Improve overall data quality by validating user input for accuracy and completeness.
This page shows how to validate user input in the UI and display useful validation messages
using both reactive and template-driven forms. It assumes some basic knowledge of the two
using both reactive and template-driven forms. It assumes some basic knowledge of the two
forms modules.
<div class="alert is-helpful">
If you're new to forms, start by reviewing the [Forms](guide/forms) and
If you're new to forms, start by reviewing the [Forms](guide/forms) and
[Reactive Forms](guide/reactive-forms) guides.
</div>
@ -19,11 +19,11 @@ If you're new to forms, start by reviewing the [Forms](guide/forms) and
## Template-driven validation
To add validation to a template-driven form, you add the same validation attributes as you
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
To add validation to a template-driven form, you add the same validation attributes as you
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
Angular uses directives to match these attributes with validator functions in the framework.
Every time the value of a form control changes, Angular runs validation and generates
Every time the value of a form control changes, Angular runs validation and generates
either a list of validation errors, which results in an INVALID status, or null, which results in a VALID status.
You can then inspect the control's state by exporting `ngModel` to a local template variable.
@ -36,12 +36,12 @@ The following example exports `NgModel` into a variable called `name`:
Note the following:
* The `<input>` element carries the HTML validation attributes: `required` and `minlength`. It
also carries a custom validator directive, `forbiddenName`. For more
* The `<input>` element carries the HTML validation attributes: `required` and `minlength`. It
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 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)
* `#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.
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
@ -49,7 +49,7 @@ but only if the `name` is invalid and the control is either `dirty` or `touched`
* Each nested `<div>` can present a custom message for one of the possible validation errors.
There are messages for `required`, `minlength`, and `forbiddenName`.
<div class="alert is-helpful">
@ -58,8 +58,8 @@ There are messages for `required`, `minlength`, and `forbiddenName`.
#### Why check _dirty_ and _touched_?
You may not want your application to display errors before the user has a chance to edit the form.
The checks for `dirty` and `touched` prevent errors from showing until the user
does one of two things: changes the value,
The checks for `dirty` and `touched` prevent errors from showing until the user
does one of two things: changes the value,
turning the control dirty; or blurs the form control element, setting the control to touched.
</div>
@ -70,24 +70,24 @@ In a reactive form, the source of truth is the component class. Instead of addin
### Validator functions
There are two types of validator functions: sync validators and async validators.
There are two types of validator functions: sync validators and async validators.
* **Sync validators**: functions that take a control instance and immediately return either a set of validation errors or `null`. You can pass these in as the second argument when you instantiate a `FormControl`.
* **Async validators**: functions that take a control instance and return a Promise
or Observable that later emits a set of validation errors or `null`. You can
pass these in as the third argument when you instantiate a `FormControl`.
* **Async validators**: functions that take a control instance and return a Promise
or Observable that later emits a set of validation errors or `null`. You can
pass these in as the third argument when you instantiate a `FormControl`.
Note: for performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.
### Built-in validators
You can choose to [write your own validator functions](guide/form-validation#custom-validators), or you can use some of
Angular's built-in validators.
You can choose to [write your own validator functions](guide/form-validation#custom-validators), or you can use some of
Angular's built-in validators.
The same built-in validators that are available as attributes in template-driven forms, such as `required` and `minlength`, are all available to use as functions from the `Validators` class. For a full list of built-in validators, see the [Validators](api/forms/Validators) API reference.
To update the hero form to be a reactive form, you can use some of the same
To update the hero form to be a reactive form, you can use some of the same
built-in validators&mdash;this time, in function form. See below:
{@a reactive-component-class}
@ -98,31 +98,31 @@ built-in validators&mdash;this time, in function form. See below:
Note that:
* The name control sets up two built-in validators&mdash;`Validators.required` and `Validators.minLength(4)`&mdash;and one custom validator, `forbiddenNameValidator`. For more details see the [Custom validators](guide/form-validation#custom-validators) section in this guide.
* As these validators are all sync validators, you pass them in as the second argument.
* As these validators are all sync validators, you pass them in as the second argument.
* Support multiple validators by passing the functions in as an array.
* This example adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthands
* This example adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthands
for the template.
If you look at the template for the name input again, it is fairly similar to the template-driven example.
If you look at the template for the name input again, it is fairly similar to the template-driven example.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" title="reactive/hero-form-reactive.component.html (name with error msg)" linenums="false">
</code-example>
Key takeaways:
* The form no longer exports any directives, and instead uses the `name` getter defined in
* The form no longer exports any directives, and instead uses the `name` getter defined in
the component class.
* The `required` attribute is still present. While it's not necessary for validation purposes,
* The `required` attribute is still present. While it's not necessary for validation purposes,
you may want to keep it in your template for CSS styling or accessibility reasons.
## Custom validators
Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
Consider the `forbiddenNameValidator` function from previous
[examples](guide/form-validation#reactive-component-class) in
[examples](guide/form-validation#reactive-component-class) in
this guide. Here's what the definition of that function looks like:
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" title="shared/forbidden-name.directive.ts (forbiddenNameValidator)" linenums="false">
@ -145,7 +145,7 @@ at which point the form uses the last value emitted for validation.
### Adding to reactive forms
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
to the `FormControl`.
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
@ -153,7 +153,7 @@ to the `FormControl`.
### Adding to template-driven forms
In template-driven forms, you don't have direct access to the `FormControl` instance, so you can't pass the
In template-driven forms, you don't have direct access to the `FormControl` instance, so you can't pass the
validator in like you can for reactive forms. Instead, you need to add a directive to the template.
The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `forbiddenNameValidator`.
@ -164,8 +164,8 @@ with the `NG_VALIDATORS` provider, a provider with an extensible collection of v
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" title="shared/forbidden-name.directive.ts (providers)" linenums="false">
</code-example>
The directive class then implements the `Validator` interface, so that it can easily integrate
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
The directive class then implements the `Validator` interface, so that it can easily integrate
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
comes together:
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive" title="shared/forbidden-name.directive.ts (directive)">
@ -201,14 +201,14 @@ Like in AngularJS, Angular automatically mirrors many control properties onto th
* `.ng-untouched`
* `.ng-touched`
The hero form uses the `.ng-valid` and `.ng-invalid` classes to
The hero form uses the `.ng-valid` and `.ng-invalid` classes to
set the color of each form control's border.
<code-example path="form-validation/src/assets/forms.css" title="forms.css (status classes)">
</code-example>
## Cross field validation
## Cross field validation
This section shows how to perform cross field validation. It assumes some basic knowledge of creating custom validators.
<div class="alert is-helpful">
@ -217,7 +217,7 @@ If you haven't created custom validators before, start by reviewing the [custom
</div>
In the following section, we will make sure that our heroes do not reveal their true identities by filling out the Hero Form. We will do that by validating that the hero names and alter egos do not match.
In the following section, we will make sure that our heroes do not reveal their true identities by filling out the Hero Form. We will do that by validating that the hero names and alter egos do not match.
### Adding to reactive forms
@ -250,7 +250,7 @@ The validator code is as follows:
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
First we retrieve the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method. Then we simply compare the values of the `name` and `alterEgo` controls.
First we retrieve the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method. Then we simply compare the values of the `name` and `alterEgo` controls.
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
@ -259,7 +259,7 @@ Next, to provide better user experience, we show an appropriate error message wh
</code-example>
Note that we check if:
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
### Adding to template driven forms
@ -277,11 +277,78 @@ To provide better user experience, we show an appropriate error message when the
</code-example>
Note that we check if:
- the form has the cross validation error returned by the `identityRevealed` validator,
- the form has the cross validation error returned by the `identityRevealed` validator,
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
This completes the cross validation example. We managed to:
- validate the form based on the values of two sibling controls,
- validate the form based on the values of two sibling controls,
- show a descriptive error message after the user interacted with the form and the validation failed.
## Async Validation
This section shows how to create asynchronous validators. It assumes some basic knowledge of creating [custom validators](guide/form-validation#custom-validators).
### The Basics
Just like synchronous validators have the `ValidatorFn` and `Validator` interfaces, asynchronous validators have their own counterparts: `AsyncValidatorFn` and `AsyncValidator`.
They are very similar with the only difference being:
* They must return a Promise or an Observable,
* The observable returned must be finite, meaning it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as `first`, `last`, `take`, or `takeUntil`.
It is important to note that the asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful. This check allows forms to avoid potentially expensive async validation processes such as an HTTP request if more basic validation methods fail.
After asynchronous validation begins, the form control enters a `pending` state. You can inspect the control's `pending` property and use it to give visual feedback about the ongoing validation.
A common UI pattern is to show a spinner while the async validation is being performed. The following example presents how to achieve this with template-driven forms:
```html
<input [(ngModel)}="name" #model="ngModel" appSomeAsyncValidator>
<app-spinner *ngIf="model.pending"></app-spinner>
```
### Implementing Custom Async Validator
In the following section, validation is performed asynchronously to ensure that our heroes pick an alter ego that is not already taken. New heroes are constantly enlisting and old heroes are leaving the service. That means that we do not have the list of available alter egos ahead of time.
To validate the potential alter ego, we need to consult a central database of all currently enlisted heroes. The process is asynchronous, so we need a special validator for that.
Let's start by creating the validator class.
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator" linenums="false"></code-example>
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:
```typescript
interface HeroesService {
isAlterEgoTaken: (alterEgo: string) => Observable<boolean>;
}
```
In a real world application, the `HeroesService` is responsible for making an HTTP request to the hero database to check if the alter ego is available. From the validator's point of view, the actual implementation of the service is not important, so we can just code against the `HeroesService` interface.
As the validation begins, the `UniqueAlterEgoValidator` delegates to the `HeroesService` `isAlterEgoTaken()` method with the current control value. At this point the control is marked as `pending` and remains in this state until the observable chain returned from the `validate()` method completes.
The `isAlterEgoTaken()` method dispatches an HTTP request that checks if the alter ego is available, and returns `Observable<boolean>` as the result. We pipe the response through the `map` operator and transform it into a validation result. As always, we return `null` if the form is valid, and `ValidationErrors` if it is not. We make sure to handle any potential errors with the `catchError` operator.
Here we decided that `isAlterEgoTaken()` error is treated as a successful validation, because failure to make a validation request does not necessarily mean that the alter ego is invalid. You could handle the error differently and return the `ValidationError` object instead.
After some time passes, the observable chain completes and the async validation is done. The `pending` flag is set to `false`, and the form validity is updated.
### Note on performance
By default, all validators are run after every form value change. With synchronous validators, this will not likely have a noticeable impact on application performance. However, it's common for async validators to perform some kind of HTTP request to validate the control. Dispatching an HTTP request after every keystroke could put a strain on the backend API, and should be avoided if possible.
We can delay updating the form validity by changing the `updateOn` property from `change` (default) to `submit` or `blur`.
With template-driven forms:
```html
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
```
With reactive forms:
```typescript
new FormControl('', {updateOn: 'blur'});
```
**You can run the <live-example></live-example> to see the complete reactive and template-driven example code.**

View File

@ -73,11 +73,11 @@ Typically you dont interact with the compiler directly; rather, you use it in
**@angular/forms**: support for both [template-driven](guide/forms) and [reactive forms](guide/reactive-forms).
**@angular/http**: Angular's old, soon-to-be-deprecated, HTTP client.
**@angular/http**: Angular's old, deprecated, HTTP client.
**@angular/platform-browser**: Everything DOM and browser related, especially
the pieces that help render into the DOM.
This package also includes the `bootstrapStatic()` method
This package also includes the `bootstrapModuleFactory()` method
for bootstrapping applications for production builds that pre-compile with [AOT](guide/aot-compiler).
**@angular/platform-browser-dynamic**: Includes [Providers](api/core/Provider)

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -653,5 +653,15 @@
"twitter": "elanathellama",
"bio": "Elana is a Developer Relations intern on the Angular team at Google. She is working on migration paths from AngularJS to Angular and would love to chat about your experience with upgrading.",
"group": "Angular"
},
"kevinyang": {
"name": "Kevin Yang",
"picture": "kevinyang.jpg",
"twitter": "chgc",
"website": "https://blog.kevinyang.net/",
"bio":
"Kevin is a Angular Taiwan, Angular Girls Taiwan community organzier. He loves sharing knowledge with other developers through blogging, speaking, workshops.",
"group": "GDE"
}
}

View File

@ -13,12 +13,6 @@
</tr>
</thead>
<tbody>
<!-- ReactiveConf -->
<tr>
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
<td>Bratislava, Slovakia</td>
<td>October 25, 2017</td>
</tr>
<!-- AngularConnect-->
<tr>
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
@ -61,6 +55,12 @@
<td>Melbourne, Australia</td>
<td>Jun 22, 2018</td>
</tr>
<!-- ReactiveConf -->
<tr>
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
<td>Bratislava, Slovakia</td>
<td>October 29-31, 2018</td>
</tr>
<!-- AngularConnect-->
<tr>
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>

View File

@ -16,6 +16,12 @@
"rev": true,
"title": "Angular Conferences and Angular Camps in Moscow, Russia.",
"url": "https://angular-ru.github.io/"
},
"made-with-angular": {
"desc": "A showcase of web apps built with Angular.",
"rev": true,
"title": "Made with Angular",
"url": "https://www.madewithangular.com/"
}
}
},
@ -181,7 +187,7 @@
"rev": true,
"title": "Amexio Canvas Web Based Drag and Drop IDE by MetaMagic",
"url": "https://amexio.tech/"
}
}
}
},
"Tooling": {
@ -260,6 +266,13 @@
"title": "Nx",
"logo": "https://nrwl.io/assets/nx-logo.png",
"url": "https://nrwl.io/nx"
},
"uijar": {
"desc": "A drop in module to automatically create a living style guide based on the test you write for your components.",
"logo": "",
"rev": true,
"title": "UI-jar - Test Driven Style Guide Development",
"url": "https://github.com/ui-jar/ui-jar"
}
}
},
@ -317,7 +330,7 @@
"url": "http://www.primefaces.org/primeng/"
},
"a3b": {
"desc": "One of the first major UI frameworks to support Angular",
"desc": "A professional grade library of Angular UI components written in TypeScript that includes our Data Grid, TreeView, Charts, Editors, DropDowns, DatePickers, and many more. Features include support for AOT compilation, Tree Shaking for high-performance, localization, and accessibility.",
"logo": "",
"rev": true,
"title": "Kendo UI",
@ -421,8 +434,8 @@
"-KLIzGEp8Mh5W-FkiQnL": {
"desc": "Your quick, no-nonsense guide to building real-world apps with Angular",
"rev": true,
"title": "Learning Angular",
"url": "https://www.packtpub.com/web-development/learning-angular-2"
"title": "Learning Angular - Second Edition",
"url": "https://www.packtpub.com/web-development/learning-angular-second-edition"
},
"3ab": {
"desc": "More than 15 books from O'Reilly about Angular",
@ -503,6 +516,13 @@
"rev": true,
"title": "The Angular Guide by Wishtack (Français)",
"url": "https://guide-angular.wishtack.io/"
},
"ab5": {
"desc": "How to build Angular applications using NGRX",
"logo": "",
"rev": true,
"title": "Architecting Angular Applications with NGRX",
"url": "https://www.packtpub.com/web-development/architecting-angular-applications-redux"
}
}
},
@ -629,12 +649,24 @@
"rev": true,
"title": "AngularFirebase.com",
"url": "https://angularfirebase.com/"
},
"loiane-angulartraining": {
"desc": "Free Angular course in Portuguese.",
"rev": true,
"title": "Loiane Training (Português)",
"url": "https://loiane.training/course/angular/"
}
}
},
"Workshops & Onsite Training": {
"order": 2,
"resources": {
"-acceleb": {
"desc": "Customized, Instructor-Led Angular Training",
"rev": true,
"title": "Accelebrate",
"url": "https://www.accelebrate.com/angular-training"
},
"-KLIBoFWStce29UCwkvY": {
"desc": "Private Angular Training and Mentoring",
"rev": true,
@ -736,6 +768,12 @@
"rev": true,
"title": "Angular.Schule (German)",
"url": "https://angular.schule/"
},
"strbrw": {
"desc": "Angular and RxJS trainings, Code Reviews and consultancy. We help software engineers all over the world to create better web-applications...",
"rev": true,
"title": "StrongBrew",
"url": "https://strongbrew.io/"
}
}
}

View File

@ -89,15 +89,8 @@ Registering the provider in the `@Injectable` metadata also allows Angular to op
<div class="alert is-helpful">
If you need to, you can register providers at different levels:
in the `HeroesComponent`, in the `AppComponent`, in the `AppModule`.
For instance, you could have told the CLI to provide the service at the module level automatically by appending `--module=app`.
<code-example language="sh" class="code-shell">
ng generate service hero --module=app
</code-example>
To learn more about providers and injectors, see the [Dependency Injection guide](guide/dependency-injection).
To learn more about providers, see the [Providers section](guide/providers).
To learn more about injectors, see the [Dependency Injection guide](guide/dependency-injection).
</div>

View File

@ -250,7 +250,7 @@ There are three significant differences from `getHeroes()`.
## Update heroes
Editing a hero's name in the _hero detail_ view.
Edit a hero's name in the _hero detail_ view.
As you type, the hero name updates the heading at the top of the page.
But when you click the "go back button", the changes are lost.
@ -291,11 +291,11 @@ That header is in the `httpOptions` constant defined in the `HeroService`.
<code-example
path="toh-pt6/src/app/hero.service.ts"
region="http-options">
region="http-options"
title="src/app/hero.service.ts">
</code-example>
Refresh the browser, change a hero name, save your change,
and click the "go back" button.
Refresh the browser, change a hero name and save your change. Navigating to the previous view is implemented in the `save()` method defined in `HeroDetailComponent`.
The hero now appears in the list with the changed name.
## Add a new hero
@ -440,7 +440,7 @@ Create a `HeroSearchComponent` with the CLI.
ng generate component hero-search
</code-example>
The CLI generates the three `HeroSearchComponent` and adds the component to the `AppModule' declarations
The CLI generates the three `HeroSearchComponent` files and adds the component to the `AppModule` declarations
Replace the generated `HeroSearchComponent` _template_ with a text box and a list of matching search results like this.

View File

@ -7,14 +7,15 @@
<mat-toolbar color="primary" class="app-toolbar no-print" [class.transitioning]="isTransitioning">
<mat-toolbar-row class="notification-container">
<aio-notification
icon="insert_comment"
iconLabel="Announcement"
buttonText="Learn More"
actionUrl="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4"
notificationId="angular-v6-announcement"
expirationDate="2018-07-01"
[dismissOnContentClick]="true"
(dismissed)="notificationDismissed()">
Version 6 of Angular Now Available!
<a href="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4">
<mat-icon class="icon" svgIcon="insert_comment" aria-label="Announcement"></mat-icon>
<span class="message">Version 6 of Angular Now Available!</span>
<span class="action-button">Learn More</span>
</a>
</aio-notification>
</mat-toolbar-row>
<mat-toolbar-row>

View File

@ -115,7 +115,6 @@ describe('ApiListComponent', () => {
component.filteredSections.subscribe(filtered => {
filtered = filtered.filter(s => s.items);
console.log(filtered);
expect(filtered.length).toBe(1, 'sections');
expect(filtered[0].name).toBe(section, 'section name');
const items = filtered[0].items!;

View File

@ -1,8 +1,6 @@
<a href="{{actionUrl}}" class="content" (click)="dismiss()">
<mat-icon class="icon" [svgIcon]="icon" [attr.aria-label]="iconLabel"></mat-icon>
<span class="message"><ng-content></ng-content></span>
<span class="action-button">{{buttonText}}</span>
</a>
<span class="content" (click)="contentClick()">
<ng-content></ng-content>
</span>
<button mat-icon-button class="close-button" aria-label="Close" (click)="dismiss()">
<mat-icon svgIcon="close" aria-label="Dismiss notification"></mat-icon>

View File

@ -30,37 +30,49 @@ describe('NotificationComponent', () => {
fixture.detectChanges();
}
it('should display the message', () => {
configTestingModule();
createComponent();
expect(fixture.nativeElement.innerHTML).toContain('Help Angular by taking a <strong>1 minute survey</strong>!');
describe('content projection', () => {
it('should display the message text', () => {
configTestingModule();
createComponent();
expect(fixture.nativeElement.innerHTML).toContain('Version 6 of Angular Now Available!');
});
it('should render HTML elements', () => {
configTestingModule();
createComponent();
const button = fixture.debugElement.query(By.css('.action-button'));
expect(button.nativeElement.textContent).toEqual('Learn More');
});
it('should process Angular directives', () => {
configTestingModule();
createComponent();
const badSpans = fixture.debugElement.queryAll(By.css('.bad'));
expect(badSpans.length).toEqual(0);
});
});
it('should display an icon', () => {
configTestingModule();
createComponent();
const iconElement = fixture.debugElement.query(By.css('.icon'));
expect(iconElement.properties['svgIcon']).toEqual('insert_comment');
expect(iconElement.attributes['aria-label']).toEqual('Survey');
});
it('should display a button', () => {
configTestingModule();
createComponent();
const button = fixture.debugElement.query(By.css('.action-button'));
expect(button.nativeElement.textContent).toEqual('Go to survey');
});
it('should call dismiss when the message link is clicked', () => {
it('should call dismiss() when the message link is clicked, if dismissOnContentClick is true', () => {
configTestingModule();
createComponent();
spyOn(component, 'dismiss');
fixture.debugElement.query(By.css('a')).triggerEventHandler('click', null);
fixture.detectChanges();
component.dismissOnContentClick = true;
const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement;
message.click();
expect(component.dismiss).toHaveBeenCalled();
});
it('should call dismiss when the close button is clicked', () => {
it('should not call dismiss() when the message link is clicked, if dismissOnContentClick is false', () => {
configTestingModule();
createComponent();
spyOn(component, 'dismiss');
component.dismissOnContentClick = false;
const message: HTMLSpanElement = fixture.debugElement.query(By.css('.messageholder')).nativeElement;
message.click();
expect(component.dismiss).not.toHaveBeenCalled();
});
it('should call dismiss() when the close button is clicked', () => {
configTestingModule();
createComponent();
spyOn(component, 'dismiss');
@ -104,13 +116,15 @@ describe('NotificationComponent', () => {
@Component({
template: `
<aio-notification
icon="insert_comment"
iconLabel="Survey"
buttonText="Go to survey"
actionUrl="https://bit.ly/angular-survey-2018"
notificationId="survey-january-2018"
expirationDate="2018-01-22">
Help Angular by taking a <strong>1 minute survey</strong>!
<span class="messageholder">
<a href="https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4">
<span *ngIf="false" class="bad">This should not appear</span>
<span class="message">Version 6 of Angular Now Available!</span>
<span class="action-button">Learn More</span>
</a>
</span>
</aio-notification>`
})
class TestComponent {

View File

@ -22,10 +22,7 @@ const LOCAL_STORAGE_NAMESPACE = 'aio-notification/';
export class NotificationComponent implements OnInit {
private get localStorage() { return this.window.localStorage; }
@Input() icon: string;
@Input() iconLabel: string;
@Input() buttonText: string;
@Input() actionUrl: string;
@Input() dismissOnContentClick: boolean;
@Input() notificationId: string;
@Input() expirationDate: string;
@Output() dismissed = new EventEmitter();
@ -44,6 +41,12 @@ export class NotificationComponent implements OnInit {
this.showNotification = previouslyHidden || expired ? 'hide' : 'show';
}
contentClick() {
if (this.dismissOnContentClick) {
this.dismiss();
}
}
dismiss() {
this.localStorage.setItem(LOCAL_STORAGE_NAMESPACE + this.notificationId, 'hide');
this.showNotification = 'hide';

View File

@ -74,6 +74,13 @@ describe('GaService', () => {
});
});
describe('sendEvent', () => {
it('should send "event" with associated data', () => {
gaService.sendEvent('some source', 'some campaign', 'a label', 45);
expect(gaSpy).toHaveBeenCalledWith('send', 'event', 'some source', 'some campaign', 'a label', 45);
});
});
it('should support replacing the `window.ga` function', () => {
const gaSpy2 = jasmine.createSpy('new ga');
mockWindow.ga = gaSpy2;

View File

@ -29,6 +29,10 @@ export class GaService {
this.ga('send', 'pageview');
}
sendEvent(source: string, action: string, label?: string, value?: number) {
this.ga('send', 'event', source, action, label, value);
}
ga(...args: any[]) {
const gaFn = (this.window as any)['ga'];
if (gaFn) {

View File

@ -64,18 +64,22 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
margin: $hamburgerShownMargin;
padding: 0;
&:not(.starting) {
transition-duration: 0.4s;
transition-property: color, margin;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
}
@media (min-width: 992px) {
// Hamburger hidden by default on large screens.
// (Will be shown per doc.)
margin: $hamburgerHiddenMargin;
}
@media (max-width: 480px) {
min-width: 15%;
}
&:not(.starting) {
transition-duration: 0.4s;
transition-property: color, margin;
transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1);
}
&:hover {
color: $offwhite;
}
@ -92,6 +96,10 @@ aio-shell.folder-tutorial mat-toolbar.mat-toolbar {
margin: 0 16px 0 0;
padding: 21px 0;
@media screen and (max-width: 480px) {
margin-right: 8px;
}
img {
position: relative;
margin-top: -21px;
@ -194,12 +202,17 @@ aio-search-box.search-container {
.toolbar-external-icons-container {
display: flex;
flex-direction: row;
height: 100%;
a {
display: flex;
align-items: center;
margin-left: 16px;
@media screen and (max-width: 480px) {
margin-left: 8px;
}
&:hover {
opacity: 0.8;
}

View File

@ -31,11 +31,14 @@ aio-notification {
}
.content {
display: flex;
max-width: calc(100% - #{$notificationHeight});
text-transform: none;
padding: 0;
> * {
display: flex;
}
.icon {
margin-right: 10px;
@media (max-width: 464px) {
@ -46,10 +49,10 @@ aio-notification {
.message {
overflow: hidden;
text-overflow: ellipsis;
margin-right: 10px;
}
.action-button {
margin-left: 10px;
background: $brightred;
border-radius: 15px;
text-transform: uppercase;

View File

@ -0,0 +1,21 @@
{
"scripts": [
{ "name": "ng", "command": "ng" },
{ "name": "build", "command": "ng build" },
{ "name": "start", "command": "ng serve" },
{ "name": "test", "command": "ng test" },
{ "name": "lint", "command": "ng lint" },
{ "name": "e2e", "command": "ng e2e" }
],
"dependencies": [
"@angular/elements"
],
"devDependencies": [
"@angular-devkit/build-angular",
"@angular/cli",
"@types/jasminewd2",
"jasmine-spec-reporter",
"karma-coverage-istanbul-reporter",
"ts-node"
]
}

View File

@ -68,7 +68,7 @@ function runE2e() {
// that they should run under. Then run each app/spec collection sequentially.
function findAndRunE2eTests(filter, outputFile, shard) {
const shardParts = shard ? shard.split('/') : [0,1];
const shardParts = shard ? shard.split('/') : [0, 1];
const shardModulo = parseInt(shardParts[0], 10);
const shardDivider = parseInt(shardParts[1], 10);
@ -82,11 +82,17 @@ function findAndRunE2eTests(filter, outputFile, shard) {
const status = { passed: [], failed: [] };
return getE2eSpecs(EXAMPLES_PATH, filter)
.then(e2eSpecPaths => {
console.log('All e2e specs:');
logSpecs(e2eSpecPaths);
Object.keys(e2eSpecPaths).forEach(key => {
const value = e2eSpecPaths[key];
e2eSpecPaths[key] = value.filter((p, index) => index % shardDivider === shardModulo);
});
console.log(`E2e specs for shard ${shardParts.join('/')}:`);
logSpecs(e2eSpecPaths);
return e2eSpecPaths.systemjs.reduce((promise, specPath) => {
return promise.then(() => {
const examplePath = path.dirname(specPath);
@ -313,4 +319,16 @@ function loadExampleConfig(exampleFolder) {
return config;
}
// Log the specs (for debugging purposes).
// `e2eSpecPaths` is of type: `{[type: string]: string[]}`
// (where `type` is `systemjs`, `cli, etc.)
function logSpecs(e2eSpecPaths) {
Object.keys(e2eSpecPaths).forEach(type => {
const paths = e2eSpecPaths[type];
console.log(` ${type.toUpperCase()}:`);
console.log(paths.map(p => ` ${p}`).join('\n'));
});
}
runE2e();

View File

@ -8,11 +8,27 @@ module.exports = function computeApiBreadCrumbs(API_DOC_TYPES_TO_RENDER) {
if (API_DOC_TYPES_TO_RENDER.indexOf(doc.docType) !== -1) {
doc.breadCrumbs = [];
doc.breadCrumbs.push({ text: 'API', path: '/api' });
if (doc.moduleDoc) doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.path });
if (isSecondaryEntryPoint(doc)) {
doc.breadCrumbs.push(createPackageBreadcrumb(doc));
}
if (doc.moduleDoc) {
if (isSecondaryEntryPoint(doc.moduleDoc)) {
doc.breadCrumbs.push(createPackageBreadcrumb(doc.moduleDoc));
}
doc.breadCrumbs.push({ text: '@angular/' + doc.moduleDoc.id, path: doc.moduleDoc.path });
}
doc.breadCrumbs.push({ text: doc.name, path: doc.path });
}
});
}
};
};
function isSecondaryEntryPoint(doc) {
return doc.docType === 'package' && !doc.isPrimaryPackage;
}
function createPackageBreadcrumb(doc) {
return { text: doc.packageInfo.primary.name, path: doc.packageInfo.primary.path };
}

View File

@ -17,11 +17,17 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
const API_DOC_TYPES_TO_RENDER = ['class', 'interface', 'package'];
const processor = processorFactory(API_DOC_TYPES_TO_RENDER);
const httpPackage = { docType: 'package', name: '@angular/http', id: 'http', path: 'http', isPrimaryPackage: true };
const httpTestingPackage = { docType: 'package', name: '@angular/http/testing', id: 'http/testing', path: 'http/testing', packageInfo: { primary: httpPackage } };
const testRequestClass = { docType: 'class', name: 'TestRequest', path: 'http/testing/test-request', moduleDoc: httpTestingPackage };
const docs = [
{ docType: 'class', name: 'ClassA', path: 'module-1/class-a', moduleDoc: { id: 'moduleOne', path: 'module-1' } },
{ docType: 'interface', name: 'InterfaceB', path: 'module-2/interface-b', moduleDoc: { id: 'moduleTwo', path: 'module-2' } },
{ docType: 'guide', name: 'Guide One', path: 'guide/guide-1' },
{ docType: 'package', name: 'testing', id: 'http/testing', path: 'http/testing' },
httpPackage,
httpTestingPackage,
testRequestClass
];
processor.$process(docs);
@ -38,7 +44,18 @@ describe('angular-api-package: computeApiBreadCrumbs processor', () => {
expect(docs[2].breadCrumbs).toBeUndefined();
expect(docs[3].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: 'testing', path: 'http/testing' },
{ text: '@angular/http', path: 'http' },
]);
expect(docs[4].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' },
{ text: '@angular/http/testing', path: 'http/testing' },
]);
expect(docs[5].breadCrumbs).toEqual([
{ text: 'API', path: '/api' },
{ text: '@angular/http', path: 'http' },
{ text: '@angular/http/testing', path: 'http/testing' },
{ text: 'TestRequest', path: 'http/testing/test-request' },
]);
});
});

View File

@ -1,6 +1,6 @@
{% import "lib/githubLinks.html" as github -%}
{% set comma = joiner(',') %}
{% set slash = joiner('/') %}
{% set breadcrumbDelimiter = joiner('&gt;') %}
<article>
{$ github.githubLinks(doc, versionInfo) $}
<div class="breadcrumb">
@ -14,14 +14,11 @@
]
}
</script>
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ slash() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
</div>
<header class="api-header">
<h1>{$ doc.name $}</h1>
<label class="api-type-label {$ doc.docType $}">{$ doc.docType $}</label>
{% if doc.deprecated !== undefined %}<label class="api-status-label deprecated">deprecated</label>{% endif %}
{% if doc.experimental !== undefined %}<label class="api-status-label experimental">experimental</label>{% endif %}
{% if doc.stable !== undefined %}<label class="api-status-label stable">stable</label>{% endif %}
{% if doc.pipeOptions.pure === 'false' %}<label class="api-status-label impure-pipe">impure</label>{% endif %}
</header>
<aio-toc class="embedded"></aio-toc>

View File

@ -1,6 +1,6 @@
{% extends 'base.template.html' -%}
{% macro listItems(items, title) %}
{% macro listItems(items, title, overridePath) %}
{% if items.length %}
<section class="export-list">
<h3>{$ title $}</h3>
@ -8,7 +8,7 @@
{% for item in items %}
<tr>
<td><code class="code-anchor">
<a href="{$ item.path $}">{$ item.name $}</a></code></td>
<a href="{$ overridePath or item.path $}">{$ item.name $}</a></code></td>
<td>{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}</td>
</tr>
{% endfor %}
@ -24,12 +24,13 @@
{% include "includes/see-also.html" %}
{% if doc.isPrimaryPackage %}
<h2>Entry points</h2>
{$ listItems([doc.packageInfo.primary], 'Primary') $}
{$ listItems([doc.packageInfo.primary], 'Primary', '#primary-entry-point-exports') $}
{$ listItems(doc.packageInfo.secondary, 'Secondary') $}
{% endif %}
<h2>Exports</h2>
<h2>{% if doc.isPrimaryPackage %}Primary entry{% else %}Entry{% endif %} point exports</h2>
{$ listItems(doc.classes, 'Classes') $}
{$ listItems(doc.decorators, 'Decorators') $}
{$ listItems(doc.functions, 'Functions') $}

View File

@ -153,7 +153,7 @@ Note that Bazel has a `--stamp` argument to `bazel build`, but this has no effec
Bazel supports fetching action results from a cache, allowing a clean build to pick up artifacts from prior builds.
This makes builds incremental, even on CI.
It works because Bazel assigns a content-based hash to all action inputs, which is used as the cache key for the action outputs.
Thanks the the hermeticity property, we can skip executing an action if the inputs hash is already present in the cache.
Thanks to the hermeticity property, we can skip executing an action if the inputs hash is already present in the cache.
Of course, non-hermeticity in an action can cause problems.
At worst, you can fetch a broken artifact from the cache, making your build non-reproducible.

View File

@ -14,11 +14,17 @@ Ctrl + Shift + j.
By default the debug tools are disabled. You can enable debug tools as follows:
```typescript
import {ApplicationRef} from '@angular/core';
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {enableDebugTools} from '@angular/platform-browser';
bootstrap(Application).then((appRef) => {
enableDebugTools(appRef);
});
platformBrowserDynamic().bootstrapModule(AppModule)
.then(moduleRef => {
const applicationRef = moduleRef.injector.get(ApplicationRef);
const appComponent = applicationRef.components[0];
enableDebugTools(appComponent);
})
```
### Using debug tools

26
index.bzl Normal file
View File

@ -0,0 +1,26 @@
# Copyright Google Inc. All Rights Reserved.
#
# Use of this source code is governed by an MIT-style license that can be
# found in the LICENSE file at https://angular.io/license
""" Public API surface is re-exported here.
This API is exported for user's building angular from source in downstream
projects. The rules from packages/bazel are re-exported here as well
as the ng_setup_workspace repository rule needed when building angular
from source downstream. Alternately, this API is available from the
@angular/bazel npm package if the npm distribution of angular is
used in a downstream project.
"""
load("//packages/bazel:index.bzl",
_ng_module = "ng_module",
_ng_package = "ng_package",
_protractor_web_test = "protractor_web_test",
_protractor_web_test_suite = "protractor_web_test_suite")
load("//tools:ng_setup_workspace.bzl", _ng_setup_workspace = "ng_setup_workspace")
ng_module = _ng_module
ng_package = _ng_package
protractor_web_test = _protractor_web_test
protractor_web_test_suite = _protractor_web_test_suite
ng_setup_workspace = _ng_setup_workspace

View File

@ -6,23 +6,30 @@ workspace(name = "bazel_integration_test")
http_archive(
name = "build_bazel_rules_nodejs",
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
strip_prefix = "rules_nodejs-0.10.1",
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.11.4.zip"],
strip_prefix = "rules_nodejs-0.11.4",
sha256 = "c31c4ead696944a50fad2b3ee9dfbbeffe31a8dcca0b21b9bf5b3e6c6b069801",
)
http_archive(
name = "bazel_skylib",
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip"],
strip_prefix = "bazel-skylib-0.3.1",
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
)
http_archive(
name = "io_bazel_rules_webtesting",
url = "https://github.com/bazelbuild/rules_webtesting/archive/8fd9ce0fd9254bde251da0bc373d6cd08e811434.zip",
strip_prefix = "rules_webtesting-8fd9ce0fd9254bde251da0bc373d6cd08e811434",
sha256 = "4baee95fcfadfbaf868707af8accfd1cb98c5d13f808908e0152468bfb47f0f7",
url = "https://github.com/bazelbuild/rules_webtesting/archive/0.2.1.zip",
strip_prefix = "rules_webtesting-0.2.1",
sha256 = "7d490aadff9b5262e5251fa69427ab2ffd1548422467cb9f9e1d110e2c36f0fa",
)
http_archive(
name = "build_bazel_rules_typescript",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.0.zip",
strip_prefix = "rules_typescript-0.15.0",
sha256 = "1aa75917330b820cb239b3c10a936a10f0a46fe215063d4492dd76dc6e1616f4",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
strip_prefix = "rules_typescript-0.16.0",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
)
http_archive(
@ -38,6 +45,20 @@ http_archive(
sha256 = "b243c4d64f054c174051785862ab079050d90b37a1cef7da93821c6981cb9ad4",
)
#
# Point Bazel to WORKSPACEs that live in subdirectories
#
local_repository(
name = "angular",
path = "node_modules/@angular/bazel",
)
local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
#
# Load and install our dependencies downloaded above.
#
@ -68,24 +89,6 @@ load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()
#
# Point Bazel to WORKSPACEs that live in subdirectories
#
local_repository(
name = "angular",
path = "node_modules/@angular/bazel",
)
local_repository(
name = "rxjs",
path = "node_modules/rxjs/src",
)
#
# Load and install our dependencies from local repositories
#
load("@angular//:index.bzl", "ng_setup_workspace")
ng_setup_workspace()

View File

@ -32,6 +32,7 @@ ts_library(
name = "test_lib",
testonly = 1,
srcs = glob(["*.spec.ts"]),
tsconfig = "//src:tsconfig.json",
deps = [":hello-world"],
)

View File

@ -5,6 +5,7 @@ ts_library(
name = "e2e",
testonly = 1,
srcs = ["app.spec.ts"],
tsconfig = ":tsconfig.json",
)
ts_library(

View File

@ -3,47 +3,50 @@
"@angular/animations@file:../../dist/packages-dist/animations":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/bazel@file:../../dist/packages-dist/bazel":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
"@bazel/typescript" "^0.15.0"
"@types/node" "6.0.84"
protobufjs "5.0.0"
"@angular/common@file:../../dist/packages-dist/common":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/compiler-cli@file:../../dist/packages-dist/compiler-cli":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
chokidar "^1.4.2"
convert-source-map "^1.5.1"
magic-string "^0.25.0"
minimist "^1.2.0"
reflect-metadata "^0.1.2"
tsickle "^0.30.0"
source-map "^0.6.1"
tsickle "^0.32.1"
"@angular/compiler@file:../../dist/packages-dist/compiler":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/core@file:../../dist/packages-dist/core":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser-dynamic@file:../../dist/packages-dist/platform-browser-dynamic":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
"@angular/platform-browser@file:../../dist/packages-dist/platform-browser":
version "6.1.0-beta.3"
version "6.1.0"
dependencies:
tslib "^1.9.0"
@ -316,6 +319,10 @@ console-control-strings@^1.0.0, console-control-strings@~1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
convert-source-map@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5"
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -826,6 +833,12 @@ long@~3:
version "3.2.0"
resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b"
magic-string@^0.25.0:
version "0.25.0"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.0.tgz#1f3696f9931ff0a1ed4c132250529e19cad6759b"
dependencies:
sourcemap-codec "^1.4.1"
math-random@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/math-random/-/math-random-1.0.1.tgz#8b3aac588b8a66e4975e3cdea67f7bb329601fac"
@ -1223,7 +1236,7 @@ rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.1:
glob "^7.0.5"
"rxjs@file:../../node_modules/rxjs":
version "6.0.0"
version "6.2.1"
dependencies:
tslib "^1.9.0"
@ -1305,10 +1318,14 @@ source-map@^0.5.6:
version "0.5.7"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
source-map@^0.6.0:
source-map@^0.6.0, source-map@^0.6.1:
version "0.6.1"
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
sourcemap-codec@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz#c8fd92d91889e902a07aee392bdd2c5863958ba2"
sshpk@^1.7.0:
version "1.14.2"
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.14.2.tgz#c6fc61648a3d9c4e764fd3fcdf4ea105e492ba98"
@ -1393,9 +1410,9 @@ tough-cookie@~2.3.3:
dependencies:
punycode "^1.4.1"
tsickle@^0.30.0:
version "0.30.0"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.30.0.tgz#7941146ae92933854a8742fa1047606c4536649b"
tsickle@^0.32.1:
version "0.32.1"
resolved "https://registry.yarnpkg.com/tsickle/-/tsickle-0.32.1.tgz#f16e94ba80b32fc9ebe320dc94fbc2ca7f3521a5"
dependencies:
jasmine-diff "^0.1.3"
minimist "^1.2.0"

View File

@ -10,7 +10,7 @@
"@angular/core": "file:../../dist/packages-dist/core",
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
"google-closure-compiler": "20180319.0.0",
"google-closure-compiler": "20180716.0.0",
"rxjs": "file:../../node_modules/rxjs",
"typescript": "file:../../node_modules/typescript",
"zone.js": "file:../../node_modules/zone.js"

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,8 @@ module.exports = function(config) {
// Serve AngularJS for `ngUpgrade` testing.
{pattern: 'node_modules/angular-1.5/angular.js', included: false, watched: false},
{pattern: 'node_modules/angular-mocks-1.5/angular-mocks.js', included: false, watched: false},
{pattern: 'node_modules/angular-1.6/angular.js', included: false, watched: false},
{pattern: 'node_modules/angular-mocks-1.6/angular-mocks.js', included: false, watched: false},
{pattern: 'node_modules/angular/angular.js', included: false, watched: false},
{pattern: 'node_modules/angular-mocks/angular-mocks.js', included: false, watched: false},
@ -82,7 +84,7 @@ module.exports = function(config) {
'dist/all/@angular/elements/schematics/**',
'dist/all/@angular/examples/**/e2e_test/*',
'dist/all/@angular/language-service/**',
'dist/all/@angular/router/test/**',
'dist/all/@angular/router/**/test/**',
'dist/all/@angular/platform-browser/testing/e2e_util.js',
'dist/all/angular1_router.js',
'dist/examples/**/e2e_test/**',
@ -110,6 +112,7 @@ module.exports = function(config) {
// don't need this entire config file.
proxies: {
'/base/angular/': '/base/',
'/base/angular_deps/': '/base/',
},
reporters: ['dots'],

View File

@ -29,7 +29,7 @@ describe('largeform benchmark perf', () => {
[CreateAndDestroyWorker].forEach((worker) => {
describe(worker.id, () => {
it('should run for ng2', (done) => {
it('should run for ng2', done => {
runLargeFormBenchmark({
id: `largeform.ng2.${worker.id}`,
url: 'all/benchmarks/src/largeform/ng2/index.html',

View File

@ -40,7 +40,7 @@ describe('largetable benchmark perf', () => {
[CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => {
describe(worker.id, () => {
it('should run for ng2', (done) => {
it('should run for ng2', done => {
runTableBenchmark({
id: `largeTable.ng2.${worker.id}`,
url: 'all/benchmarks/src/largetable/ng2/index.html',
@ -48,7 +48,7 @@ describe('largetable benchmark perf', () => {
}).then(done, done.fail);
});
it('should run for ng2 with ngSwitch', (done) => {
it('should run for ng2 with ngSwitch', done => {
runTableBenchmark({
id: `largeTable.ng2_switch.${worker.id}`,
url: 'all/benchmarks/src/largetable/ng2_switch/index.html',
@ -56,7 +56,7 @@ describe('largetable benchmark perf', () => {
}).then(done, done.fail);
});
it('should run for render3', (done) => {
it('should run for render3', done => {
runTableBenchmark({
id: `largeTable.render3.${worker.id}`,
url: 'all/benchmarks/src/largetable/render3/index.html',
@ -65,7 +65,7 @@ describe('largetable benchmark perf', () => {
}).then(done, done.fail);
});
it('should run for iv', (done) => {
it('should run for iv', done => {
runTableBenchmark({
id: `largeTable.iv.${worker.id}`,
url: 'all/benchmarks/src/largetable/iv/index.html',
@ -74,7 +74,7 @@ describe('largetable benchmark perf', () => {
}).then(done, done.fail);
});
it('should run for the baseline', (done) => {
it('should run for the baseline', done => {
runTableBenchmark({
id: `largeTable.baseline.${worker.id}`,
url: 'all/benchmarks/src/largetable/baseline/index.html',
@ -83,7 +83,7 @@ describe('largetable benchmark perf', () => {
}).then(done, done.fail);
});
it('should run for incremental-dom', (done) => {
it('should run for incremental-dom', done => {
runTableBenchmark({
id: `largeTable.incremental_dom.${worker.id}`,
url: 'all/benchmarks/src/largetable/incremental_dom/index.html',

View File

@ -24,7 +24,7 @@ describe('tree benchmark perf', () => {
Benchmarks.forEach(benchmark => {
describe(benchmark.id, () => {
// This is actually a destroyOnly benchmark
it('should work for createOnly', (done) => {
it('should work for createOnly', done => {
runTreeBenchmark({
id: 'createOnly',
benchmark,
@ -33,7 +33,7 @@ describe('tree benchmark perf', () => {
}).then(done, done.fail);
});
it('should work for createOnlyForReal', (done) => {
it('should work for createOnlyForReal', done => {
runTreeBenchmark({
id: 'createOnlyForReal',
benchmark,
@ -42,7 +42,7 @@ describe('tree benchmark perf', () => {
}).then(done, done.fail);
});
it('should work for createDestroy', (done) => {
it('should work for createDestroy', done => {
runTreeBenchmark({
id: 'createDestroy',
benchmark,
@ -53,13 +53,13 @@ describe('tree benchmark perf', () => {
}).then(done, done.fail);
});
it('should work for update', (done) => {
it('should work for update', done => {
runTreeBenchmark({id: 'update', benchmark, work: () => $(CreateBtn).click()})
.then(done, done.fail);
});
if (benchmark.buttons.indexOf(DetectChangesBtn) !== -1) {
it('should work for detectChanges', (done) => {
it('should work for detectChanges', done => {
runTreeBenchmark({
id: 'detectChanges',
benchmark,

View File

@ -41,7 +41,7 @@ describe('largetable benchmark perf', () => {
[CreateOnlyWorker, CreateAndDestroyWorker, UpdateWorker].forEach((worker) => {
describe(worker.id, () => {
it('should run for render3', (done) => {
it('should run for render3', done => {
runTableBenchmark({
id: `largeTable.render3.${worker.id}`,
url: 'index.html',

10
modules/types.d.ts vendored
View File

@ -8,11 +8,11 @@
// This file contains all ambient imports needed to compile the modules/ source code
/// <reference path="../node_modules/@types/hammerjs/index.d.ts" />
/// <reference path="../node_modules/@types/jasmine/index.d.ts" />
/// <reference path="../node_modules/@types/jasminewd2/index.d.ts" />
/// <reference path="../node_modules/@types/node/index.d.ts" />
/// <reference path="../node_modules/zone.js/dist/zone.js.d.ts" />
/// <reference types="hammerjs" />
/// <reference types="jasmine" />
/// <reference types="jasminewd2" />
/// <reference types="node" />
/// <reference types="zone.js" />
/// <reference path="../tools/types-ext/jasminewd2.d.ts" />
/// <reference path="./es6-subset.d.ts" />
/// <reference path="./system.d.ts" />

View File

@ -1,6 +1,6 @@
{
"name": "angular-srcs",
"version": "6.1.0",
"version": "6.1.3",
"private": true,
"branchPattern": "2.0.*",
"description": "Angular - a web framework for modern web apps",
@ -21,7 +21,9 @@
"prebuildifier": "bazel build --noshow_progress @com_github_bazelbuild_buildtools//buildifier",
"buildifier": "find . -type f \\( -name BUILD -or -name BUILD.bazel \\) ! -path \"*/node_modules/*\" | xargs $(bazel info bazel-bin)/external/com_github_bazelbuild_buildtools/buildifier/*/buildifier",
"preinstall": "node tools/yarn/check-yarn.js",
"postinstall": "yarn update-webdriver && node ./tools/postinstall-patches.js",
"postinstall": "yarn update-webdriver && node ./tools/postinstall-patches.js && yarn patch-types",
"//patch-types": "work-around for issue https://github.com/angular/angular/issues/25051",
"patch-types": "node -e \"var sh = require('shelljs'); sh.set('-e'); sh.mkdir('-p', 'node_modules/@types/zone.js'); sh.cp('-f', 'node_modules/zone.js/dist/zone.js.d.ts', 'node_modules/@types/zone.js/index.d.ts')\"",
"update-webdriver": "webdriver-manager update --gecko false $CHROMEDRIVER_VERSION_ARG",
"check-env": "gulp check-env",
"commitmsg": "node ./scripts/git/commit-msg.js"
@ -56,10 +58,12 @@
"@types/source-map": "^0.5.1",
"@types/systemjs": "0.19.32",
"@webcomponents/custom-elements": "^1.0.4",
"angular": "npm:angular@1.6",
"angular": "npm:angular@1.7",
"angular-1.5": "npm:angular@1.5",
"angular-mocks": "npm:angular-mocks@1.6",
"angular-1.6": "npm:angular@1.6",
"angular-mocks": "npm:angular-mocks@1.7",
"angular-mocks-1.5": "npm:angular-mocks@1.5",
"angular-mocks-1.6": "npm:angular-mocks@1.6",
"base64-js": "1.2.1",
"bower": "1.8.2",
"browserstacktunnel-wrapper": "2.0.1",

View File

@ -9,31 +9,42 @@ import {AnimationMetadata, AnimationOptions} from './animation_metadata';
import {AnimationPlayer} from './players/animation_player';
/**
* AnimationBuilder is an injectable service that is available when the {@link
* BrowserAnimationsModule BrowserAnimationsModule} or {@link NoopAnimationsModule
* NoopAnimationsModule} modules are used within an application.
* An injectable service that produces an animation sequence programmatically within an
* Angular component or directive.
* Provided by the `BrowserAnimationsModule` or `NoopAnimationsModule`.
*
* The purpose of this service is to produce an animation sequence programmatically within an
* angular component or directive.
* @usageNotes
*
* Programmatic animations are first built and then a player is created when the build animation is
* attached to an element.
* To use this service, add it to your component or directive as a dependency.
* The service is instantiated along with your component.
*
* Apps do not typically need to create their own animation players, but if you
* do need to, follow these steps:
*
* 1. Use the `build()` method to create a programmatic animation using the
* `animate()` function. The method returns an `AnimationFactory` instance.
*
* 2. Use the factory object to create an `AnimationPlayer` and attach it to a DOM element.
*
* 3. Use the player object to control the animation programmatically.
*
* For example:
*
* ```ts
* // remember to include the BrowserAnimationsModule module for this to work...
* // import the service from BrowserAnimationsModule
* import {AnimationBuilder} from '@angular/animations';
*
* // require the service as a dependency
* class MyCmp {
* constructor(private _builder: AnimationBuilder) {}
*
* makeAnimation(element: any) {
* // first build the animation
* // first define a reusable animation
* const myAnimation = this._builder.build([
* style({ width: 0 }),
* animate(1000, style({ width: '100px' }))
* ]);
*
* // then create a player from it
* // use the returned factory object to create a player
* const player = myAnimation.create(element);
*
* player.play();
@ -41,22 +52,29 @@ import {AnimationPlayer} from './players/animation_player';
* }
* ```
*
* When an animation is built an instance of {@link AnimationFactory AnimationFactory} will be
* returned. Using that an {@link AnimationPlayer AnimationPlayer} can be created which can then be
* used to start the animation.
*
* @experimental Animation support is experimental.
*/
export abstract class AnimationBuilder {
/**
* Builds a factory for producing a defined animation.
* @param animation A reusable animation definition.
* @returns A factory object that can create a player for the defined animation.
* @see `animate()`
*/
abstract build(animation: AnimationMetadata|AnimationMetadata[]): AnimationFactory;
}
/**
* An instance of `AnimationFactory` is returned from {@link AnimationBuilder#build
* AnimationBuilder.build}.
* A factory object returned from the `AnimationBuilder`.`build()` method.
*
* @experimental Animation support is experimental.
*/
export abstract class AnimationFactory {
/**
* Creates an `AnimationPlayer` instance for the reusable animation defined by
* the `AnimationBuilder`.`build()` method that created this factory.
* Attaches the new player a DOM element.
* @param element The DOM element to which to attach the animation.
* @param options A set of options that can include a time delay and
* additional developer-defined parameters.
*/
abstract create(element: any, options?: AnimationOptions): AnimationPlayer;
}

View File

@ -38,11 +38,33 @@
* @experimental Animation support is experimental.
*/
export interface AnimationEvent {
/**
* The name of the state from which the animation is triggered.
*/
fromState: string;
/**
* The name of the state in which the animation completes.
*/
toState: string;
/**
* The time it takes the animation to complete, in milliseconds.
*/
totalTime: number;
/**
* The animation phase in which the callback was invoked, one of
* "start" or "done".
*/
phaseName: string;
/**
* The element to which the animation is attached.
*/
element: any;
/**
* Internal.
*/
triggerName: string;
/**
* Internal.
*/
disabled: boolean;
}

View File

@ -9,6 +9,14 @@
import {scheduleMicroTask} from '../util';
import {AnimationPlayer} from './animation_player';
/**
* A programmatic controller for a group of reusable animations.
* Used internally to control animations.
*
* @see `AnimationPlayer`
* @see `{@link animations/group group()}`
*
*/
export class AnimationGroupPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = [];
private _onStartFns: Function[] = [];

View File

@ -8,37 +8,110 @@
import {scheduleMicroTask} from '../util';
/**
* AnimationPlayer controls an animation sequence that was produced from a programmatic animation.
* (see {@link AnimationBuilder AnimationBuilder} for more information on how to create programmatic
* animations.)
* Provides programmatic control of a reusable animation sequence,
* built using the `build()` method of `AnimationBuilder`. The `build()` method
* returns a factory, whose `create()` method instantiates and initializes this interface.
*
* @see `AnimationBuilder`
* @see `AnimationFactory`
* @see `animate()`
*
* @experimental Animation support is experimental.
*/
export interface AnimationPlayer {
/**
* Provides a callback to invoke when the animation finishes.
* @param fn The callback function.
* @see `finish()`
*/
onDone(fn: () => void): void;
/**
* Provides a callback to invoke when the animation starts.
* @param fn The callback function.
* @see `run()`
*/
onStart(fn: () => void): void;
/**
* Provides a callback to invoke after the animation is destroyed.
* @param fn The callback function.
* @see `destroy()`
* @see `beforeDestroy()`
*/
onDestroy(fn: () => void): void;
/**
* Initializes the animation.
*/
init(): void;
/**
* Reports whether the animation has started.
* @returns True if the animation has started, false otherwise.
*/
hasStarted(): boolean;
/**
* Runs the animation, invoking the `onStart()` callback.
*/
play(): void;
/**
* Pauses the animation.
*/
pause(): void;
/**
* Restarts the paused animation.
*/
restart(): void;
/**
* Ends the animation, invoking the `onDone()` callback.
*/
finish(): void;
/**
* Destroys the animation, after invoking the `beforeDestroy()` callback.
* Calls the `onDestroy()` callback when destruction is completed.
*/
destroy(): void;
/**
* Resets the animation to its initial state.
*/
reset(): void;
setPosition(p: any /** TODO #9100 */): void;
/**
* Sets the position of the animation.
* @param position A 0-based offset into the duration, in milliseconds.
*/
setPosition(position: any /** TODO #9100 */): void;
/**
* Reports the current position of the animation.
* @returns A 0-based offset into the duration, in milliseconds.
*/
getPosition(): number;
/**
* The parent of this player, if any.
*/
parentPlayer: AnimationPlayer|null;
/**
* The total run time of the animation, in milliseconds.
*/
readonly totalTime: number;
/**
* Provides a callback to invoke before the animation is destroyed.
*/
beforeDestroy?: () => any;
/** @internal */
/** @internal
* Internal
*/
triggerCallback?: (phaseName: string) => void;
/** @internal */
/** @internal
* Internal
*/
disabled?: boolean;
}
/**
* @experimental Animation support is experimental.
* An empty programmatic controller for reusable animations.
* Used internally when animations are disabled, to avoid
* checking for the null case when an animation player is expected.
*
* @see `animate()`
* @see `AnimationPlayer`
* @see `GroupPlayer`
*
*/
export class NoopAnimationPlayer implements AnimationPlayer {
private _onDoneFns: Function[] = [];
@ -93,7 +166,7 @@ export class NoopAnimationPlayer implements AnimationPlayer {
}
}
reset(): void {}
setPosition(p: number): void {}
setPosition(position: number): void {}
getPosition(): number { return 0; }
/** @internal */

View File

@ -19,6 +19,7 @@ nodejs_binary(
# dependency @build_bazel_rules_nodejs_rollup_deps. We don't need any
# additional npm dependencies when we run rollup or uglify.
entry_point = "build_bazel_rules_nodejs_rollup_deps/node_modules/rollup/bin/rollup",
install_source_map_support = False,
node_modules = "@build_bazel_rules_nodejs_rollup_deps//:node_modules",
)
@ -26,4 +27,5 @@ nodejs_binary(
name = "modify_tsconfig",
data = ["modify_tsconfig.js"],
entry_point = "angular/packages/bazel/src/modify_tsconfig.js",
install_source_map_support = False,
)

View File

@ -22,5 +22,6 @@ nodejs_binary(
name = "packager",
data = ["lib"],
entry_point = "angular/packages/bazel/src/ng_package/packager.js",
install_source_map_support = False,
node_modules = "@angular_packager_deps//:node_modules",
)

View File

@ -6,15 +6,19 @@
"Install toolchain dependencies"
load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install")
load("@build_bazel_rules_typescript//:defs.bzl", "check_rules_typescript_version")
def ng_setup_workspace():
"""This repository rule should be called from your WORKSPACE file.
"""This repository rule should be called from your WORKSPACE file.
It creates some additional Bazel external repositories that are used internally
by the Angular rules.
"""
yarn_install(
name = "angular_packager_deps",
package_json = "@angular//packages/bazel/src/ng_package:package.json",
yarn_lock = "@angular//packages/bazel/src/ng_package:yarn.lock",
)
It creates some additional Bazel external repositories that are used internally
by the Angular rules.
"""
yarn_install(
name = "angular_packager_deps",
package_json = "@angular//packages/bazel/src/ng_package:package.json",
yarn_lock = "@angular//packages/bazel/src/ng_package:yarn.lock",
)
# 0.16.0: minimal version required to work with ng_module
check_rules_typescript_version("0.16.0")

View File

@ -30,7 +30,7 @@ nodejs_binary(
name = "ngc-wrapped",
data = [
":ngc_lib",
"@build_bazel_rules_typescript//internal:worker_protocol.proto",
"@build_bazel_rules_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto",
],
entry_point = "angular/packages/bazel/src/ngc-wrapped/index.js",
visibility = ["//visibility:public"],

View File

@ -76,7 +76,8 @@ export function runOneBuild(args: string[], inputs?: {[path: string]: string}):
export function relativeToRootDirs(filePath: string, rootDirs: string[]): string {
if (!filePath) return filePath;
// NB: the rootDirs should have been sorted longest-first
for (const dir of rootDirs || []) {
for (let i = 0; i < rootDirs.length; i++) {
const dir = rootDirs[i];
const rel = path.posix.relative(dir, filePath);
if (rel.indexOf('.') != 0) return rel;
}
@ -106,7 +107,9 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
fileLoader = new CachedFileLoader(fileCache, allowNonHermeticReads);
// Resolve the inputs to absolute paths to match TypeScript internals
const resolvedInputs: {[path: string]: string} = {};
for (const key of Object.keys(inputs)) {
const inputKeys = Object.keys(inputs);
for (let i = 0; i < inputKeys.length; i++) {
const key = inputKeys[i];
resolvedInputs[resolveNormalizedPath(key)] = inputs[key];
}
fileCache.updateCache(resolvedInputs);
@ -207,6 +210,12 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
if (fileName ===
path.join(compilerOpts.baseUrl, bazelOpts.package, compilerOpts.flatModuleOutFile + '.ts'))
return true;
// Also handle the case when angular is build from source as an external repository
if (fileName ===
path.join(
compilerOpts.baseUrl, 'external/angular', bazelOpts.package,
compilerOpts.flatModuleOutFile + '.ts'))
return true;
return origBazelHostShouldNameModule(fileName) || NGC_GEN_FILES.test(fileName);
};
@ -300,8 +309,8 @@ export function compile({allowNonHermeticReads, allDepsCompiledWithBazel = true,
fs.writeFileSync(bazelOpts.tsickleExternsPath, externs);
}
for (const missing of writtenExpectedOuts) {
originalWriteFile(missing, '', false);
for (let i = 0; i < writtenExpectedOuts.length; i++) {
originalWriteFile(writtenExpectedOuts[i], '', false);
}
return {program, diagnostics};
@ -317,7 +326,8 @@ function generateMetadataJson(
program: ts.Program, files: string[], rootDirs: string[], bazelBin: string,
tsHost: ts.CompilerHost) {
const collector = new ng.MetadataCollector();
for (const file of files) {
for (let i = 0; i < files.length; i++) {
const file = files[i];
const sourceFile = program.getSourceFile(file);
if (sourceFile) {
const metadata = collector.getMetadata(sourceFile);
@ -347,7 +357,9 @@ function gatherDiagnosticsForInputsOnly(
// program.getDeclarationDiagnostics() it somehow corrupts the emit.
diagnostics.push(...tsProgram.getOptionsDiagnostics());
diagnostics.push(...tsProgram.getGlobalDiagnostics());
for (const sf of tsProgram.getSourceFiles().filter(f => isCompilationTarget(bazelOpts, f))) {
const programFiles = tsProgram.getSourceFiles().filter(f => isCompilationTarget(bazelOpts, f));
for (let i = 0; i < programFiles.length; i++) {
const sf = programFiles[i];
// Note: We only get the diagnostics for individual files
// to e.g. not check libraries.
diagnostics.push(...tsProgram.getSyntacticDiagnostics(sf));

View File

@ -30,6 +30,6 @@ jasmine_node_test(
":angular_core",
"//packages/bazel/test/ngc-wrapped/empty:empty_tsconfig.json",
"//packages/bazel/test/ngc-wrapped/empty:tsconfig.json",
"@build_bazel_rules_typescript//internal:worker_protocol.proto",
"@build_bazel_rules_typescript//third_party/github.com/bazelbuild/bazel/src/main/protobuf:worker_protocol.proto",
],
)

View File

@ -70,7 +70,7 @@ export function createTsConfig(options: TsConfigOptions) {
'tsickleExternsPath': '',
// we don't copy the node_modules into our tmp dir, so we should look in
// the original workspace directory for it
'nodeModulesPrefix': '../angular/node_modules',
'nodeModulesPrefix': '../angular/external/angular_deps/node_modules',
},
'files': options.files,
'angularCompilerOptions': {

View File

@ -6,6 +6,7 @@ load("//tools/http-server:http_server.bzl", "http_server")
ts_library(
name = "app",
srcs = ["app.ts"],
tsconfig = ":tsconfig.json",
)
ts_devserver(
@ -33,6 +34,7 @@ ts_library(
name = "ts_spec",
testonly = True,
srcs = ["test.spec.ts"],
tsconfig = ":tsconfig.json",
)
protractor_web_test_suite(
@ -49,6 +51,6 @@ protractor_web_test_suite(
configuration = ":conf.js",
data = ["//packages/bazel/src/protractor/utils"],
on_prepare = ":on-prepare.js",
server = ":prodserver",
server = ":devserver",
deps = [":ts_spec"],
)

View File

@ -1,5 +1,5 @@
{
"compilerOptions": {
"lib": ["es2015"]
"lib": ["dom", "es2015"]
}
}

View File

@ -5,6 +5,7 @@ ts_library(
name = "ts_spec",
testonly = True,
srcs = ["test.spec.ts"],
tsconfig = ":tsconfig.json",
)
ts_library(

View File

@ -28,13 +28,13 @@ export interface HttpParameterCodec {
*
*/
export class HttpUrlEncodingCodec implements HttpParameterCodec {
encodeKey(k: string): string { return standardEncoding(k); }
encodeKey(key: string): string { return standardEncoding(key); }
encodeValue(v: string): string { return standardEncoding(v); }
encodeValue(value: string): string { return standardEncoding(value); }
decodeKey(k: string): string { return decodeURIComponent(k); }
decodeKey(key: string): string { return decodeURIComponent(key); }
decodeValue(v: string) { return decodeURIComponent(v); }
decodeValue(value: string) { return decodeURIComponent(value); }
}

View File

@ -6,15 +6,14 @@
* found in the LICENSE file at https://angular.io/license
*/
import {HttpHandler} from '../src/backend';
import {HttpHeaders} from '../src/headers';
import {HttpRequest} from '../src/request';
import {HttpXsrfCookieExtractor, HttpXsrfInterceptor} from '../src/xsrf';
import {HttpXsrfCookieExtractor, HttpXsrfInterceptor, HttpXsrfTokenExtractor} from '../src/xsrf';
import {HttpClientTestingBackend} from '../testing/src/backend';
class SampleTokenExtractor {
constructor(private token: string|null) {}
class SampleTokenExtractor extends HttpXsrfTokenExtractor {
constructor(private token: string|null) { super(); }
getToken(): string|null { return this.token; }
}

View File

@ -51,22 +51,22 @@ export class NgClass implements DoCheck {
private _ngEl: ElementRef, private _renderer: Renderer2) {}
@Input('class')
set klass(v: string) {
set klass(value: string) {
this._removeClasses(this._initialClasses);
this._initialClasses = typeof v === 'string' ? v.split(/\s+/) : [];
this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : [];
this._applyClasses(this._initialClasses);
this._applyClasses(this._rawClass);
}
@Input()
set ngClass(v: string|string[]|Set<string>|{[klass: string]: any}) {
set ngClass(value: string|string[]|Set<string>|{[klass: string]: any}) {
this._removeClasses(this._rawClass);
this._applyClasses(this._initialClasses);
this._iterableDiffer = null;
this._keyValueDiffer = null;
this._rawClass = typeof v === 'string' ? v.split(/\s+/) : v;
this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value;
if (this._rawClass) {
if (isListLikeIterable(this._rawClass)) {

View File

@ -16,6 +16,8 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
* `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and
* any existing component will get destroyed.
*
* @usageNotes
*
* ### Fine tune control
*
* You can control the component creation process by using the following optional attributes:
@ -50,7 +52,8 @@ import {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, NgMo
* ngModuleFactory: moduleFactory;">
* </ng-container>
* ```
* ## Example
*
* ### A simple example
*
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
*

View File

@ -27,6 +27,8 @@ export class NgForOfContext<T> {
* for each instantiated template inherits from the outer context with the given loop variable
* set to the current item from the iterable.
*
* @usageNotes
*
* ### Local Variables
*
* `NgForOf` provides several exported values that can be aliased to local variables:

View File

@ -17,13 +17,16 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
* - `then` template is the inline template of `ngIf` unless bound to a different value.
* - `else` template is blank unless it is bound.
*
* ## Most common usage
*
* @usageNotes
*
* ### Most common usage
*
* The most common usage of the `ngIf` directive is to conditionally show the inline template as
* seen in this example:
* {@example common/ngIf/ts/module.ts region='NgIfSimple'}
*
* ## Showing an alternative template using `else`
* ### Showing an alternative template using `else`
*
* If it is necessary to display a template when the `expression` is falsy use the `else` template
* binding as shown. Note that the `else` binding points to a `<ng-template>` labeled `#elseBlock`.
@ -32,7 +35,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
*
* {@example common/ngIf/ts/module.ts region='NgIfElse'}
*
* ## Using non-inlined `then` template
* ### Using non-inlined `then` template
*
* Usually the `then` template is the inlined template of the `ngIf`, but it can be changed using
* a binding (just like `else`). Because `then` and `else` are bindings, the template references can
@ -40,7 +43,7 @@ import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef, ɵstri
*
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
*
* ## Storing conditional result in a variable
* ### Storing conditional result in a variable
*
* A common pattern is that we need to show a set of properties from the same object. If the
* object is undefined, then we have to use the safe-traversal-operator `?.` to guard against

View File

@ -41,10 +41,10 @@ export class NgStyle implements DoCheck {
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer2) {}
@Input()
set ngStyle(v: {[key: string]: string}) {
this._ngStyle = v;
if (!this._differ && v) {
this._differ = this._differs.find(v).create();
set ngStyle(values: {[key: string]: string}) {
this._ngStyle = values;
if (!this._differ && values) {
this._differ = this._differs.find(values).create();
}
}

View File

@ -11,11 +11,6 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange
/**
* @ngModule CommonModule
*
* @usageNotes
* ```
* <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
* ```
*
* @description
*
* Inserts an embedded view from a prepared `TemplateRef`.
@ -24,13 +19,17 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChange, SimpleChange
* `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding
* by the local template `let` declarations.
*
* Note: using the key `$implicit` in the context object will set its value as default.
* @usageNotes
* ```
* <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
* ```
*
* ## Example
* Using the key `$implicit` in the context object will set its value as default.
*
* ### Example
*
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
*
*
*/
@Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet implements OnChanges {

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