Compare commits

..

147 Commits

Author SHA1 Message Date
4a8d56a820 release: cut the v6.1.6 release 2018-08-29 15:38:51 -07:00
0a3dd872e3 build: fix bad merge onto patch branch (#25729)
PR Close #25729
2018-08-29 18:34:22 -04:00
3e690e0062 fix(bazel): Cache fileNameToModuleName lookups (#25731)
This saves expensive re-parsing of the file when not run as a Bazel worker

PR Close #25731
2018-08-29 17:39:22 -04:00
7f8d6c1066 release: cut the v6.1.5 release 2018-08-28 21:39:18 -07:00
c6d502f7f8 docs: clarification of hero selection in routing section (#25634)
PR Close #25634
2018-08-28 22:16:18 -04:00
7aff3641a1 fix(bazel): only lookup amd module-name tags in .d.ts files (#25710)
PR Close #25710
2018-08-28 21:07:15 -04:00
2194b5a5c3 ci: remove vicb from pullapprove.yml (#25702)
PR Close #25702
2018-08-28 11:15:54 -07:00
8a35290686 build: update Bazel to 0.16 (#25646)
PR Close #25646
2018-08-27 18:20:32 -04:00
e40519c32a build(docs-infra): render all overloads if they are abstract (#25670)
In an overloaded method, the overload with the function body is the
actual method doc, and this doc is not included in the list of "additional"
overloads.

Moreover, the logic (all in dgeni-packages) is that if none of the items
has a body then we use the first overload as the actual method doc.

In the case of abstract methods, none of the methods have a body. So we
have a situation where the overloads collection does not contain the first
abstract method, even though it is not the "implementation" of the method.
Therefore we need to still render it.

Closes #25610

PR Close #25670
2018-08-27 18:19:08 -04:00
b560189c0e build(docs-infra): remove "annotations" section from API pages (#25677)
PR Close #25677
2018-08-27 18:18:42 -04:00
59cfc8a729 build(bazel): use value of /// <amd-module name=“”> directive to convert fileNameToModuleName in ngc-wrapped (#25650)
PR Close #25650
2018-08-27 12:21:19 -04:00
72ed2e90d0 build(bazel): esm5_outputs_aspect to work with targets such as ts_proto_library with no replay_params attribute (#25648)
PR Close #25648
2018-08-24 11:48:04 -04:00
4e82a76998 docs(elements): fix typo (tranformation --> transformation) (#25600)
PR Close #25600
2018-08-23 16:54:49 -04:00
51d5b433d0 fix(aio): show aio-themed 404 page for unknown resources (#23188)
Fixes #23179

PR Close #23188
2018-08-23 15:25:48 -04:00
cc0d0a9d1e fix(docs-infra): fix closure warning issue for improper internal flag (#25628)
PR Close #25628
2018-08-22 21:59:23 -04:00
82f26fe5f5 build(bazel): remove workaround no longer needed for module names for ngfactory & ngsummary files (#25604)
Workaround was added in https://github.com/angular/angular/pull/25335. It was necessary for .ngfactory & .ngsummary files to have proper AMD module names starting with @angular when building angular downstream from source using Bazel. The underlying issue has been resolved in the compiler and these files now get proper AMD module names without the need for this workaround. The workaround had an unexpected consequence https://github.com/angular/angular-cli/issues/11835 which is fixed by its removal.

PR Close #25604
2018-08-22 21:11:18 -04:00
8de57c9887 release: cut the v6.1.4 release 2018-08-22 15:05:02 -07:00
ace4e4ffa5 build: remove NGBUILDS_IO_KEY now that it is not used any more (#25601)
This is a follow-up to #25536.

PR Close #25601
2018-08-22 15:59:14 -04:00
1fa97903a3 docs: Improve docs for downgrading a service (#19371)
PR Close #19371
2018-08-21 10:49:00 -07:00
7e61645b82 fix(router): default scroll position restoration to disabled (#25586)
Fixes #25145
FW-305 #resolve

PR Close #25586
2018-08-21 10:48:14 -07:00
46b0ce9fc6 refactor(bazel): allow and ignore extra args for _ts_expected_outs (#25558)
This is needed to let ts_compile_actions take explicit list of srcs and deps to generate tsc actions from another rule. This is no-op for ngc for now.

PR Close #25558
2018-08-20 16:27:48 -07:00
78750a7fec docs: fix typo in service worker getting started guide (#25512)
PR Close #25512
2018-08-20 11:09:52 -07:00
77d9975eb2 build(aio): update dgeni-packages to 0.26.3 to fix reference types issue (#25528)
PR Close #25528
2018-08-20 11:09:24 -07:00
7eed4ee837 build: refactor ambient node & jasmine types so they are only included where needed (#25528)
PR Close #25528
2018-08-20 11:09:24 -07:00
292b435495 docs: fix typo in reactive forms guide (#25543)
PR Close #25543
2018-08-20 11:08:32 -07:00
5939c420ce docs: copy edit glossary (#25468)
PR Close #25468
2018-08-17 14:33:51 -07:00
a5cc9dbb53 ci: ensure aio_preview job has needed node_modules (#25536)
PR Close #25536
2018-08-17 13:48:27 -07:00
2b810a4e57 docs(docs-infra): the build.sh script was renamed to create-image.sh (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
2acf369664 ci(docs-infra): rename 'upload-server' to 'preview-server' (#25554)
The server no longer has files uploaded to it. Instead it is more
accurate to refer to it as dealing with "previews" of PRs.

PR Close #25554
2018-08-17 13:47:54 -07:00
860b79289f ci(docs-infra): add explicit return types to methods (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
b519d41f42 ci(docs-infra): improve preview-server logging (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
faf184ad63 ci(docs-infra): change AIO preview server stuff to pull builds from CircleCI (#25554)
Previously, Travis pushed the build artitfacts to the preview server.
This required us to use JWT to secure the POST request from Travis, to
ensure we couldn't receive malicious builds.

JWT has been deprecated and we are moving our builds to CircleCI.

This commit rewrites the TypeScript part of the preview server that
handles converting build artifact into hosted previews of the docs.

PR Close #25554
2018-08-17 13:47:54 -07:00
1e0f455855 ci(docs-infra): factor out the aio-builds-setup environment variables (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
ced30982df ci(docs-infra): move the payload-size check to the test job (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
fed429b0cc ci(docs-infra): add helper scripts for running TDD in Docker (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
9cb3107dda docs(docs-infra): update the preview server documentation (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
548a972c2a ci(docs-infra): move AIO preview deployment to CircleCI (#25554)
Now instead of pushing the AIO build artifacts to the preview server
from inside a Travis job, the artifacts are built and hosted on the
CircleCI infrastructure. The preview server will then pull these
down after being triggered by a CircleCI build webhook.

PR Close #25554
2018-08-17 13:47:54 -07:00
20dcc25eed ci(docs-infra): update upload-server to run on node.js v10 (#25554)
PR Close #25554
2018-08-17 13:47:54 -07:00
620d1402fe docs: add HttpClientModule import code to services tutorial (#24854)
To be able to copy and paste.

PR Close #24854
2018-08-16 13:51:18 -07:00
36fb4f4fdb docs: reactive forms guide copy edits (#25417)
PR Close #25417
2018-08-16 13:50:51 -07:00
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
525 changed files with 11250 additions and 33797 deletions

View File

@ -20,6 +20,18 @@ build --announce_rc
# We use this when uploading artifacts after the build finishes
build --symlink_prefix=dist/
# Enable experimental CircleCI bazel remote cache proxy
# See remote cache documentation in /docs/BAZEL.md
build --experimental_remote_spawn_cache --remote_rest_cache=http://localhost:7643
# Prevent unstable environment variables from tainting cache keys
build --experimental_strict_action_env
# Save downloaded repositories such as the go toolchain
# This directory can then be included in the CircleCI cache
# It should save time running the first build
build --experimental_repository_cache=/home/circleci/bazel_repository_cache
# Workaround https://github.com/bazelbuild/bazel/issues/3645
# Bazel doesn't calculate the memory ceiling correctly when running under Docker.
# Limit Bazel to consuming resources that fit in CircleCI "xlarge" class

View File

@ -13,7 +13,7 @@
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
var_1: &docker_image angular/ngcontainer:0.4.0
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-bust1-0.4.0
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.4.0
# Define common ENV vars
var_3: &define_env_vars
@ -48,7 +48,7 @@ jobs:
# Check BUILD.bazel formatting before we have a node_modules directory
# Then we don't need any exclude pattern to avoid checking those files
- run: 'buildifier -mode=check $(find . -type f \( -name "*.bzl" -or -name BUILD.bazel -or -name BUILD \)) ||
- run: 'buildifier -mode=check $(find . -type f \( -name BUILD.bazel -or -name BUILD \)) ||
(echo "BUILD files not formatted. Please run ''yarn buildifier''" ; exit 1)'
# Run the skylark linter to check our Bazel rules
# deprecated-api is disabled because we use actions.new_file(genfiles_dir)
@ -180,20 +180,12 @@ jobs:
- checkout:
<<: *post_checkout
# See remote cache documentation in /docs/BAZEL.md
- run: .circleci/setup_cache.sh
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
- *setup-bazel-remote-cache
- run: bazel run @nodejs//:yarn
- run:
# RBE is enabled by appending rbe-bazel.rc.
name: Enable RBE
command: 'sudo bash -c "cat .circleci/rbe-bazel.rc >> /etc/bazel.bazelrc"'
- run:
name: "Setup GCP environment"
command: 'openssl aes-256-cbc -d -in .circleci/gcp_token -k "${CIRCLE_PROJECT_REPONAME}" -out /home/circleci/.gcp_credentials'
- run:
name: build-packages-dist
command: scripts/build-packages-dist.sh
environment:
GOOGLE_APPLICATION_CREDENTIALS: /home/circleci/.gcp_credentials
- run: scripts/build-packages-dist.sh
# Save the npm packages from //packages/... for other workflow jobs to read
# https://circleci.com/docs/2.0/workflows/#using-workspaces-to-share-data-among-jobs
@ -301,4 +293,4 @@ workflows:
- master
notify:
webhooks:
- url: https://ngbuilds.io/circle-build
- url: https://ngbuilds.io/circle-build

Binary file not shown.

View File

@ -1,77 +0,0 @@
# These options are enabled when running on CI with Remote Build Execution.
################################################################
# Toolchain related flags for remote build execution. #
################################################################
# Remote Build Execution requires a strong hash function, such as SHA256.
startup --host_jvm_args=-Dbazel.DigestFunction=SHA256
# Depending on how many machines are in the remote execution instance, setting
# this higher can make builds faster by allowing more jobs to run in parallel.
# Setting it too high can result in jobs that timeout, however, while waiting
# for a remote machine to execute them.
build --jobs=150
# Set several flags related to specifying the platform, toolchain and java
# properties.
# These flags are duplicated rather than imported from (for example)
# %workspace%/configs/ubuntu16_04_clang/1.0/toolchain.bazelrc to make this
# bazelrc a standalone file that can be copied more easily.
# These flags should only be used as is for the rbe-ubuntu16-04 container
# and need to be adapted to work with other toolchain containers.
build --host_javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:jdk8
build --javabase=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:jdk8
build --host_java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
build --java_toolchain=@bazel_tools//tools/jdk:toolchain_hostjdk8
build --crosstool_top=@bazel_toolchains//configs/ubuntu16_04_clang/1.0/bazel_0.15.0/default:toolchain
build --action_env=BAZEL_DO_NOT_DETECT_CPP_TOOLCHAIN=1
# Platform flags:
# The toolchain container used for execution is defined in the target indicated
# by "extra_execution_platforms", "host_platform" and "platforms".
# If you are using your own toolchain container, you need to create a platform
# target with "constraint_values" that allow for the toolchain specified with
# "extra_toolchains" to be selected (given constraints defined in
# "exec_compatible_with").
# More about platforms: https://docs.bazel.build/versions/master/platforms.html
build --extra_toolchains=@bazel_toolchains//configs/ubuntu16_04_clang/1.0/bazel_0.15.0/cpp:cc-toolchain-clang-x86_64-default
build --extra_execution_platforms=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:rbe_ubuntu1604
build --host_platform=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:rbe_ubuntu1604
build --platforms=@bazel_toolchains//configs/ubuntu16_04_clang/1.0:rbe_ubuntu1604
# Set various strategies so that all actions execute remotely. Mixing remote
# and local execution will lead to errors unless the toolchain and remote
# machine exactly match the host machine.
build --spawn_strategy=remote
build --strategy=Javac=remote
build --strategy=Closure=remote
build --genrule_strategy=remote
build --define=EXECUTOR=remote
# Enable the remote cache so action results can be shared across machines,
# developers, and workspaces.
build --remote_cache=remotebuildexecution.googleapis.com
# Enable remote execution so actions are performed on the remote systems.
build --remote_executor=remotebuildexecution.googleapis.com
# Remote instance.
build --remote_instance_name=projects/internal-200822/instances/default_instance
# Enable encryption.
build --tls_enabled=true
# Enforce stricter environment rules, which eliminates some non-hermetic
# behavior and therefore improves both the remote cache hit rate and the
# correctness and repeatability of the build.
build --experimental_strict_action_env=true
# Set a higher timeout value, just in case.
build --remote_timeout=3600
# Enable authentication. This will pick up application default credentials by
# default. You can use --auth_credentials=some_file.json to use a service
# account credential instead.
build --auth_enabled=true
# Do not accept remote cache.
build --remote_accept_cached=false

View File

@ -1,16 +1,3 @@
<a name="7.0.0-beta.4"></a>
# [7.0.0-beta.4](https://github.com/angular/angular/compare/7.0.0-beta.3...7.0.0-beta.4) (2018-08-29)
### Bug Fixes
* **bazel:** Cache fileNameToModuleName lookups ([#25731](https://github.com/angular/angular/issues/25731)) ([f394ba0](https://github.com/angular/angular/commit/f394ba0))
* **bazel:** move bazel managed runtime deps for downstream usage ([#25690](https://github.com/angular/angular/issues/25690)) ([6ed7993](https://github.com/angular/angular/commit/6ed7993))
* **bazel:** only lookup amd module-name tags in .d.ts files ([#25710](https://github.com/angular/angular/issues/25710)) ([42072c4](https://github.com/angular/angular/commit/42072c4))
* **compiler:** update compiler to generate new slot allocations ([#25607](https://github.com/angular/angular/issues/25607)) ([27e2039](https://github.com/angular/angular/commit/27e2039))
<a name="6.1.6"></a>
## [6.1.6](https://github.com/angular/angular/compare/6.1.5...6.1.6) (2018-08-29)
@ -21,17 +8,7 @@
* **bazel:** only lookup amd module-name tags in .d.ts files ([#25710](https://github.com/angular/angular/issues/25710)) ([7aff364](https://github.com/angular/angular/commit/7aff364))
Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6 instead. sorry! :-)
<a name="7.0.0-beta.3"></a>
# [7.0.0-beta.3](https://github.com/angular/angular/compare/7.0.0-beta.2...7.0.0-beta.3) (2018-08-22)
### Features
* **router:** add UrlSegment[] to CanLoad interface ([#13127](https://github.com/angular/angular/issues/13127)) ([07d8d39](https://github.com/angular/angular/commit/07d8d39)), closes [#12411](https://github.com/angular/angular/issues/12411)
Note: the 1.6.5 release on npm accidentally glitched-out midway, so we cut 6.1.6 instead. sorry! :-)
<a name="6.1.4"></a>
## [6.1.4](https://github.com/angular/angular/compare/6.1.3...6.1.4) (2018-08-22)
@ -43,15 +20,6 @@ Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6
<a name="7.0.0-beta.2"></a>
# [7.0.0-beta.2](https://github.com/angular/angular/compare/7.0.0-beta.1...7.0.0-beta.2) (2018-08-15)
### Bug Fixes
* **bazel:** correct type concatenated to devmode_js ([#25467](https://github.com/angular/angular/issues/25467)) ([fb2c524](https://github.com/angular/angular/commit/fb2c524))
<a name="6.1.3"></a>
## [6.1.3](https://github.com/angular/angular/compare/6.1.2...6.1.3) (2018-08-15)
@ -62,24 +30,6 @@ Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6
<a name="7.0.0-beta.1"></a>
# [7.0.0-beta.1](https://github.com/angular/angular/compare/7.0.0-beta.0...7.0.0-beta.1) (2018-08-08)
### Bug Fixes
* **compiler-cli:** use the oldProgram option in watch mode ([#21364](https://github.com/angular/angular/issues/21364)) ([c6e5b97](https://github.com/angular/angular/commit/c6e5b97)), closes [#21361](https://github.com/angular/angular/issues/21361)
* **core:** In Testability.whenStable update callback, pass more complete ([#25010](https://github.com/angular/angular/issues/25010)) ([16c03c0](https://github.com/angular/angular/commit/16c03c0))
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([02e201a](https://github.com/angular/angular/commit/02e201a))
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ba1e25f](https://github.com/angular/angular/commit/ba1e25f)), closes [#20061](https://github.com/angular/angular/issues/20061)
### Features
* **core:** add DoBootstrap interface. ([#24558](https://github.com/angular/angular/issues/24558)) ([732026c](https://github.com/angular/angular/commit/732026c)), closes [#24557](https://github.com/angular/angular/issues/24557)
<a name="6.1.2"></a>
## [6.1.2](https://github.com/angular/angular/compare/6.1.1...6.1.2) (2018-08-08)
@ -90,26 +40,13 @@ Note: the 6.1.5 release on npm accidentally glitched-out midway, so we cut 6.1.6
* 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="7.0.0-beta.0"></a>
# [7.0.0-beta.0](https://github.com/angular/angular/compare/6.1.0...7.0.0-beta.0) (2018-08-02)
### Bug Fixes
* **bazel:** allow compile_strategy to be (privately) imported ([#25080](https://github.com/angular/angular/issues/25080)) ([0d1d589](https://github.com/angular/angular/commit/0d1d589))
* **compiler:** update compiler to flatten nested template fns ([#24943](https://github.com/angular/angular/issues/24943)) ([fe14f18](https://github.com/angular/angular/commit/fe14f18))
* **compiler-cli:** correct realPath to realpath. ([#25023](https://github.com/angular/angular/issues/25023)) ([01e6dab](https://github.com/angular/angular/commit/01e6dab))
* **core:** throw error message when @Output not initialized ([#19116](https://github.com/angular/angular/issues/19116)) ([adf510f](https://github.com/angular/angular/commit/adf510f)), closes [#3664](https://github.com/angular/angular/issues/3664)
### Features
* **compiler:** add "original" placeholder value on extracted XMB ([#25079](https://github.com/angular/angular/issues/25079)) ([e99d860](https://github.com/angular/angular/commit/e99d860))
<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))

View File

@ -10,30 +10,31 @@ http_archive(
sha256 = "2977cdbc8ae0eed7d4186385af56a50a3321a549e2136a959998bba89d2edb6e",
)
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/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.16.2.zip",
strip_prefix = "rules_typescript-0.16.2",
sha256 = "31601b777840fbf600dbd1893ade0d1de37166e7ba52b90735b107cfb67e38c7",
)
load("@build_bazel_rules_typescript//:package.bzl", "rules_typescript_dependencies")
rules_typescript_dependencies()
http_archive(
name = "bazel_toolchains",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-toolchains/archive/5124557861ebf4c0b67f98180bff1f8551e0b421.tar.gz",
"https://github.com/bazelbuild/bazel-toolchains/archive/5124557861ebf4c0b67f98180bff1f8551e0b421.tar.gz",
],
strip_prefix = "bazel-toolchains-5124557861ebf4c0b67f98180bff1f8551e0b421",
sha256 = "c3b08805602cd1d2b67ebe96407c1e8c6ed3d4ce55236ae2efe2f1948f38168d",
url = "https://github.com/bazelbuild/rules_typescript/archive/0.16.0.zip",
strip_prefix = "rules_typescript-0.16.0",
sha256 = "e65c5639a42e2f6d3f9d2bda62487d6b42734830dda45be1620c3e2b1115070c",
)
http_archive(
name = "io_bazel_rules_sass",
url = "https://github.com/bazelbuild/rules_sass/archive/1.11.0.zip",
strip_prefix = "rules_sass-1.11.0",
sha256 = "dbe9fb97d5a7833b2a733eebc78c9c1e3880f676ac8af16e58ccf2139cbcad03",
name = "io_bazel_rules_go",
url = "https://github.com/bazelbuild/rules_go/releases/download/0.10.3/rules_go-0.10.3.tar.gz",
sha256 = "feba3278c13cde8d67e341a837f69a029f698d7a27ddbb2a202be7a10b22142a",
)
# This commit matches the version of buildifier in angular/ngcontainer
@ -56,14 +57,6 @@ http_archive(
sha256 = "e373d2ae24955c1254c495c9c421c009d88966565c35e4e8444c082cb1f0f48f",
)
http_archive(
name = "io_bazel_skydoc",
# TODO: switch to upstream when https://github.com/bazelbuild/skydoc/pull/103 is merged
url = "https://github.com/alexeagle/skydoc/archive/fe2e9f888d28e567fef62ec9d4a93c425526d701.zip",
strip_prefix = "skydoc-fe2e9f888d28e567fef62ec9d4a93c425526d701",
sha256 = "7bfb5545f59792a2745f2523b9eef363f9c3e7274791c030885e7069f8116016",
)
# We have a source dependency on the Devkit repository, because it's built with
# Bazel.
# This allows us to edit sources and have the effect appear immediately without
@ -79,9 +72,9 @@ http_archive(
http_archive(
name = "org_brotli",
url = "https://github.com/google/brotli/archive/v1.0.5.zip",
strip_prefix = "brotli-1.0.5",
sha256 = "774b893a0700b0692a76e2e5b7e7610dbbe330ffbe3fe864b4b52ca718061d5a",
url = "https://github.com/google/brotli/archive/f9b8c02673c576a3e807edbf3a9328e9e7af6d7c.zip",
strip_prefix = "brotli-f9b8c02673c576a3e807edbf3a9328e9e7af6d7c",
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
)
#
@ -104,7 +97,7 @@ local_repository(
# Load and install our dependencies downloaded above.
#
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories")
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
check_bazel_version("0.16.0", """
If you are on a Mac and using Homebrew, there is a breaking change to the installation in Bazel 0.16
@ -137,11 +130,20 @@ load("@angular//:index.bzl", "ng_setup_workspace")
ng_setup_workspace()
##################################
# Skylark documentation generation
#
# Ask Bazel to manage these toolchain dependencies for us.
# Bazel will run `yarn install` when one of these toolchains is requested during
# a build.
#
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
sass_repositories()
yarn_install(
name = "ts-api-guardian_runtime_deps",
package_json = "//tools/ts-api-guardian:package.json",
yarn_lock = "//tools/ts-api-guardian:yarn.lock",
)
load("@io_bazel_skydoc//skylark:skylark.bzl", "skydoc_repositories")
skydoc_repositories()
yarn_install(
name = "http-server_runtime_deps",
package_json = "//tools/http-server:package.json",
yarn_lock = "//tools/http-server:yarn.lock",
)

View File

@ -22,8 +22,8 @@ Here are the most important tasks you might need to use:
* `yarn start` - run a development web server that watches the files; then builds the doc-viewer and reloads the page, as necessary.
* `yarn serve-and-sync` - run both the `docs-watch` and `start` in the same console.
* `yarn lint` - check that the doc-viewer code follows our style rules.
* `yarn test` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
* `yarn test --watch=false` - run all the unit tests once.
* `yarn test` - run all the unit tests once.
* `yarn test --watch` - watch all the source files, for the doc-viewer, and run all the unit tests when any change.
* `yarn e2e` - run all the e2e tests for the doc-viewer.
* `yarn docs` - generate all the docs from the source files.
@ -56,9 +56,14 @@ It's necessary to remove the temporary files, because otherwise they're displaye
## Using ServiceWorker locally
Running `yarn start` (even when explicitly targeting production mode) does not set up the
ServiceWorker. If you want to test the ServiceWorker locally, you can use `yarn build` and then
serve the files in `dist/` with `yarn http-server dist -p 4200`.
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
would require manually running `yarn sw-manifest` and `yarn sw-copy` (something that is not possible
with webpack serving the files from memory).
If you want to test ServiceWorker locally, you can use `yarn build` and serve the files in `dist/`
with `yarn http-server dist -p 4200`.
For more details see #16745.
## Guide to authoring

View File

@ -33,6 +33,7 @@
"src/assets",
"src/generated",
"src/app/search/search-worker.js",
"src/favicon.ico",
"src/pwa-manifest.json",
"src/google385281288605d160.html",
{
@ -61,8 +62,7 @@
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.next.ts"
}
],
"serviceWorker": true
]
},
"stable": {
"fileReplacements": [
@ -70,8 +70,7 @@
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.stable.ts"
}
],
"serviceWorker": true
]
},
"archive": {
"fileReplacements": [
@ -79,8 +78,7 @@
"src": "src/environments/environment.ts",
"replaceWith": "src/environments/environment.archive.ts"
}
],
"serviceWorker": true
]
}
}
},
@ -125,6 +123,7 @@
"src/assets",
"src/generated",
"src/app/search/search-worker.js",
"src/favicon.ico",
"src/pwa-manifest.json",
"src/google385281288605d160.html",
{

View File

@ -36,7 +36,3 @@
<div class="di-component">
<app-parent-finder></app-parent-finder>
</div>
<div class="di-component">
<app-storage></app-storage>
</div>

View File

@ -9,6 +9,9 @@ import { UserService } from './user.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
// #docregion providers
providers: [ LoggerService, UserContextService, UserService ]
// #enddocregion providers
})
export class AppComponent {
// #enddocregion import-services

View File

@ -31,7 +31,6 @@ import { ParentFinderComponent,
BarryComponent,
BethComponent,
BobComponent } from './parent-finder.component';
import { StorageComponent } from './storage.component';
const declarations = [
AppComponent,
@ -64,7 +63,6 @@ const c_components = [
a_components,
b_components,
c_components,
StorageComponent,
],
bootstrap: [ AppComponent ],
// #docregion providers

View File

@ -5,9 +5,7 @@ import { Injectable } from '@angular/core';
import { LoggerService } from './logger.service';
// #docregion date-logger-service
@Injectable({
providedIn: 'root'
})
@Injectable()
// #docregion date-logger-service-signature
export class DateLoggerService extends LoggerService
// #enddocregion date-logger-service-signature

View File

@ -2,9 +2,7 @@
import { Injectable } from '@angular/core';
import { Hero } from './hero';
@Injectable({
providedIn: 'root'
})
@Injectable()
export class HeroService {
// TODO: move to database

View File

@ -1,9 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
@Injectable()
export class LoggerService {
logs: string[] = [];

View File

@ -1,38 +0,0 @@
// #docregion
import { Component, OnInit, Self, SkipSelf } from '@angular/core';
import { BROWSER_STORAGE, BrowserStorageService } from './storage.service';
@Component({
selector: 'app-storage',
template: `
Open the inspector to see the local/session storage keys:
<h3>Session Storage</h3>
<button (click)="setSession()">Set Session Storage</button>
<h3>Local Storage</h3>
<button (click)="setLocal()">Set Local Storage</button>
`,
providers: [
BrowserStorageService,
{ provide: BROWSER_STORAGE, useFactory: () => sessionStorage }
]
})
export class StorageComponent implements OnInit {
constructor(
@Self() private sessionStorageService: BrowserStorageService,
@SkipSelf() private localStorageService: BrowserStorageService,
) { }
ngOnInit() {
}
setSession() {
this.sessionStorageService.set('hero', 'Mr. Nice - Session');
}
setLocal() {
this.localStorageService.set('hero', 'Mr. Nice - Local');
}
}

View File

@ -1,34 +0,0 @@
// #docregion
import { Inject, Injectable, InjectionToken } from '@angular/core';
// #docregion storage-token
export const BROWSER_STORAGE = new InjectionToken<Storage>('Browser Storage', {
providedIn: 'root',
factory: () => localStorage
});
// #enddocregion storage-token
// #docregion inject-storage-token
@Injectable({
providedIn: 'root'
})
export class BrowserStorageService {
constructor(@Inject(BROWSER_STORAGE) public storage: Storage) {}
get(key: string) {
this.storage.getItem(key);
}
set(key: string, value: string) {
this.storage.setItem(key, value);
}
remove(key: string) {
this.storage.removeItem(key);
}
clear() {
this.storage.clear();
}
}
// #enddocregion inject-storage-token

View File

@ -6,9 +6,7 @@ import { LoggerService } from './logger.service';
import { UserService } from './user.service';
// #docregion injectables, injectable
@Injectable({
providedIn: 'root'
})
@Injectable()
export class UserContextService {
// #enddocregion injectables, injectable
name: string;

View File

@ -1,9 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
@Injectable()
export class UserService {
getUserById(userId: number): any {

View File

@ -1,10 +1,11 @@
// #docregion
import { Injectable } from '@angular/core';
import { HEROES } from './mock-heroes';
import { HEROES } from './mock-heroes';
@Injectable({
// we declare that this service should be created
// by the root application injector.
providedIn: 'root',
})
export class HeroService {

View File

@ -1,11 +1,12 @@
// #docregion
import { Injectable } from '@angular/core';
import { HeroModule } from './hero.module';
import { HEROES } from './mock-heroes';
import { HEROES } from './mock-heroes';
@Injectable({
// we declare that this service should be created
// by any injector that includes HeroModule.
providedIn: HeroModule,
})
export class HeroService {

View File

@ -1,7 +1,6 @@
// #docplaster
// #docregion, v1
import { Component } from '@angular/core';
// #enddocregion v1
import { HeroService } from './hero.service';

View File

@ -1,9 +1,7 @@
// #docregion
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
@Injectable()
export class Logger {
logs: string[] = []; // capture logs for testing

View File

@ -11,9 +11,7 @@ export class User {
let alice = new User('Alice', true);
let bob = new User('Bob', false);
@Injectable({
providedIn: 'root'
})
@Injectable()
export class UserService {
user = bob; // initial user is Bob

View File

@ -3,18 +3,23 @@ import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { HeroTaxReturnComponent } from './hero-tax-return.component';
import { HeroesListComponent } from './heroes-list.component';
import { VillainsListComponent } from './villains-list.component';
import { AppComponent } from './app.component';
import { HeroTaxReturnComponent } from './hero-tax-return.component';
import { HeroesListComponent } from './heroes-list.component';
import { HeroesService } from './heroes.service';
import { VillainsListComponent } from './villains-list.component';
import { carComponents } from './car.components';
import { carComponents, carServices } from './car.components';
@NgModule({
imports: [
BrowserModule,
FormsModule
],
providers: [
carServices,
HeroesService
],
declarations: [
AppComponent,
carComponents,

View File

@ -21,17 +21,13 @@ export class Tires {
}
//// Engine services ///
@Injectable({
providedIn: 'root'
})
@Injectable()
export class EngineService {
id = 'E1';
getEngine() { return new Engine(); }
}
@Injectable({
providedIn: 'root'
})
@Injectable()
export class EngineService2 {
id = 'E2';
getEngine() {
@ -42,18 +38,14 @@ export class EngineService2 {
}
//// Tire services ///
@Injectable({
providedIn: 'root'
})
@Injectable()
export class TiresService {
id = 'T1';
getTires() { return new Tires(); }
}
/// Car Services ///
@Injectable({
providedIn: 'root'
})
@Injectable()
export class CarService {
id = 'C1';
constructor(
@ -71,9 +63,7 @@ export class CarService {
}
}
@Injectable({
providedIn: 'root'
})
@Injectable()
export class CarService2 extends CarService {
id = 'C2';
constructor(
@ -88,9 +78,7 @@ export class CarService2 extends CarService {
}
}
@Injectable({
providedIn: 'root'
})
@Injectable()
export class CarService3 extends CarService2 {
id = 'C3';
constructor(

View File

@ -13,19 +13,17 @@ import { HeroTaxReturnService } from './hero-tax-return.service';
})
export class HeroTaxReturnComponent {
message = '';
@Output() close = new EventEmitter<void>();
get taxReturn(): HeroTaxReturn {
return this.heroTaxReturnService.taxReturn;
}
@Input()
set taxReturn (htr: HeroTaxReturn) {
this.heroTaxReturnService.taxReturn = htr;
}
constructor(private heroTaxReturnService: HeroTaxReturnService) { }
constructor(private heroTaxReturnService: HeroTaxReturnService ) { }
onCanceled() {
this.flashMessage('Canceled');

View File

@ -4,9 +4,7 @@ import { Observable, Observer } from 'rxjs';
import { Hero, HeroTaxReturn } from './hero';
@Injectable({
providedIn: 'root'
})
@Injectable()
export class HeroesService {
heroes: Hero[] = [
{ id: 1, name: 'RubberMan', tid: '082-27-5678'},

File diff suppressed because it is too large Load Diff

View File

@ -1,312 +0,0 @@
# Navigate the component tree with DI
Application components often need to share information.
You can often use loosely coupled techniques for sharing information,
such as data binding and service sharing,
but sometimes it makes sense for one component to have a direct reference to another component.
You need a direct reference, for instance, to access values or call methods on that component.
Obtaining a component reference is a bit tricky in Angular.
Angular components themselves do not have a tree that you can
inspect or navigate programmatically. The parent-child relationship is indirect,
established through the components' [view objects](guide/glossary#view).
Each component has a *host view*, and can have additional *embedded views*.
An embedded view in component A is the
host view of component B, which can in turn have embedded view.
This means that there is a [view hierarchy](guide/glossary#view-hierarchy) for each component,
of which that component's host view is the root.
There is an API for navigating *down* the view hierarchy.
Check out `Query`, `QueryList`, `ViewChildren`, and `ContentChildren`
in the [API Reference](api/).
There is no public API for acquiring a parent reference.
However, because every component instance is added to an injector's container,
you can use Angular dependency injection to reach a parent component.
This section describes some techniques for doing that.
{@a find-parent}
{@a known-parent}
### Find a parent component of known type
You use standard class injection to acquire a parent component whose type you know.
In the following example, the parent `AlexComponent` has several children including a `CathyComponent`:
{@a alex}
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-1" title="parent-finder.component.ts (AlexComponent v.1)" linenums="false">
</code-example>
*Cathy* reports whether or not she has access to *Alex*
after injecting an `AlexComponent` into her constructor:
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="cathy" title="parent-finder.component.ts (CathyComponent)" linenums="false">
</code-example>
Notice that even though the [@Optional](guide/dependency-injection-in-action#optional) qualifier
is there for safety,
the <live-example name="dependency-injection-in-action"></live-example>
confirms that the `alex` parameter is set.
{@a base-parent}
### Unable to find a parent by its base class
What if you *don't* know the concrete parent component class?
A re-usable component might be a child of multiple components.
Imagine a component for rendering breaking news about a financial instrument.
For business reasons, this news component makes frequent calls
directly into its parent instrument as changing market data streams by.
The app probably defines more than a dozen financial instrument components.
If you're lucky, they all implement the same base class
whose API your `NewsComponent` understands.
<div class="alert is-helpful">
Looking for components that implement an interface would be better.
That's not possible because TypeScript interfaces disappear
from the transpiled JavaScript, which doesn't support interfaces.
There's no artifact to look for.
</div>
This isn't necessarily good design.
This example is examining *whether a component can
inject its parent via the parent's base class*.
The sample's `CraigComponent` explores this question. [Looking back](#alex),
you see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" title="parent-finder.component.ts (Alex class signature)" linenums="false">
</code-example>
The `CraigComponent` tries to inject `Base` into its `alex` constructor parameter and reports if it succeeded.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="craig" title="parent-finder.component.ts (CraigComponent)" linenums="false">
</code-example>
Unfortunately, this does'nt work.
The <live-example name="dependency-injection-in-action"></live-example>
confirms that the `alex` parameter is null.
*You cannot inject a parent by its base class.*
{@a class-interface-parent}
### Find a parent by its class interface
You can find a parent component with a [class interface](guide/dependency-injection-in-action#class-interface).
The parent must cooperate by providing an *alias* to itself in the name of a class interface token.
Recall that Angular always adds a component instance to its own injector;
that's why you could inject *Alex* into *Cathy* [earlier](#known-parent).
Write an [*alias provider*](guide/dependency-injection-in-action#useexisting)&mdash;a `provide` object literal with a `useExisting`
definition&mdash;that creates an *alternative* way to inject the same component instance
and add that provider to the `providers` array of the `@Component()` metadata for the `AlexComponent`.
{@a alex-providers}
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" title="parent-finder.component.ts (AlexComponent providers)" linenums="false">
</code-example>
[Parent](#parent-token) is the provider's class interface token.
The [*forwardRef*](guide/dependency-injection-in-action#forwardref) breaks the circular reference you just created by having the `AlexComponent` refer to itself.
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter,
the same way you've done it before.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-class" title="parent-finder.component.ts (CarolComponent class)" linenums="false">
</code-example>
Here's *Alex* and family in action.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/alex.png" alt="Alex in action">
</figure>
{@a parent-tree}
### Find a parent in a tree with _@SkipSelf()_
Imagine one branch of a component hierarchy: *Alice* -> *Barry* -> *Carol*.
Both *Alice* and *Barry* implement the `Parent' class interface.
*Barry* is the problem. He needs to reach his parent, *Alice*, and also be a parent to *Carol*.
That means he must both *inject* the `Parent` class interface to get *Alice* and
*provide* a `Parent` to satisfy *Carol*.
Here's *Barry*.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry" title="parent-finder.component.ts (BarryComponent)" linenums="false">
</code-example>
*Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers).
If you're going to keep writing [*alias providers*](guide/dependency-injection-in-action#useexisting) like this you should create a [helper function](#provideparent).
For now, focus on *Barry*'s constructor.
<code-tabs>
<code-pane title="Barry's constructor" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="barry-ctor">
</code-pane>
<code-pane title="Carol's constructor" path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="carol-ctor">
</code-pane>
</code-tabs>
It's identical to *Carol*'s constructor except for the additional `@SkipSelf` decorator.
`@SkipSelf` is essential for two reasons:
1. It tells the injector to start its search for a `Parent` dependency in a component *above* itself,
which *is* what parent means.
2. Angular throws a cyclic dependency error if you omit the `@SkipSelf` decorator.
`Cannot instantiate cyclic dependency! (BethComponent -> Parent -> BethComponent)`
Here's *Alice*, *Barry*, and family in action.
<figure>
<img src="generated/images/guide/dependency-injection-in-action/alice.png" alt="Alice in action">
</figure>
{@a parent-token}
### Parent class interface
You [learned earlier](guide/dependency-injection-in-action#class-interface) that a class interface is an abstract class used as an interface rather than as a base class.
The example defines a `Parent` class interface.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="parent" title="parent-finder.component.ts (Parent class-interface)" linenums="false">
</code-example>
The `Parent` class interface defines a `name` property with a type declaration but *no implementation*.
The `name` property is the only member of a parent component that a child component can call.
Such a narrow interface helps decouple the child component class from its parent components.
A component that could serve as a parent *should* implement the class interface as the `AliceComponent` does.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-class-signature" title="parent-finder.component.ts (AliceComponent class signature)" linenums="false">
</code-example>
Doing so adds clarity to the code. But it's not technically necessary.
Although `AlexComponent` has a `name` property, as required by its `Base` class,
its class signature doesn't mention `Parent`.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-class-signature" title="parent-finder.component.ts (AlexComponent class signature)" linenums="false">
</code-example>
<div class="alert is-helpful">
`AlexComponent` *should* implement `Parent` as a matter of proper style.
It doesn't in this example *only* to demonstrate that the code will compile and run without the interface.
</div>
{@a provideparent}
### `provideParent()` helper function
Writing variations of the same parent *alias provider* gets old quickly,
especially this awful mouthful with a [*forwardRef*](guide/dependency-injection-in-action#forwardref).
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alex-providers" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
You can extract that logic into a helper function like the following.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-the-parent" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
Now you can add a simpler, more meaningful parent provider to your components.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="alice-providers" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
You can do better. The current version of the helper function can only alias the `Parent` class interface.
The application might have a variety of parent types, each with its own class interface token.
Here's a revised version that defaults to `parent` but also accepts an optional second parameter for a different parent class interface.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="provide-parent" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>
And here's how you could use it with a different parent type.
<code-example path="dependency-injection-in-action/src/app/parent-finder.component.ts" region="beth-providers" title="dependency-injection-in-action/src/app/parent-finder.component.ts" linenums="false">
</code-example>

View File

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

View File

@ -1,353 +0,0 @@
# Dependency Providers
A dependency [provider](guide/glossary#provider) configures an injector
with a [DI token](guide/glossary#di-token),
which that injector uses to provide the concrete, runtime version of a dependency value.
The injector relies on the provider configuration to create instances of the dependencies
that it injects into components, directives, pipes, and other services.
You must configure an injector with a provider, or it won't know how to create the dependency.
The most obvious way for an injector to create an instance of a service class is with the class itself.
If you specify the service class itself as the provider token, the default behavior is for the injector to instantiate that class with `new`.
In the following typical example, the `Logger` class itself provides a `Logger` instance.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example>
You can, however, configure an injector with an alternative provider,
in order to deliver some other object that provides the needed logging functionality.
For instance:
* You can provide a substitute class.
* You can provide a logger-like object.
* Your provider can call a logger factory function.
{@a provide}
## The `Provider` object literal
The class-provider syntax is a shorthand expression that expands
into a provider configuration, defined by the [`Provider` interface](api/core/Provider).
The following code snippets shows how a class that is given as the `providers` value is expanded into a full provider object.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3" >
</code-example>
The expanded provider configuration is an object literal with two properties.
* The `provide` property holds the [token](guide/dependency-injection#token)
that serves as the key for both locating a dependency value and configuring the injector.
* The second property is a provider definition object, which tells the injector how to create the dependency value.
The provider-definition key can be `useClass`, as in the example.
It can also be `useExisting`, `useValue`, or `useFactory`.
Each of these keys provides a different type of dependency, as discussed below.
{@a class-provider}
## Alternative class providers
Different classes can provide the same service.
For example, the following code tells the injector
to return a `BetterLogger` instance when the component asks for a logger
using the `Logger` token.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4" >
</code-example>
{@a class-provider-dependencies}
### Class providers with dependencies
Another class, `EvenBetterLogger`, might display the user name in the log message.
This logger gets the user from an injected `UserService` instance.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example>
The injector needs providers for both this new logging service and its dependent `UserService`. Configure this alternative logger with the `useClass` provider-definition key, like `BetterLogger`. The following array specifies both providers in the `providers` metadata option of the parent module or component.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</code-example>
{@a aliased-class-providers}
### Aliased class providers
Suppose an old component depends upon the `OldLogger` class.
`OldLogger` has the same interface as `NewLogger`, but for some reason
you can't update the old component to use it.
When the old component logs a message with `OldLogger`,
you want the singleton instance of `NewLogger` to handle it instead.
In this case, the dependency injector should inject that singleton instance
when a component asks for either the new or the old logger.
`OldLogger` should be an *alias* for `NewLogger`.
If you try to alias `OldLogger` to `NewLogger` with `useClass`, you end up with two different `NewLogger` instances in your app.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example>
To make sure there is only one instance of `NewLogger`, alias `OldLogger` with the `useExisting` option.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</code-example>
{@a value-provider}
## Value providers
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
To inject an object you have already created,
configure the injector with the `useValue` option
The following code defines a variable that creates such an object to play the logger role.
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
</code-example>
The following provider object uses the `useValue` key to associate the variable with the `Logger` token.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
</code-example>
{@a non-class-dependencies}
### Non-class dependencies
Not all dependencies are classes.
Sometimes you want to inject a string, function, or object.
Apps often define configuration objects with lots of small facts,
like the title of the application or the address of a web API endpoint.
These configuration objects aren't always instances of a class.
They can be object literals, as shown in the following example.
<code-example path="dependency-injection/src/app/app.config.ts" region="config" title="src/app/app.config.ts (excerpt)" linenums="false">
</code-example>
{@a interface-not-valid-token}
**TypeScript interfaces are not valid tokens**
The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
Unfortunately, you cannot use a TypeScript interface as a token.
In TypeScript, an interface is a design-time artifact, and doesn't have a runtime representation (token) that the DI framework can use.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example>
<div class="alert-is-helpful">
This might seem strange if you're used to dependency injection in strongly typed languages where an interface is the preferred dependency lookup key.
However, JavaScript, doesn't have interfaces, so when TypeScript is transpiled to JavaScript, the interface disappears.
There is no interface type information left for Angular to find at runtime.
</div>
One alternative is to provide and inject the configuration object in an NgModule like `AppModule`.
<code-example path="dependency-injection/src/app/app.module.ts" region="providers" title="src/app/app.module.ts (providers)"></code-example>
Another solution to choosing a provider token for non-class dependencies is
to define and use an `InjectionToken` object.
The following example shows how to define such a token.
<code-example path="dependency-injection/src/app/app.config.ts" region="token" title="src/app/app.config.ts" linenums="false">
</code-example>
The type parameter, while optional, conveys the dependency's type to developers and tooling.
The token description is another developer aid.
Register the dependency provider using the `InjectionToken` object:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9" linenums="false">
</code-example>
Now you can inject the configuration object into any constructor that needs it, with
the help of an `@Inject()` parameter decorator.
<code-example path="dependency-injection/src/app/app.component.2.ts" region="ctor" title="src/app/app.component.ts" linenums="false">
</code-example>
<div class="alert-is-helpful">
Although the `AppConfig` interface plays no role in dependency injection,
it supports typing of the configuration object within the class.
</div>
{@a factory-provider}
{@a factory-providers}
## Factory providers
Sometimes you need to create a dependent value dynamically,
based on information you won't have until run time.
For example, you might need information that changes repeatedly in the course of the browser session.
Also, your injectable service might not have independent access to the source of the information.
In cases like this you can use a *factory provider*.
Factory providers can also be useful when creating an instance of a dependency from
a third-party library that wasn't designed to work with DI.
For example, suppose `HeroService` must hide *secret* heroes from normal users.
Only authorized users should see secret heroes.
Like `EvenBetterLogger`, `HeroService` needs to know if the user is authorized to see secret heroes.
That authorization can change during the course of a single application session,
as when you log in a different user.
Let's say you don't want to inject `UserService` directly into `HeroService`, because you don't want to complicate that service with security-sensitive information.
`HeroService` won't have direct access to the user information to decide
who is authorized and who isn't.
To resolve this, we give the `HeroService` constructor a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" title="src/app/heroes/hero.service.ts (excerpt)" linenums="false">
</code-example>
You can inject `Logger`, but you can't inject the `isAuthorized` flag. Instead, you can use a factory provider to create a new logger instance for `HeroService`.
A factory provider needs a factory function.
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
Although `HeroService` has no access to `UserService`, the factory function does.
You inject both `Logger` and `UserService` into the factory provider
and let the injector pass them along to the factory function.
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
* The `useFactory` field tells Angular that the provider is a factory function whose implementation is `heroServiceFactory`.
* The `deps` property is an array of [provider tokens](guide/dependency-injection#token).
The `Logger` and `UserService` classes serve as tokens for their own class providers.
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
Notice that you captured the factory provider in an exported variable, `heroServiceProvider`.
This extra step makes the factory provider reusable.
You can configure a provider of `HeroService` with this variable wherever you need it.
In this sample, you need it only in `HeroesComponent`,
where `heroServiceProvider` replaces `HeroService` in the metadata `providers` array.
The following shows the new and the old implementations side-by-side.
<code-tabs>
<code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts">
</code-pane>
<code-pane title="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts">
</code-pane>
</code-tabs>
## Predefined tokens and multiple providers
Angular provides a number of built-in injection-token constants that you can use to customize the behavior of
various systems.
For example, you can use the following built-in tokens as hooks into the frameworks bootstrapping and initialization process.
A provider object can associate any of these injection tokens with one or more callback functions that take app-specific initialization actions.
* [PLATFORM_INITIALIZER](api/core/PLATFORM_INITIALIZER): Callback is invoked when a platform is initialized.
* [APP_BOOTSTRAP_LISTENER](api/core/APP_BOOTSTRAP_LISTENER): Callback is invoked for each component that is bootstrapped. The handler function receives the ComponentRef instance of the bootstrapped component.
* [APP_INITIALIZER](api/core/APP_INITIALIZER): Callback is invoked before an app is initialized. All registered initializers can optionally return a Promise. All initializer functions that return Promises must be resolved before the application is bootstrapped. If one of the initializers fails to resolves, the application is not bootstrapped.
The provider object can have a third option, `multi: true`, which you can use with `APP_INITIALIZER`
to register multiple handlers for the provide event.
For example, when bootstrapping an application, you can register many initializers using the same token.
```
export const APP_TOKENS = [
{ provide: PLATFORM_INITIALIZER, useFactory: platformInitialized, multi: true },
{ provide: APP_INITIALIZER, useFactory: delayBootstrapping, multi: true },
{ provide: APP_BOOTSTRAP_LISTENER, useFactory: appBootstrapped, multi: true },
];
```
Multiple providers can be associated with a single token in other areas as well.
For example, you can register a custom form validator using the built-in [NG_VALIDATORS](api/forms/NG_VALIDATORS) token,
and provide multiple instances of a given validator provider by using the `multi: true` property in the provider object.
Angular adds your custom validators to the existing collection.
The Router also makes use of multiple providers associated with a single token.
When you provide multiple sets of routes using [RouterModule.forRoot](api/router/RouterModule#forroot)
and [RouterModule.forChild](api/router/RouterModule#forchild) in a single module,
the [ROUTES](api/router/ROUTES) token combines all the different provided sets of routes into a single value.
<div class="alert-is-helpful>
Search for [Constants in API documentation](api?type=const) to find more built-in tokens.
</div>
{@a tree-shakable-provider}
{@a tree-shakable-providers}
## Tree-shakable providers
Tree shaking refers to a compiler option that removes code from the final bundle if that code not referenced in an application.
When providers are tree-shakable, the Angular compiler removes the associated
services from the final output when it determines that they are not used in your application.
This significantly reduces the size of your bundles.
<div class="alert-is-helpful">
Ideally, if an application isn't injecting a service, it shouldn't be included in the final output.
However, Angular has to be able to identify at build time whether the service will be required or not.
Because it's always possible to inject a service directly using `injector.get(Service)`,
Angular can't 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.
Thus, services provided at the NgModule or component level are not tree-shakable.
</div>
The following example of non-tree-shakable providers in Angular configures a service provider for the injector of an NgModule.
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" title="src/app/tree-shaking/service-and-modules.ts" linenums="false"> </code-example>
This module can then be imported into your application module
to make the service available for injection in your app,
as shown in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" title="src/app/tree-shaking/app.modules.ts" linenums="false"> </code-example>
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 here because Angular can't 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
You can make a provider tree-shakable by specifying it in the `@Injectable()` decorator on the service itself, rather than in the metadata for the NgModule or component that depends on the service.
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>
The service can be instantiated by configuring a factory function, as in the following example.
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" title="src/app/tree-shaking/service.0.ts" linenums="false"> </code-example>
<div class="alert-is-helpful">
To override a tree-shakable provider, configure the injector of a specific NgModule or component with another provider, using the `providers: []` array syntax of the `@NgModule()` or `@Component()` decorator.
</div>

View File

@ -1,25 +1,23 @@
# Dependency Injection in Angular
# Angular Dependency Injection
Dependency injection (DI), is an important application design pattern.
Angular has its own DI framework, which is typically
used in the design of Angular applications to increase their efficiency and modularity.
**Dependency Injection (DI)** is a way to create objects that depend upon other objects.
A Dependency Injection system supplies the dependent objects (called the _dependencies_)
when it creates an instance of an object.
Dependencies are services or objects that a class needs to perform its function.
DI is a coding pattern in which a class asks for dependencies from external sources rather than creating them itself.
The [Dependency Injection pattern](guide/dependency-injection-pattern) page describes this general approach.
_The guide you're reading now_ explains how Angular's own Dependency Injection system works.
In Angular, the DI framework provides declared dependencies to a class when that class is instantiated. This guide explains how DI works in Angular, and how you use it to make your apps flexible, efficient, and robust, as well as testable and maintainable.
## DI by example
<div class="alert-is-helpful">
You can run the <live-example></live-example> of the sample app that accompanies this guide.
</div>
You'll learn Angular Dependency Injection through a discussion of the sample app that accompanies this guide.
Run the <live-example></live-example> anytime.
Start by reviewing this simplified version of the _heroes_ feature
from the [The Tour of Heroes](tutorial/). This simple version doesn't use DI; we'll walk through converting it to do so.
from the [The Tour of Heroes](tutorial/).
<code-tabs>
<code-pane title="src/app/heroes/heroes.component.ts" path="dependency-injection/src/app/heroes/heroes.component.1.ts" region="v1">
<code-pane title="src/app/heroes/heroes.component.ts" path="dependency-injection/src/app/heroes/heroes.component.1.ts"
region="v1">
</code-pane>
<code-pane title="src/app/heroes/hero-list.component.ts" path="dependency-injection/src/app/heroes/hero-list.component.1.ts">
@ -33,40 +31,26 @@ from the [The Tour of Heroes](tutorial/). This simple version doesn't use DI; we
</code-tabs>
`HeroesComponent` is the top-level heroes component.
Its only purpose is to display `HeroListComponent`, which displays a list of hero names.
The `HeroesComponent` is the top-level heroes component.
Its only purpose is to display the `HeroListComponent`
which displays a list of hero names.
This version of the `HeroListComponent` gets heroes from the `HEROES` array, an in-memory collection
This version of the `HeroListComponent` gets its `heroes` from the `HEROES` array, an in-memory collection
defined in a separate `mock-heroes` file.
<code-example title="src/app/heroes/hero-list.component.ts (class)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts" region="class">
<code-example title="src/app/heroes/hero-list.component.ts (class)" path="dependency-injection/src/app/heroes/hero-list.component.1.ts"
region="class">
</code-example>
This approach works for prototyping, but is not robust or maintainable.
That may suffice in the early stages of development, but it's far from ideal.
As soon as you try to test this component or get heroes from a remote server,
you have to change the implementation of `HeroesListComponent` and
replace every use of the `HEROES` mock data.
you'll have to change the implementation of `HerosListComponent` and
replace every other use of the `HEROES` mock data.
It's better to hide these details inside a _service_ class,
[defined in its own file](#one-class-per-file).
## Create and register an injectable service
The DI framework lets you supply data to a component from an injectable _service_ class, defined in its own file. To demonstrate, we'll create an injectable service class that provides a list of heroes, and register that class as a provider of that service.
<div class="alert-is-helpful">
Having multiple classes in the same file can be confusing. We generally recommend that you define components and services in separate files.
If you do combine a component and service in the same file,
it is important to define the service first, and then the component. If you define the component before the service, you get a run-time null reference error.
It is possible to define the component first with the help of the `forwardRef()` method as explained in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
You can also use forward references to break circular dependencies.
See an example in the [DI Cookbook](guide/dependency-injection-in-action#forwardref).
</div>
### Create an injectable service class
## Create an injectable _HeroService_
The [**Angular CLI**](https://cli.angular.io/) can generate a new `HeroService` class in the `src/app/heroes` folder with this command.
@ -74,84 +58,412 @@ The [**Angular CLI**](https://cli.angular.io/) can generate a new `HeroService`
ng generate service heroes/hero
</code-example>
The command creates the following `HeroService` skeleton.
The command above creates the following `HeroService` skeleton.
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" title="src/app/heroes/hero.service.ts (CLI-generated)">
</code-example>
The `@Injectable()` is an essential ingredient in every Angular service definition. The rest of the class has been written to expose a `getHeroes` method that returns the same mock data as before. (A real app would probably get its data asynchronously from a remote server, but we'll ignore that to focus on the mechanics of injecting the service.)
The `@Injectable` decorator is an essential ingredient in every Angular service definition.
The rest of the class has been rewritten to expose a `getHeroes` method
that returns the same mock data as before.
<code-example path="dependency-injection/src/app/heroes/hero.service.3.ts" title="src/app/heroes/hero.service.ts">
<code-example path="dependency-injection/src/app/heroes/hero.service.3.ts" title="src/app/heroes/hero.service.3.ts">
</code-example>
Of course, this isn't a real data service.
If the app were actually getting data from a remote server,
the `getHeroes` method signature would have to be asynchronous.
That's a defect we can safely ignore in this guide where our focus is on
_injecting the service_ into the `HeroList` component.
{@a injector-config}
{@a bootstrap}
### Configure an injector with a service provider
## Injectors
The class we have created provides a service. The `@Injectable()` decorator marks it as a service
that can be injected, but Angular can't actually inject it anywhere until you configure
an Angular [dependency injector](guide/glossary#injector) with a [provider](guide/glossary#provider) of that service.
A _service_ like `HeroService` is just a class in Angular until you register it with an Angular dependency injector.
The injector is responsible for creating service instances and injecting them into classes like `HeroListComponent`.
You rarely create an Angular injector yourself. Angular creates injectors for you as it executes the app, starting with the _root injector_ that it creates during the [bootstrap process](guide/bootstrapping).
An Angular injector is responsible for creating service instances and injecting them into classes like the `HeroListComponent`.
A provider tells an injector _how to create the service_.
You must configure an injector with a provider before that injector can create a service (or provide any other kind of dependency).
You rarely create an Angular injector yourself.
Angular creates injectors for you as it executes the app,
starting with the _root injector_ that it creates during the [bootstrap process](guide/bootstrapping).
A provider can be the service class itself, so that the injector can use `new` to create an instance.
You might also define more than one class to provide the same service in different ways,
and configure different injectors with different providers.
Angular doesn't automatically know how you want to create instances of your services or the injector to create your service. You must configure it by specifying providers for every service.
**Providers** tell the injector _how to create the service_.
Without a provider, the injector would not know
that it is responsible for injecting the service
nor be able to create the service.
<div class="alert is-helpful">
Injectors are inherited, which means that if a given injector can't resolve a dependency,
it asks the parent injector to resolve it.
A component can get services from its own injector,
from the injectors of its component ancestors,
from the injector of its parent NgModule, or from the `root` injector.
* Learn more about the [different kinds of providers](guide/dependency-injection-providers).
* Learn more about how the [injector hierarchy](guide/hierarchical-dependency-injection) works.
You'll learn much more about _providers_ [below](#providers).
For now, it is sufficient to know that they configure where and how services are created.
</div>
You can configure injectors with providers at different levels of your app, by setting a metadata value in one of three places:
There are many ways to register a service provider with an injector. This section shows the most common ways
of configuring a provider for your services.
* In the `@Injectable()` decorator for the service itself.
{@a register-providers-injectable}
* In the `@NgModule()` decorator for an NgModule.
## @Injectable providers
* In the `@Component()` decorator for a component.
The `@Injectable` decorator identifies services and other classes that are intended to be injected. It can also be used to configure a provider for those services.
The `@Injectable()` decorator has the `providedIn` metadata option, where you can specify the provider of the decorated service class with the `root` injector, or with the injector for a specific NgModule.
Here we configure a provider for `HeroService` using the `@Injectable` decorator on the class.
The `@NgModule()` and `@Component()` decorators have the `providers` metadata option, where you can configure providers for NgModule-level or component-level injectors.
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" title="src/app/heroes/heroes.service.ts" linenums="false"> </code-example>
`providedIn` tells Angular that the root injector is responsible for creating an instance of the `HeroService` (by invoking its constructor) and making it available across the application. The CLI sets up this kind of a provider automatically for you when generating a new service.
Sometimes it's not desirable to have a service always be provided in the application root injector. Perhaps users should explicitly opt-in to using the service, or the service should be provided in a lazily-loaded context. In this case, the provider should be associated with a specific `@NgModule` class, and will be used by whichever injector includes that module.
In the following excerpt, the `@Injectable` decorator is used to configure a provider that will be available in any injector that includes the HeroModule.
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" title="src/app/heroes/hero.service.ts" linenums="false"> </code-example>
{@a register-providers-ngmodule}
### _@NgModule_ providers
In the following excerpt, the root `AppModule` registers two providers in its `providers` array.
<code-example path="dependency-injection/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (providers)" region="providers">
</code-example>
The first entry registers the `UserService` class (_not shown_) under the `UserService` _injection token_.
The second registers a value (`HERO_DI_CONFIG`) under the `APP_CONFIG` _injection token_.
With the above registrations, Angular can inject the `UserService` or the `HERO_DI_CONFIG` value
into any class that it creates.
<div class="alert is-helpful">
Components are directives, and the `providers` option is inherited from `@Directive()`. You can also configure providers for directives and pipes at the same level as the component.
You'll learn about _injection tokens_ and _provider_ syntax [below](#providers).
</div>
Learn more about [where to configure providers](guide/hierarchical-dependency-injection#where-to-register).
{@a register-providers-component}
### _@Component_ providers
In addition to providing the service application-wide or within a particular `@NgModule`, services can also be provided in specific components. Services provided in component-level is only available within that component injector or in any of its child components.
The example below shows a revised `HeroesComponent` that registers the `HeroService` in its `providers` array.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" title="src/app/heroes/heroes.component.ts" linenums="false">
</code-example>
{@a ngmodule-vs-comp}
### @Injectable, _@NgModule_ or _@Component_?
Should you provide a service with an `@Injectable` decorator, in an `@NgModule`, or within an `@Component`?
The choices lead to differences in the final bundle size, service _scope_, and service _lifetime_.
When you register providers in the **@Injectable** decorator of the service itself, optimization tools such as those used by the CLI's production builds can perform tree shaking, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
**Angular module providers** (`@NgModule.providers`) are registered with the application's root injector.
Angular can inject the corresponding services in any class it creates.
Once created, a service instance lives for the life of the app and Angular injects this one service instance in every class that needs it.
You're likely to inject the `UserService` in many places throughout the app
and will want to inject the same service instance every time.
Providing the `UserService` with an Angular module is a good choice if an `@Injectable` provider is not an option..
<div class="alert is-helpful">
To be precise, Angular module providers are registered with the root injector
_unless the module is_ [lazy loaded](guide/lazy-loading-ngmodules).
In this sample, all modules are _eagerly loaded_ when the application starts,
so all module providers are registered with the app's root injector.
</div><br>
<hr>
**A component's providers** (`@Component.providers`) are registered with each component instance's own injector.
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
Angular cannot inject the same service instance anywhere else.
Note that a component-provided service may have a limited lifetime. Each new instance of the component gets its own instance of the service
and, when the component instance is destroyed, so is that service instance.
In this sample app, the `HeroComponent` is created when the application starts
and is never destroyed so the `HeroService` created for the `HeroComponent` also live for the life of the app.
If you want to restrict `HeroService` access to the `HeroComponent` and its nested `HeroListComponent`,
providing the `HeroService` in the `HeroComponent` may be a good choice.
<div class="alert is-helpful">
The scope and lifetime of component-provided services is a consequence of [the way Angular creates component instances](#component-child-injectors).
</div>
{@a providers}
## Providers
A service provider *provides* the concrete, runtime version of a dependency value.
The injector relies on **providers** to create instances of the services
that the injector injects into components, directives, pipes, and other services.
You must register a service *provider* with an injector, or it won't know how to create the service.
The next few sections explain the many ways you can specify a provider.
### The class as its own provider
There are many ways to *provide* something that looks and behaves like a `Logger`.
The `Logger` class itself is an obvious and natural provider.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example>
But it's not the only way.
You can configure the injector with alternative providers that can deliver an object that behaves like a `Logger`.
You could provide a substitute class. You could provide a logger-like object.
You could give it a provider that calls a logger factory function.
Any of these approaches might be a good choice under the right circumstances.
What matters is that the injector has a provider to go to when it needs a `Logger`.
{@a provide}
### The _provide_ object literal
Here's the class-provider syntax again.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-logger">
</code-example>
This is actually a shorthand expression for a provider registration
using a _provider_ object literal with two properties:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-3" >
</code-example>
The `provide` property holds the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value
and registering the provider.
The second property is always a provider definition object,
which you can think of as a *recipe* for creating the dependency value.
There are many ways to create dependency values just as there are many ways to write a recipe.
{@a class-provider}
### Alternative class providers
Occasionally you'll ask a different class to provide the service.
The following code tells the injector
to return a `BetterLogger` when something asks for the `Logger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-4" >
</code-example>
{@a class-provider-dependencies}
### Class provider with dependencies
Maybe an `EvenBetterLogger` could display the user name in the log message.
This logger gets the user from the injected `UserService`,
which is also injected at the application level.
<code-example path="dependency-injection/src/app/providers.component.ts" region="EvenBetterLogger" linenums="false">
</code-example>
Configure it like `BetterLogger`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-5" linenums="false">
</code-example>
{@a aliased-class-providers}
### Aliased class providers
Suppose an old component depends upon an `OldLogger` class.
`OldLogger` has the same interface as the `NewLogger`, but for some reason
you can't update the old component to use it.
When the *old* component logs a message with `OldLogger`,
you'd like the singleton instance of `NewLogger` to handle it instead.
The dependency injector should inject that singleton instance
when a component asks for either the new or the old logger.
The `OldLogger` should be an alias for `NewLogger`.
You certainly do not want two different `NewLogger` instances in your app.
Unfortunately, that's what you get if you try to alias `OldLogger` to `NewLogger` with `useClass`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6a" linenums="false">
</code-example>
The solution: alias with the `useExisting` option.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-6b" linenums="false">
</code-example>
{@a value-provider}
### Value providers
Sometimes it's easier to provide a ready-made object rather than ask the injector to create it from a class.
<code-example path="dependency-injection/src/app/providers.component.ts" region="silent-logger" linenums="false">
</code-example>
Then you register a provider with the `useValue` option,
which makes this object play the logger role.
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-7" linenums="false">
</code-example>
See more `useValue` examples in the
[Non-class dependencies](guide/dependency-injection#non-class-dependencies) and
[InjectionToken](guide/dependency-injection#injection-token) sections.
{@a factory-provider}
### Factory providers
Sometimes you need to create the dependent value dynamically,
based on information you won't have until the last possible moment.
Maybe the information changes repeatedly in the course of the browser session.
Suppose also that the injectable service has no independent access to the source of this information.
This situation calls for a **factory provider**.
To illustrate the point, add a new business requirement:
the `HeroService` must hide *secret* heroes from normal users.
Only authorized users should see secret heroes.
Like the `EvenBetterLogger`, the `HeroService` needs a fact about the user.
It needs to know if the user is authorized to see secret heroes.
That authorization can change during the course of a single application session,
as when you log in a different user.
Unlike `EvenBetterLogger`, you can't inject the `UserService` into the `HeroService`.
The `HeroService` won't have direct access to the user information to decide
who is authorized and who is not.
Instead, the `HeroService` constructor takes a boolean flag to control display of secret heroes.
<code-example path="dependency-injection/src/app/heroes/hero.service.ts" region="internals" title="src/app/heroes/hero.service.ts (excerpt)" linenums="false">
</code-example>
You can inject the `Logger`, but you can't inject the boolean `isAuthorized`.
You'll have to take over the creation of new instances of this `HeroService` with a factory provider.
A factory provider needs a factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="factory" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
Although the `HeroService` has no access to the `UserService`, the factory function does.
You inject both the `Logger` and the `UserService` into the factory provider
and let the injector pass them along to the factory function:
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
</code-example>
<div class="alert is-helpful">
The `useFactory` field tells Angular that the provider is a factory function
whose implementation is the `heroServiceFactory`.
The `deps` property is an array of [provider tokens](guide/dependency-injection#token).
The `Logger` and `UserService` classes serve as tokens for their own class providers.
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
</div>
Notice that you captured the factory provider in an exported variable, `heroServiceProvider`.
This extra step makes the factory provider reusable.
You can register the `HeroService` with this variable wherever you need it.
In this sample, you need it only in the `HeroesComponent`,
where it replaces the previous `HeroService` registration in the metadata `providers` array.
Here you see the new and the old implementation side-by-side:
<code-tabs>
<code-pane title="src/app/heroes/heroes.component (v3)" path="dependency-injection/src/app/heroes/heroes.component.ts">
</code-pane>
<code-pane title="src/app/heroes/heroes.component (v2)" path="dependency-injection/src/app/heroes/heroes.component.1.ts">
</code-pane>
</code-tabs>
{@a tree-shakable-provider}
### Tree-shakable providers
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-shakable.
Let us consider an example of non-tree-shakable providers in Angular.
In this example, to provide services in Angular, you include them in an `@NgModule`:
<code-example path="dependency-injection/src/app/tree-shaking/service-and-module.ts" title="src/app/tree-shaking/service-and-modules.ts" linenums="false"> </code-example>
This module can then be imported into your application module, to make the service available for injection in your app:
<code-example path="dependency-injection/src/app/tree-shaking/app.module.ts" title="src/app/tree-shaking/app.modules.ts" linenums="false"> </code-example>
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-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-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>
In the example above, `providedIn` allows you to declare the injector which injects this service. Unless there is a special case, the value should always be root. Setting the value to root ensures that the service is scoped to the root injector, without naming a particular module that is present in that injector.
The service can be instantiated by configuring a factory function as shown below:
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" title="src/app/tree-shaking/service.0.ts" linenums="false"> </code-example>
<div class="alert is-helpful">
To override tree-shakable providers, register the provider using the `providers: []` array syntax of any Angular decorator that supports it.
</div>
{@a injector-config}
{@a bootstrap}
## Injecting services
## Inject a service
In order for `HeroListComponent` to get heroes from `HeroService`, it needs to ask for `HeroService` to be injected, rather than creating it's own `HeroService` instance with `new`.
The `HeroListComponent` should get heroes from the `HeroService`.
You can tell Angular to inject a dependency in a component's constructor by specifying a **constructor parameter with the dependency type**. Here's the `HeroListComponent` constructor, asking for the `HeroService` to be injected.
The component shouldn't create the `HeroService` with `new`.
It should ask for the `HeroService` to be injected.
You can tell Angular to inject a dependency in the component's constructor by specifying a **constructor parameter with the dependency type**.
Here's the `HeroListComponent` constructor, asking for the `HeroService` to be injected.
<code-example title="src/app/heroes/hero-list.component (constructor signature)" path="dependency-injection/src/app/heroes/hero-list.component.ts"
region="ctor-signature">
</code-example>
Of course, `HeroListComponent` should do something with the injected `HeroService`.
Of course, the `HeroListComponent` should do something with the injected `HeroService`.
Here's the revised component, making use of the injected service, side-by-side with the previous version for comparison.
<code-tabs>
@ -162,40 +474,60 @@ Here's the revised component, making use of the injected service, side-by-side w
</code-pane>
</code-tabs>
`HeroService` must provided in some parent injector. The code in `HeroListComponent` doesn't depend on where `HeroService` comes from.
If you decided to provide `HeroService` in `AppModule`, `HeroListComponent` wouldn't change.
Notice that the `HeroListComponent` doesn't know where the `HeroService` comes from.
_You_ know that it comes from the parent `HeroesComponent`.
If you decided instead to provide the `HeroService` in the `AppModule`,
the `HeroListComponent` wouldn't change at all.
The _only thing that matters_ is that the `HeroService` is provided in some parent injector.
{@a singleton-services}
## Singleton services
Services are singletons _within the scope of an injector_.
There is at most one instance of a service in a given injector.
There is only one root injector, and the `UserService` is registered with that injector.
Therefore, there can be just one `UserService` instance in the entire app,
and every class that injects `UserService` get this service instance.
However, Angular DI is a
[hierarchical injection system](guide/hierarchical-dependency-injection),
which means that nested injectors can create their own service instances.
Angular creates nested injectors all the time.
{@a component-child-injectors}
### Injector hierarchy and service instances
## Component child injectors
Services are singletons _within the scope of an injector_. That is, there is at most one instance of a service in a given injector.
Component injectors are independent of each other and
each of them creates its own instances of the component-provided services.
There is only one root injector for an app. Providing `UserService` at the `root` or `AppModule` level means it is registered with the root injector. There is just one `UserService` instance in the entire app and every class that injects `UserService` gets this service instance _unless_ you configure another provider with a _child injector_.
For example, when Angular creates a new instance of a component that has `@Component.providers`,
it also creates a new _child injector_ for that instance.
Angular DI has a [hierarchical injection system](guide/hierarchical-dependency-injection), which means that nested injectors can create their own service instances.
Angular regularly creates nested injectors. Whenever Angular creates a new instance of a component that has `providers` specified in `@Component()`, it also creates a new _child injector_ for that instance.
Similarly, when a new NgModule is lazy-loaded at run time, Angular can create an injector for it with its own providers.
When Angular destroys one of these component instances, it also destroys the
component's injector and that injector's service instances.
Child modules and component injectors are independent of each other, and create their own separate instances of the provided services. When Angular destroys an NgModule or component instance, it also destroys that injector and that injector's service instances.
Thanks to [injector inheritance](guide/hierarchical-dependency-injection),
Because of [injector inheritance](guide/hierarchical-dependency-injection),
you can still inject application-wide services into these components.
A component's injector is a child of its parent component's injector,
and a descendent of its parent's parent's injector, and so on all the way back to the application's _root_ injector. Angular can inject a service provided by any injector in that lineage.
and a descendent of its parent's parent's injector, and so on all the way back to the application's _root_ injector.
Angular can inject a service provided by any injector in that lineage.
For example, Angular can inject `HeroListComponent` with both the `HeroService` provided in `HeroComponent` and the `UserService` provided in `AppModule`.
For example, Angular could inject a `HeroListComponent`
with both the `HeroService` provided in `HeroComponent`
and the `UserService` provided in `AppModule`.
{@a testing-the-component}
## Testing components with dependencies
## Testing the component
Designing a class with dependency injection makes the class easier to test.
Earlier you saw that designing a class for dependency injection makes the class easier to test.
Listing dependencies as constructor parameters may be all you need to test application parts effectively.
For example, you can create a new `HeroListComponent` with a mock service that you can manipulate
under test.
under test:
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" title="src/app/test.component.ts" linenums="false">
</code-example>
@ -208,12 +540,15 @@ Learn more in the [Testing](guide/testing) guide.
{@a service-needs-service}
## Services that need other services
## When the service needs a service
Service can have their own dependencies. `HeroService` is very simple and doesn't have any dependencies of its own. Suppose, however, that you want it to report its activities through a logging service. You can apply the same *constructor injection* pattern,
The `HeroService` is very simple. It doesn't have any dependencies of its own.
What if it had a dependency? What if it reported its activities through a logging service?
You'd apply the same *constructor injection* pattern,
adding a constructor that takes a `Logger` parameter.
Here is the revised `HeroService` that injects `Logger`, side by side with the previous service for comparison.
Here is the revised `HeroService` that injects the `Logger`, side-by-side with the previous service for comparison.
<code-tabs>
@ -223,85 +558,154 @@ Here is the revised `HeroService` that injects `Logger`, side by side with the p
<code-pane title="src/app/heroes/hero.service (v1)" path="dependency-injection/src/app/heroes/hero.service.1.ts">
</code-pane>
<code-pane title="src/app/logger.service"
path="dependency-injection/src/app/logger.service.ts">
</code-pane>
</code-tabs>
The constructor asks for an injected instance of `Logger` and stores it in a private field called `logger`. The `getHeroes()` method logs a message when asked to fetch heroes.
The constructor asks for an injected instance of a `Logger` and stores it in a private field called `logger`.
The `getHeroes()` method logs a message when asked to fetch heroes.
Notice that the `Logger` service also has the `@Injectable()` decorator, even though it might not need its own dependencies. In fact, the `@Injectable()` decorator is **required for all services**.
When Angular creates a class whose constructor has parameters, it looks for type and injection metadata about those parameters so that it can inject the correct service.
If Angular can't find that parameter information, it throws an error.
Angular can only find the parameter information _if the class has a decorator of some kind_.
The `@Injectable()` decorator is the standard decorator for service classes.
{@a logger-service}
<div class="alert-is-helpful">
#### The dependent _Logger_ service
The sample app's `Logger` service is quite simple:
<code-example path="dependency-injection/src/app/logger.service.ts" title="src/app/logger.service.ts">
</code-example>
If the app didn't provide this `Logger`,
Angular would throw an exception when it looked for a `Logger` to inject
into the `HeroService`.
<code-example language="sh" class="code-shell">
ERROR Error: No provider for Logger!
</code-example>
Because a singleton logger service is useful everywhere,
it's provided in the root `AppModule`.
<code-example path="dependency-injection/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (providers)" region="providers-2">
</code-example>
The decorator requirement is imposed by TypeScript. TypeScript normally discards parameter type information when it [transpiles]((guide/glossary#transpile) the code to JavaScript. TypeScript preserves this information if the class has a decorator and the `emitDecoratorMetadata` compiler option is set `true` in TypeScript's `tsconfig.json` configuration file. The CLI configures `tsconfig.json` with `emitDecoratorMetadata: true`.
This means you're responsible for putting `@Injectable()` on your service classes.
</div>
{@a token}
{@a injection-token}
## Dependency injection tokens
### Dependency injection tokens
When you configure an injector with a provider, you associate that provider with a [DI token](guide/glossary#di-token).
When you register a provider with an injector, you associate that provider with a dependency injection token.
The injector maintains an internal *token-provider* map that it references when
asked for a dependency. The token is the key to the map.
In simple examples, the dependency value is an *instance*, and
the class *type* serves as its own lookup key.
In all previous examples, the dependency value has been a class *instance*, and
the class *type* served as its own lookup key.
Here you get a `HeroService` directly from the injector by supplying the `HeroService` type as the token:
<code-example path="dependency-injection/src/app/injector.component.ts" region="get-hero-service" title="src/app/injector.component.ts" linenums="false">
</code-example>
The behavior is similar when you write a constructor that requires an injected class-based dependency.
You have similar good fortune when you write a constructor that requires an injected class-based dependency.
When you define a constructor parameter with the `HeroService` class type,
Angular knows to inject the service associated with that `HeroService` class token:
Angular knows to inject the
service associated with that `HeroService` class token:
<code-example path="dependency-injection/src/app/heroes/hero-list.component.ts" region="ctor-signature" title="src/app/heroes/hero-list.component.ts">
</code-example>
Many dependency values are provided by classes, but not all. The expanded *provide* object lets you associate different kinds of providers with a DI token.
This is especially convenient when you consider that most dependency values are provided by classes.
* Learn more about [different kinds of providers](guide/dependency-injection-providers).
{@a non-class-dependencies}
### Non-class dependencies
What if the dependency value isn't a class? Sometimes the thing you want to inject is a
string, function, or object.
Applications often define configuration objects with lots of small facts
(like the title of the application or the address of a web API endpoint)
but these configuration objects aren't always instances of a class.
They can be object literals such as this one:
<code-example path="dependency-injection/src/app/app.config.ts" region="config" title="src/app/app.config.ts (excerpt)" linenums="false">
</code-example>
What if you'd like to make this configuration object available for injection?
You know you can register an object with a [value provider](guide/dependency-injection#value-provider).
But what should you use as the token?
You don't have a class to serve as a token.
There is no `AppConfig` class.
<div class="alert is-helpful">
### TypeScript interfaces aren't valid tokens
The `HERO_DI_CONFIG` constant conforms to the `AppConfig` interface.
Unfortunately, you cannot use a TypeScript interface as a token:
<code-example path="dependency-injection/src/app/providers.component.ts" region="providers-9-interface" linenums="false">
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-9-ctor-interface" linenums="false">
</code-example>
That seems strange if you're used to dependency injection in strongly typed languages, where
an interface is the preferred dependency lookup key.
It's not Angular's doing. An interface is a TypeScript design-time artifact. JavaScript doesn't have interfaces.
The TypeScript interface disappears from the generated JavaScript.
There is no interface type information left for Angular to find at runtime.
</div>
{@a injection-token}
### _InjectionToken_
One solution to choosing a provider token for non-class dependencies is
to define and use an [*InjectionToken*](api/core/InjectionToken).
The definition of such a token looks like this:
<code-example>
import { InjectionToken } from '@angular/core';
export const TOKEN = new InjectionToken('desc');
</code-example>
You can directly configure a provider when creating an `InjectionToken`. The provider configuration determines which injector provides the token and how the value will be created. This is similar to using `@Injectable`, except that you cannot define standard providers (such as `useClass` or `useFactory`) with `InjectionToken`. Instead, you specify a factory function which returns the value to be provided directly.
<code-example>
export const TOKEN =
new InjectionToken('desc', { providedIn: 'root', factory: () => new AppConfig(), })
</code-example>
Now you can inject the configuration object into any constructor that needs it, with
the help of an `@Inject` decorator:
<code-example>
constructor(@Inject(TOKEN));
</code-example>
If the factory function needs access to other DI tokens, it can use the inject function from `@angular/core` to request dependencies.
<code-example>
const TOKEN =
new InjectionToken('tree-shakable token',
{ providedIn: 'root', factory: () =>
new AppConfig(inject(Parameter1), inject(Parameter2)), });
</code-example>
{@a optional}
### Optional dependencies
## Optional dependencies
`HeroService` *requires* a logger, but what if it could get by without
one?
You can tell Angular that the dependency is optional by annotating the constructor argument with null:
When a component or service declares a dependency, the class constructor takes that dependency as a parameter.
You can tell Angular that the dependency is optional by annotating the
constructor parameter with `@Optional()`.
<code-example path="dependency-injection/src/app/providers.component.ts" region="import-optional">
<code-example>
constructor(@Inject(Token, null));
</code-example>
<code-example path="dependency-injection/src/app/providers.component.ts" region="provider-10-ctor" linenums="false">
</code-example>
When using `@Optional()`, your code must be prepared for a null value. If you
don't register a logger provider anywhere, the injector sets the
value of `logger` to null.
<div class="alert-is-helpful">
`@Inject()` and `@Optional()` are _parameter decorators_. They alter the way the DI framework provides a dependency, by annotating the dependency parameter on the constructor of the class that requires the dependency.
Learn more about parameter decorators in [Hierarchical Dependency Injectors](guide/hierarchical-dependency-injection).
</div>
When using optional dependencies, your code must be prepared for a null value.
## Summary
@ -310,13 +714,74 @@ You can register various kinds of providers,
and you know how to ask for an injected object (such as a service) by
adding a parameter to a constructor.
Dive deeper into the capabilities and advanced feature of the Angular DI system in the following pages:
* Learn more about nested injectors in
Angular dependency injection is more capable than this guide has described.
You can learn more about its advanced features, beginning with its support for
nested injectors, in
[Hierarchical Dependency Injection](guide/hierarchical-dependency-injection).
* Learn more about [DI tokens and providers](guide/dependency-injection-providers).
{@a explicit-injector}
* [Dependency Injection in Action](guide/dependency-injection-in-action) is a cookbook for some of the interesting things you can do with DI.
## Appendix: Working with injectors directly
Developers rarely work directly with an injector, but
here's an `InjectorComponent` that does.
<code-example path="dependency-injection/src/app/injector.component.ts" region="injector" title="src/app/injector.component.ts">
</code-example>
An `Injector` is itself an injectable service.
In this example, Angular injects the component's own `Injector` into the component's constructor.
The component then asks the injected injector for the services it wants in `ngOnInit()`.
Note that the services themselves are not injected into the component.
They are retrieved by calling `injector.get()`.
The `get()` method throws an error if it can't resolve the requested service.
You can call `get()` with a second parameter, which is the value to return if the service
is not found. Angular can't find the service if it's not registered with this or any ancestor injector.
<div class="alert is-helpful">
The technique is an example of the
[service locator pattern](https://en.wikipedia.org/wiki/Service_locator_pattern).
**Avoid** this technique unless you genuinely need it.
It encourages a careless grab-bag approach such as you see here.
It's difficult to explain, understand, and test.
You can't know by inspecting the constructor what this class requires or what it will do.
It could acquire services from any ancestor component, not just its own.
You're forced to spelunk the implementation to discover what it does.
Framework developers may take this approach when they
must acquire services generically and dynamically.
</div>
{@a one-class-per-file}
## Appendix: one class per file
Having multiple classes in the same file is confusing and best avoided.
Developers expect one class per file. Keep them happy.
If you combine the `HeroService` class with
the `HeroesComponent` in the same file,
**define the component last**.
If you define the component before the service,
you'll get a runtime null reference error.
<div class="alert is-helpful">
You actually can define the component first with the help of the `forwardRef()` method as explained
in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
But it's best to avoid the problem altogether by defining components and services in separate files.
</div>

View File

@ -1,216 +1,120 @@
# Hierarchical Dependency Injectors
The Angular dependency injection system is _hierarchical_.
There is a tree of injectors that parallel an app's component tree.
You learned the basics of Angular Dependency injection in the
[Dependency Injection](guide/dependency-injection) guide.
Angular has a _Hierarchical Dependency Injection_ system.
There is actually a tree of injectors that parallel an application's component tree.
You can reconfigure the injectors at any level of that component tree.
This guide explores this system and how to use it to your advantage.
It uses examples based on this <live-example></live-example>.
{@a ngmodule-vs-comp}
{@a where-to-register}
Try the <live-example></live-example>.
## Where to configure providers
You can configure providers for different injectors in the injector hierarchy.
An internal platform-level injector is shared by all running apps.
The `AppModule` injector is the root of an app-wide injector hierarchy, and within
an NgModule, directive-level injectors follow the structure of the component hierarchy.
The choices you make about where to configure providers lead to differences in the final bundle size, service _scope_, and service _lifetime_.
## The injector tree
When you specify providers in the `@Injectable()` decorator of the service itself (typically at the app root level), optimization tools such as those used by the CLI's production builds can perform *tree shaking*, which removes services that aren't used by your app. Tree shaking results in smaller bundle sizes.
In the [Dependency Injection](guide/dependency-injection) guide,
you learned how to configure a dependency injector and how to retrieve dependencies where you need them.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
In fact, there is no such thing as ***the*** injector.
An application may have multiple injectors.
An Angular application is a tree of components. Each component instance has its own injector.
The tree of components parallels the tree of injectors.
You're likely to inject `UserService` in many places throughout the app and will want to inject the same service instance every time. Providing `UserService` through the `root` injector is a good choice, and is the default that the CLI uses when you generate a service for your app.
<div class="alert-is-helpful">
<header>Platform injector</header>
<div class="alert is-helpful">
When you use `providedIn:'root'`, you are configuring the root injector for the _app_, which is the injector for `AppModule`.
The actual root of the entire injector hierarchy is a _platform injector_ that is the parent of app-root injectors.
This allows multiple apps to share a platform configuration. For example, a browser has only one URL bar, no matter how many apps you have running.
The platform injector is used internally during bootstrap, to configure platform-specific dependencies. You can configure additional platform-specific providers at the platform level by supplying `extraProviders` using the `platformBrowser()` function.
Learn more about dependency resolution through the injector hierarchy:
[What you always wanted to know about Angular Dependency Injection tree](https://blog.angularindepth.com/angular-dependency-injection-and-tree-shakeable-tokens-4588a8f70d5d)
The component's injector may be a _proxy_ for an ancestor injector higher in the component tree.
That's an implementation detail that improves efficiency.
You won't notice the difference and
your mental model should be that every component has its own injector.
</div>
*NgModule-level* providers can be specified with `@NgModule()` `providers` metadata option, or in the `@Injectable()` `providedIn` option (with some module other than the root `AppModule`).
Use the `@NgModule()` `provides` option if a module is [lazy loaded](guide/lazy-loading-ngmodules). The module's own injector is configured with the provider when that module is loaded, and Angular can inject the corresponding services in any class it creates in that module. If you use the `@Injectable()` option `providedIn: MyLazyloadModule`, the provider could be shaken out at compile time, if it is not used anywhere else in the app.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
For both root-level and module-level injectors, a service instance lives for the life of the app or module, and Angular injects this one service instance in every class that needs it.
*Component-level* providers configure each component instance's own injector.
Angular can only inject the corresponding services in that component instance or one of its descendant component instances.
Angular can't inject the same service instance anywhere else.
A component-provided service may have a limited lifetime.
Each new instance of the component gets its own instance of the service.
When the component instance is destroyed, so is that service instance.
In our sample app, `HeroComponent` is created when the application starts
and is never destroyed,
so the `HeroService` instance created for `HeroComponent` lives for the life of the app.
If you want to restrict `HeroService` access to `HeroComponent` and its nested
`HeroListComponent`, provide `HeroService` at the component level, in `HeroComponent` metadata.
* See more [examples of component-level injection](#component-injectors) below.
{@a register-providers-injectable}
### @Injectable-level configuration
The `@Injectable()` decorator identifies every service class. The `providedIn` metadata option for a service class configures a specific injector (typically `root`)
to use the decorated class as a provider of the service.
When an injectable class provides its own service to the `root` injector, the service is available anywhere the class is imported.
The following example configures a provider for `HeroService` using the `@Injectable()` decorator on the class.
<code-example path="dependency-injection/src/app/heroes/hero.service.0.ts" title="src/app/heroes/heroes.service.ts" linenums="false"> </code-example>
This configuration tells Angular that the app's root injector is responsible for creating an
instance of `HeroService` by invoking its constructor,
and for making that instance available across the application.
Providing a service with the app's root injector is a typical case,
and the CLI sets up this kind of a provider automatically for you
when generating a new service.
However, you might not always want to provide your service at the root level.
You might, for instance, want users to explicitly opt-in to using the service.
Instead of specifying the `root` injector, you can set `providedIn` to a specific NgModule.
For example, in the following excerpt, the `@Injectable()` decorator configures a provider
that is available in any injector that includes the `HeroModule`.
<code-example path="dependency-injection/src/app/heroes/hero.service.4.ts" title="src/app/heroes/hero.service.ts" linenums="false"> </code-example>
This is generally no different from configuring the injector of the NgModule itself,
except that the service is tree-shakable if the NgModule doesn't use it.
It can be useful for a library that offers a particular service that some
components *might* want to inject optionally,
and leave it up to the app whether to provide the service.
* Learn more about [tree-shakable providers](guide/dependency-injection-providers#tree-shakable-providers).
### @NgModule-level injectors
You can configure a provider at the module level using the `providedIn` metadata option for a non-root NgModule, in order to limit the scope of the provider to that module.
This is the equivalent of specifying the non-root module in the `@Injectable()` metadata, except that the service provided this way is not tree-shakable.
You generally don't need to specify `AppModule` with `providedIn`, because the app's `root` injector is the `AppModule` injector.
However, if you configure a app-wide provider in the`@NgModule()` metadata for `AppModule`,
it overrides one configured for `root` in the `@Injectable()` metadata.
You can do this to configure a non-default provider of a service that is shared with multiple apps.
Here is an example of the case where the component router configuration includes
a non-default [location strategy](guide/router#location-strategy) by listing its provider
in the `providers` list of the `AppModule`.
<code-example path="dependency-injection-in-action/src/app/app.module.ts" region="providers" title="src/app/app.module.ts (providers)" linenums="false">
</code-example>
{@a register-providers-component}
### @Component-level injectors
Individual components within an NgModule have their own injectors.
You can limit the scope of a provider to a component and its children
by configuring the provider at the component level using the `@Component` metadata.
The following example is a revised `HeroesComponent` that specifies `HeroService` in its `providers` array. `HeroService` can provide heroes to instances of this component, or to any child component instances.
<code-example path="dependency-injection/src/app/heroes/heroes.component.1.ts" title="src/app/heroes/heroes.component.ts" linenums="false">
</code-example>
### Element injectors
An injector does not actually belong to a component, but rather to the component instance's anchor element in the DOM. A different component instance on a different DOM element uses a different injector.
Components are a special type of directive, and the `providers` property of
`@Component()` is inherited from `@Directive()`.
Directives can also have dependencies, and you can configure providers
in their `@Directive()` metadata.
When you configure a provider for a component or directive using the `providers` property, that provider belongs to the injector for the anchor DOM element. Components and directives on the same element share an injector.
<!--- TBD with examples
* For an example of how this works, see [Element-level providers](guide/dependency-injection-in-action#directive-level-providers).
--->
* Learn more about [Element Injectors in Angular](https://blog.angularindepth.com/a-curios-case-of-the-host-decorator-and-element-injectors-in-angular-582562abcf0a).
## Injector bubbling
Consider this guide's variation on the Tour of Heroes application.
At the top is the `AppComponent` which has some subcomponents, such as the `HeroesListComponent`.
At the top is the `AppComponent` which has some sub-components.
One of them is the `HeroesListComponent`.
The `HeroesListComponent` holds and manages multiple instances of the `HeroTaxReturnComponent`.
The following diagram represents the state of this three-level component tree when there are three instances of `HeroTaxReturnComponent` open simultaneously.
The following diagram represents the state of the this guide's three-level component tree when there are three instances of `HeroTaxReturnComponent`
open simultaneously.
<figure>
<img src="generated/images/guide/dependency-injection/component-hierarchy.png" alt="injector tree">
</figure>
### Injector bubbling
When a component requests a dependency, Angular tries to satisfy that dependency with a provider registered in that component's own injector.
If the component's injector lacks the provider, it passes the request up to its parent component's injector.
If that injector can't satisfy the request, it passes the request along to the next parent injector up the tree.
If that injector can't satisfy the request, it passes it along to *its* parent injector.
The requests keep bubbling up until Angular finds an injector that can handle the request or runs out of ancestor injectors.
If it runs out of ancestors, Angular throws an error.
If you have registered a provider for the same DI token at different levels, the first one Angular encounters is the one it uses to provide the dependency. If, for example, a provider is registered locally in the component that needs a service, Angular doesn't look for another provider of the same service.
If it runs out of ancestors, Angular throws an error.
<div class="alert is-helpful">
You can cap the bubbling by adding the `@Host()` parameter decorator on the dependant-service parameter
in a component's constructor.
The hunt for providers stops at the injector for the host element of the component.
* See an [example](guide/dependency-injection-in-action#qualify-dependency-lookup) of using `@Host` together with `@Optional`, another parameter decorator that lets you handle the null case if no provider is found.
* Learn more about the [`@Host` decorator and Element Injectors](https://blog.angularindepth.com/a-curios-case-of-the-host-decorator-and-element-injectors-in-angular-582562abcf0a).
You can cap the bubbling. An intermediate component can declare that it is the "host" component.
The hunt for providers will climb no higher than the injector for that host component.
This is a topic for another day.
</div>
If you only register providers with the root injector at the top level (typically the root `AppModule`), the tree of injectors appears to be flat.
All requests bubble up to the root injector, whether you configured it with the `bootstrapModule` method, or registered all providers with `root` in their own services.
{@a component-injectors}
### Re-providing a service at different levels
You can re-register a provider for a particular dependency token at multiple levels of the injector tree.
You don't *have* to re-register providers. You shouldn't do so unless you have a good reason.
But you *can*.
As the resolution logic works upwards, the first provider encountered wins.
Thus, a provider in an intermediate injector intercepts a request for a service from something lower in the tree.
It effectively "reconfigures" and "shadows" a provider at a higher level in the tree.
If you only specify providers at the top level (typically the root `AppModule`), the tree of injectors appears to be flat.
All requests bubble up to the root <code>NgModule</code> injector that you configured with the `bootstrapModule` method.
## Component injectors
The ability to configure one or more providers at different levels opens up interesting and useful possibilities.
The guide sample offers some scenarios where you might want to do so.
### Scenario: service isolation
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
For example, the guide sample includes a `VillainsListComponent` that displays a list of villains.
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
The guide sample includes a `VillainsListComponent` that displays a list of villains.
It gets those villains from a `VillainsService`.
If you provide `VillainsService` in the root `AppModule` (where you registered the `HeroesService`),
that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows. If you later modified the `VillainsService`, you could break something in a hero component somewhere. Providing the service in the root `AppModule` creates that risk.
While you _could_ provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`),
that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows.
Instead, you can provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
If you later modified the `VillainsService`, you could break something in a hero component somewhere.
That's not supposed to happen but providing the service in the root `AppModule` creates that risk.
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
<code-example path="hierarchical-dependency-injection/src/app/villains-list.component.ts" linenums="false" title="src/app/villains-list.component.ts (metadata)" region="metadata">
</code-example>
By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else,
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
It's still a singleton, but it's a singleton that exist solely in the _villain_ domain.
@ -240,10 +144,12 @@ Each tax return component has the following characteristics:
<img src="generated/images/guide/dependency-injection/hid-heroes-anim.gif" alt="Heroes in action">
</figure>
Suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes.
One might suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes.
That would be a pretty easy task for a simple hero tax return.
In the real world, with a rich tax return data model, the change management would be tricky.
You could delegate that management to a helper service, as this example does.
You might delegate that management to a helper service, as this example does.
Here is the `HeroTaxReturnService`.
It caches a single `HeroTaxReturn`, tracks changes to that return, and can save or restore it.
@ -254,6 +160,8 @@ It also delegates to the application-wide singleton `HeroService`, which it gets
</code-example>
Here is the `HeroTaxReturnComponent` that makes use of it.
@ -262,29 +170,36 @@ Here is the `HeroTaxReturnComponent` that makes use of it.
</code-example>
The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters.
The setter initializes the component's own instance of the `HeroTaxReturnService` with the incoming return.
The getter always returns what that service says is the current state of the hero.
The component also asks the service to save and restore this tax return.
This won't work if the service is an application-wide singleton.
Every component would share the same service instance, and each component would overwrite the tax return that belonged to another hero.
There'd be big trouble if _this_ service were an application-wide singleton.
Every component would share the same service instance.
Each component would overwrite the tax return that belonged to another hero.
What a mess!
To prevent this, we configure the component-level injector of `HeroTaxReturnComponent` to provide the service, using the `providers` property in the component metadata.
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
<code-example path="hierarchical-dependency-injection/src/app/hero-tax-return.component.ts" linenums="false" title="src/app/hero-tax-return.component.ts (providers)" region="providers">
</code-example>
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
Recall that every component _instance_ has its own injector.
Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service, and no tax return gets overwritten.
Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service.
No tax return overwriting. No mess.
<div class="alert is-helpful">
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.
You can review it and download it from the <live-example></live-example>.
@ -295,9 +210,10 @@ You can review it and download it from the <live-example></live-example>.
### Scenario: specialized providers
Another reason to re-provide a service at another level is to substitute a _more specialized_ implementation of that service, deeper in the component tree.
Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service,
deeper in the component tree.
Consider a Car component that depends on several services.
Consider again the Car example from the [Dependency Injection](guide/dependency-injection) guide.
Suppose you configured the root injector (marked as A) with _generic_ providers for
`CarService`, `EngineService` and `TiresService`.
@ -313,6 +229,8 @@ Component (B) is the parent of another component (C) that defines its own, even
<img src="generated/images/guide/dependency-injection/car-components.png" alt="car components">
</figure>
Behind the scenes, each component sets up its own injector with zero, one, or more providers defined for that component itself.
When you resolve an instance of `Car` at the deepest component (C),
@ -329,7 +247,9 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
<div class="alert is-helpful">
The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample
which you can review and download from the <live-example></live-example>.
</div>

View File

@ -88,5 +88,5 @@ Register a provider with a component when you must limit a service instance to a
You may also be interested in:
* [Singleton Services](guide/singleton-services), which elaborates on the concepts covered on this page.
* [Lazy Loading Modules](guide/lazy-loading-ngmodules).
* [Tree-shakable Providers](guide/dependency-injection-providers#tree-shakable-providers).
* [Tree-shakable Providers](guide/dependency-injection#tree-shakable-providers).
* [NgModule FAQ](guide/ngmodule-faq).

View File

@ -1,6 +1,6 @@
# Angular service worker introduction
Service workers augment the traditional web deployment model and empower applications to deliver a user experience with the reliability and performance on par with natively-installed code. Adding a service worker to an Angular application is one of the steps for turning an application into a [Progressive Web App](https://developers.google.com/web/progressive-web-apps/) (also known as a PWA).
Service workers augment the traditional web deployment model and empower applications to deliver a user experience with the reliability and performance on par with natively-installed code.
At its simplest, a service worker is a script that runs in the web browser and manages caching for an application.

View File

@ -768,7 +768,7 @@ code. For example, you might have a service called `HeroesService` in AngularJS:
<code-example path="upgrade-module/src/app/ajs-to-a-providers/heroes.service.ts" title="heroes.service.ts">
</code-example>
You can upgrade the service using a Angular [factory provider](guide/dependency-injection-providers#factory-providers)
You can upgrade the service using a Angular [factory provider](guide/dependency-injection#factory-providers)
that requests the service from the AngularJS `$injector`.
Many developers prefer to declare the factory provider in a separate `ajs-upgraded-providers.ts` file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

@ -4,31 +4,6 @@
<article>
<p>Where we'll be presenting:</p>
<table class="is-full-width">
<thead>
<tr>
<th>Event</th>
<th>Location</th>
<th>Date</th>
</tr>
</thead>
<tbody>
<!-- ReactiveConf -->
<tr>
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
<td>Prague, Czech Republic</td>
<td>October 29-31, 2018</td>
</tr>
<!-- AngularConnect-->
<tr>
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
<td>London, United Kingdom</td>
<td>November 5-7, 2018</td>
</tr>
</tbody>
</table>
<p>Where we already presented:</p>
<table class="is-full-width">
<thead>
<tr>
@ -80,6 +55,18 @@
<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>
<td>London, United Kingdom</td>
<td>November 5-7, 2018</td>
</tr>
</tbody>
</table>
</article>

View File

@ -89,16 +89,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/animations.png" alt="Animations Icon">
<img src="assets/images/logos/concept-icons/animations.png" alt="Animations Icon">
</div>
<div>
<h3 class="l-space-left-3">ANIMATIONS</h3>
<ul class="l-space-left-3">
<li>
<span>Animations Icon (png) - <a href="generated/images/marketing/concept-icons/animations.png" download>Download</a></span>
<span>Animations Icon (png) - <a href="assets/images/logos/concept-icons/animations.png" download>Download</a></span>
</li>
<li>
<span>Animations Icon (svg) - <a href="generated/images/marketing/concept-icons/animations.svg" download>Download</a></span>
<span>Animations Icon (svg) - <a href="assets/images/logos/concept-icons/animations.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -108,16 +108,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/augury.png" alt="Augury Icon">
<img src="assets/images/logos/concept-icons/augury.png" alt="Augury Icon">
</div>
<div>
<h3 class="l-space-left-3">AUGURY</h3>
<ul class="l-space-left-3">
<li>
<span>Augury Icon (png) - <a href="generated/images/marketing/concept-icons/augury.png" download>Download</a></span>
<span>Augury Icon (png) - <a href="assets/images/logos/concept-icons/augury.png" download>Download</a></span>
</li>
<li>
<span>Augury Icon (svg) - <a href="generated/images/marketing/concept-icons/augury.svg" download>Download</a></span>
<span>Augury Icon (svg) - <a href="assets/images/logos/concept-icons/augury.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -127,16 +127,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/cdk.png" alt="CDK Icon">
<img src="assets/images/logos/concept-icons/cdk.png" alt="CDK Icon">
</div>
<div>
<h3 class="l-space-left-3">COMPONENT DEV KIT (CDK)</h3>
<ul class="l-space-left-3">
<li>
<span>CDK Icon (png) - <a href="generated/images/marketing/concept-icons/cdk.png" download>Download</a></span>
<span>CDK Icon (png) - <a href="assets/images/logos/concept-icons/cdk.png" download>Download</a></span>
</li>
<li>
<span>CDK Icon (svg) - <a href="generated/images/marketing/concept-icons/cdk.svg" download>Download</a></span>
<span>CDK Icon (svg) - <a href="assets/images/logos/concept-icons/cdk.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -146,16 +146,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/cli.png" alt="CLI Icon">
<img src="assets/images/logos/concept-icons/cli.png" alt="CLI Icon">
</div>
<div>
<h3 class="l-space-left-3">CLI</h3>
<ul class="l-space-left-3">
<li>
<span>CLI Icon (png) - <a href="generated/images/marketing/concept-icons/cli.png" download>Download</a></span>
<span>CLI Icon (png) - <a href="assets/images/logos/concept-icons/cli.png" download>Download</a></span>
</li>
<li>
<span>CLI Icon (svg) - <a href="generated/images/marketing/concept-icons/cli.svg" download>Download</a></span>
<span>CLI Icon (svg) - <a href="assets/images/logos/concept-icons/cli.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -165,16 +165,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/compiler.png" alt="Compiler Icon">
<img src="assets/images/logos/concept-icons/compiler.png" alt="Compiler Icon">
</div>
<div>
<h3 class="l-space-left-3">COMPILER</h3>
<ul class="l-space-left-3">
<li>
<span>Compiler Icon (png) - <a href="generated/images/marketing/concept-icons/compiler.png" download>Download</a></span>
<span>Compiler Icon (png) - <a href="assets/images/logos/concept-icons/compiler.png" download>Download</a></span>
</li>
<li>
<span>Compiler Icon (svg) - <a href="generated/images/marketing/concept-icons/compiler.svg" download>Download</a></span>
<span>Compiler Icon (svg) - <a href="assets/images/logos/concept-icons/compiler.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -184,16 +184,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/components.png" alt="Components Icon">
<img src="assets/images/logos/concept-icons/components.png" alt="Components Icon">
</div>
<div>
<h3 class="l-space-left-3">WEB COMPONENTS</h3>
<ul class="l-space-left-3">
<li>
<span>Web Components Icon (png) - <a href="generated/images/marketing/concept-icons/components.png" download>Download</a></span>
<span>Web Components Icon (png) - <a href="assets/images/logos/concept-icons/components.png" download>Download</a></span>
</li>
<li>
<span>Web Components Icon (svg) - <a href="generated/images/marketing/concept-icons/components.svg" download>Download</a></span>
<span>Web Components Icon (svg) - <a href="assets/images/logos/concept-icons/components.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -203,16 +203,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/forms.png" alt="Forms Icon">
<img src="assets/images/logos/concept-icons/forms.png" alt="Forms Icon">
</div>
<div>
<h3 class="l-space-left-3">FORMS</h3>
<ul class="l-space-left-3">
<li>
<span>Forms Icon (png) - <a href="generated/images/marketing/concept-icons/forms.png" download>Download</a></span>
<span>Forms Icon (png) - <a href="assets/images/logos/concept-icons/forms.png" download>Download</a></span>
</li>
<li>
<span>Forms Icon (svg) - <a href="generated/images/marketing/concept-icons/forms.svg" download>Download</a></span>
<span>Forms Icon (svg) - <a href="assets/images/logos/concept-icons/forms.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -222,16 +222,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/http.png" alt="HTTP Icon">
<img src="assets/images/logos/concept-icons/http.png" alt="HTTP Icon">
</div>
<div>
<h3 class="l-space-left-3">HTTP</h3>
<ul class="l-space-left-3">
<li>
<span>HTTP Icon (png) - <a href="generated/images/marketing/concept-icons/http.png" download>Download</a></span>
<span>HTTP Icon (png) - <a href="assets/images/logos/concept-icons/http.png" download>Download</a></span>
</li>
<li>
<span>HTTP Icon (svg) - <a href="generated/images/marketing/concept-icons/http.svg" download>Download</a></span>
<span>HTTP Icon (svg) - <a href="assets/images/logos/concept-icons/http.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -241,16 +241,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/i18n.png" alt="i18n Icon">
<img src="assets/images/logos/concept-icons/i18n.png" alt="i18n Icon">
</div>
<div>
<h3 class="l-space-left-3">i18n</h3>
<ul class="l-space-left-3">
<li>
<span>HTTP Icon (png) - <a href="generated/images/marketing/concept-icons/i18n.png" download>Download</a></span>
<span>HTTP Icon (png) - <a href="assets/images/logos/concept-icons/i18n.png" download>Download</a></span>
</li>
<li>
<span>HTTP Icon (svg) - <a href="generated/images/marketing/concept-icons/i18n.svg" download>Download</a></span>
<span>HTTP Icon (svg) - <a href="assets/images/logos/concept-icons/i18n.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -260,16 +260,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/karma.png" alt="Karma Icon">
<img src="assets/images/logos/concept-icons/karma.png" alt="Karma Icon">
</div>
<div>
<h3 class="l-space-left-3">KARMA</h3>
<ul class="l-space-left-3">
<li>
<span>Karma Icon (png) - <a href="generated/images/marketing/concept-icons/karma.png" download>Download</a></span>
<span>Karma Icon (png) - <a href="assets/images/logos/concept-icons/karma.png" download>Download</a></span>
</li>
<li>
<span>Karma Icon (svg) - <a href="generated/images/marketing/concept-icons/karma.svg" download>Download</a></span>
<span>Karma Icon (svg) - <a href="assets/images/logos/concept-icons/karma.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -279,16 +279,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/labs.png" alt="Labs Icon">
<img src="assets/images/logos/concept-icons/labs.png" alt="Labs Icon">
</div>
<div>
<h3 class="l-space-left-3">LABS</h3>
<ul class="l-space-left-3">
<li>
<span>Labs Icon (png) - <a href="generated/images/marketing/concept-icons/labs.png" download>Download</a></span>
<span>Labs Icon (png) - <a href="assets/images/logos/concept-icons/labs.png" download>Download</a></span>
</li>
<li>
<span>Labs Icon (svg) - <a href="generated/images/marketing/concept-icons/labs.svg" download>Download</a></span>
<span>Labs Icon (svg) - <a href="assets/images/logos/concept-icons/labs.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -298,16 +298,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/language-services.png" alt="Language Services Icon">
<img src="assets/images/logos/concept-icons/language-services.png" alt="Language Services Icon">
</div>
<div>
<h3 class="l-space-left-3">LANGUAGE SERVICES</h3>
<ul class="l-space-left-3">
<li>
<span>Language Services Icon (png) - <a href="generated/images/marketing/concept-icons/language-services.png" download>Download</a></span>
<span>Language Services Icon (png) - <a href="assets/images/logos/concept-icons/language-services.png" download>Download</a></span>
</li>
<li>
<span>Language Services Icon (svg) - <a href="generated/images/marketing/concept-icons/language-services.svg" download>Download</a></span>
<span>Language Services Icon (svg) - <a href="assets/images/logos/concept-icons/language-services.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -317,16 +317,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/material.png" alt="Material Icon">
<img src="assets/images/logos/concept-icons/material.png" alt="Material Icon">
</div>
<div>
<h3 class="l-space-left-3">MATERIAL</h3>
<ul class="l-space-left-3">
<li>
<span>Material Icon (png) - <a href="generated/images/marketing/concept-icons/material.png" download>Download</a></span>
<span>Material Icon (png) - <a href="assets/images/logos/concept-icons/material.png" download>Download</a></span>
</li>
<li>
<span>Material Icon (svg) - <a href="generated/images/marketing/concept-icons/material.svg" download>Download</a></span>
<span>Material Icon (svg) - <a href="assets/images/logos/concept-icons/material.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -336,16 +336,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/protractor.png" alt="Protractor Icon">
<img src="assets/images/logos/concept-icons/protractor.png" alt="Protractor Icon">
</div>
<div>
<h3 class="l-space-left-3">PROTRACTOR</h3>
<ul class="l-space-left-3">
<li>
<span>Protractor Icon (png) - <a href="generated/images/marketing/concept-icons/protractor.png" download>Download</a></span>
<span>Protractor Icon (png) - <a href="assets/images/logos/concept-icons/protractor.png" download>Download</a></span>
</li>
<li>
<span>Protractor Icon (svg) - <a href="generated/images/marketing/concept-icons/protractor.svg" download>Download</a></span>
<span>Protractor Icon (svg) - <a href="assets/images/logos/concept-icons/protractor.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -355,16 +355,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/pwa.png" alt="PWA Icon">
<img src="assets/images/logos/concept-icons/pwa.png" alt="PWA Icon">
</div>
<div>
<h3 class="l-space-left-3">PWA</h3>
<ul class="l-space-left-3">
<li>
<span>PWA Icon (png) - <a href="generated/images/marketing/concept-icons/pwa.png" download>Download</a></span>
<span>PWA Icon (png) - <a href="assets/images/logos/concept-icons/pwa.png" download>Download</a></span>
</li>
<li>
<span>PWA Icon (svg) - <a href="generated/images/marketing/concept-icons/pwa.svg" download>Download</a></span>
<span>PWA Icon (svg) - <a href="assets/images/logos/concept-icons/pwa.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -374,16 +374,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/router.png" alt="Router Icon">
<img src="assets/images/logos/concept-icons/router.png" alt="Router Icon">
</div>
<div>
<h3 class="l-space-left-3">ROUTER</h3>
<ul class="l-space-left-3">
<li>
<span>Router Icon (png) - <a href="generated/images/marketing/concept-icons/router.png" download>Download</a></span>
<span>Router Icon (png) - <a href="assets/images/logos/concept-icons/router.png" download>Download</a></span>
</li>
<li>
<span>Router Icon (svg) - <a href="generated/images/marketing/concept-icons/router.svg" download>Download</a></span>
<span>Router Icon (svg) - <a href="assets/images/logos/concept-icons/router.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -393,16 +393,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/universal.png" alt="Universal Icon">
<img src="assets/images/logos/concept-icons/universal.png" alt="Universal Icon">
</div>
<div>
<h3 class="l-space-left-3">UNIVERSAL</h3>
<ul class="l-space-left-3">
<li>
<span>Universal Icon (png) - <a href="generated/images/marketing/concept-icons/universal.png" download>Download</a></span>
<span>Universal Icon (png) - <a href="assets/images/logos/concept-icons/universal.png" download>Download</a></span>
</li>
<li>
<span>Universal Icon (svg) - <a href="generated/images/marketing/concept-icons/universal.svg" download>Download</a></span>
<span>Universal Icon (svg) - <a href="assets/images/logos/concept-icons/universal.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -420,16 +420,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/dependency-injection.png" alt="Dependency Injection Icon">
<img src="assets/images/logos/concept-icons/dependency-injection.png" alt="Dependency Injection Icon">
</div>
<div>
<h3 class="l-space-left-3">DEPENDENCY INJECTION</h3>
<ul class="l-space-left-3">
<li>
<span>Dependency Injection Icon (png) - <a href="generated/images/marketing/concept-icons/dependency-injection.png" download>Download</a></span>
<span>Dependency Injection Icon (png) - <a href="assets/images/logos/concept-icons/dependency-injection.png" download>Download</a></span>
</li>
<li>
<span>Dependency Injection Icon (svg) - <a href="generated/images/marketing/concept-icons/dependency-injection.svg" download>Download</a></span>
<span>Dependency Injection Icon (svg) - <a href="assets/images/logos/concept-icons/dependency-injection.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -439,16 +439,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/lazy-loading.png" alt="Lazy Loading Icon">
<img src="assets/images/logos/concept-icons/lazy-loading.png" alt="Lazy Loading Icon">
</div>
<div>
<h3 class="l-space-left-3">LAZY LOADING</h3>
<ul class="l-space-left-3">
<li>
<span>Lazy Loading Icon (png) - <a href="generated/images/marketing/concept-icons/lazy-loading.png" download>Download</a></span>
<span>Lazy Loading Icon (png) - <a href="assets/images/logos/concept-icons/lazy-loading.png" download>Download</a></span>
</li>
<li>
<span>Lazy Loading Icon (svg) - <a href="generated/images/marketing/concept-icons/lazy-loading.svg" download>Download</a></span>
<span>Lazy Loading Icon (svg) - <a href="assets/images/logos/concept-icons/lazy-loading.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -458,16 +458,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/libraries.png" alt="Libraries Icon">
<img src="assets/images/logos/concept-icons/libraries.png" alt="Libraries Icon">
</div>
<div>
<h3 class="l-space-left-3">LIBRARIES</h3>
<ul class="l-space-left-3">
<li>
<span>Libraries Icon (png) - <a href="generated/images/marketing/concept-icons/libraries.png" download>Download</a></span>
<span>Libraries Icon (png) - <a href="assets/images/logos/concept-icons/libraries.png" download>Download</a></span>
</li>
<li>
<span>Libraries Icon (svg) - <a href="generated/images/marketing/concept-icons/libraries.svg" download>Download</a></span>
<span>Libraries Icon (svg) - <a href="assets/images/logos/concept-icons/libraries.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -477,16 +477,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/performance.png" alt="Performance Icon">
<img src="assets/images/logos/concept-icons/performance.png" alt="Performance Icon">
</div>
<div>
<h3 class="l-space-left-3">PERFORMANCE</h3>
<ul class="l-space-left-3">
<li>
<span>Performance Icon (png) - <a href="generated/images/marketing/concept-icons/performance.png" download>Download</a></span>
<span>Performance Icon (png) - <a href="assets/images/logos/concept-icons/performance.png" download>Download</a></span>
</li>
<li>
<span>Performance Icon (svg) - <a href="generated/images/marketing/concept-icons/performance.svg" download>Download</a></span>
<span>Performance Icon (svg) - <a href="assets/images/logos/concept-icons/performance.svg" download>Download</a></span>
</li>
</ul>
</div>
@ -496,16 +496,16 @@
<div class="presskit-row">
<div class="presskit-inner">
<div class="presskit-image-container">
<img src="generated/images/marketing/concept-icons/templates.png" alt="Templates Icon">
<img src="assets/images/logos/concept-icons/templates.png" alt="Templates Icon">
</div>
<div>
<h3 class="l-space-left-3">TEMPLATES</h3>
<ul class="l-space-left-3">
<li>
<span>Templates Icon (png) - <a href="generated/images/marketing/concept-icons/templates.png" download>Download</a></span>
<span>Templates Icon (png) - <a href="assets/images/logos/concept-icons/templates.png" download>Download</a></span>
</li>
<li>
<span>Templates Icon (svg) - <a href="generated/images/marketing/concept-icons/templates.svg" download>Download</a></span>
<span>Templates Icon (svg) - <a href="assets/images/logos/concept-icons/templates.svg" download>Download</a></span>
</li>
</ul>
</div>

View File

@ -413,7 +413,7 @@
"desc": "TrulyUI is an Angular UI Framework especially developed for Desktop Applications based on Web Components using the greatest technologies of the world.",
"rev": true,
"title": "Truly UI",
"url": "http://truly-ui.com"
"url": "http://truly-ui.tk"
}
}
}

View File

@ -349,6 +349,11 @@
"title": "Dependency Injection",
"tooltip": "Dependency Injection: creating and injecting services",
"children": [
{
"url": "guide/dependency-injection-pattern",
"title": "The Dependency Injection pattern",
"tooltip": "Learn about the dependency injection pattern behind the Angular DI system."
},
{
"url": "guide/dependency-injection",
"title": "Angular Dependency Injection",
@ -357,22 +362,12 @@
{
"url": "guide/hierarchical-dependency-injection",
"title": "Hierarchical Injectors",
"tooltip": "An injector tree parallels the component tree and supports nested dependencies."
},
{
"url": "guide/dependency-injection-providers",
"title": "DI Providers",
"tooltip": "More about the different kinds of providers."
"tooltip": "Angular's hierarchical dependency injection system supports nested injectors in parallel with the component tree."
},
{
"url": "guide/dependency-injection-in-action",
"title": "DI in Action",
"tooltip": "Techniques for dependency injection."
},
{
"url": "guide/dependency-injection-navtree",
"title": "Navigate the Component Tree",
"tooltip": "Use the injection tree to find parent components."
"tooltip": "Techniques for Dependency Injection."
}
]
},

View File

@ -12,7 +12,7 @@ Using the Angular CLI, generate a new component named `heroes`.
ng generate component heroes
</code-example>
The CLI creates a new folder, `src/app/heroes/`, and generates
The CLI creates a new folder, `src/app/heroes/` and generates
the three files of the `HeroesComponent`.
The `HeroesComponent` class file is as follows:
@ -38,7 +38,7 @@ The CLI generated three metadata properties:
The [CSS element selector](https://developer.mozilla.org/en-US/docs/Web/CSS/Type_selectors),
`'app-heroes'`, matches the name of the HTML element that identifies this component within a parent component's template.
The `ngOnInit` is a [lifecycle hook](guide/lifecycle-hooks#oninit).
The `ngOnInit` is a [lifecycle hook](guide/lifecycle-hooks#oninit)
Angular calls `ngOnInit` shortly after creating a component.
It's a good place to put initialization logic.
@ -108,7 +108,7 @@ and show both `id` and `name` in a details layout like this:
title="heroes.component.html (HeroesComponent's template)" linenums="false">
</code-example>
The browser refreshes and displays the hero's information.
The browser refreshes and display's the hero's information.
## Format with the _UppercasePipe_

View File

@ -9,7 +9,7 @@
//////////////////////////////////////////////////////////////////////////////////////////////
// README:
// Redirects must also be handled by the ServiceWorker. If you add a redirect rule here,
// make sure it is compatible with the configuration in `ngsw-config.json`.
// make sure the routing RegExp in `ngsw-manifest.json` is updated accordingly.
//////////////////////////////////////////////////////////////////////////////////////////////
// A random bad indexed page that used `api/api`

View File

@ -1,127 +0,0 @@
{
"index": "/",
"assetGroups": [
{
"name": "app-shell",
"installMode": "prefetch",
"updateMode": "prefetch",
"resources": {
"files": [
"/pwa-manifest.json",
"/app/search/search-worker.js",
"/assets/images/favicons/favicon.ico",
"/assets/js/*.js",
"/*.css",
"/*.js"
],
"urls": [
"https://fonts.googleapis.com/**",
"https://fonts.gstatic.com/s/**",
"https://maxcdn.bootstrapcdn.com/**"
]
}
}, {
"name": "assets-eager",
"installMode": "prefetch",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/images/**",
"/generated/images/marketing/**",
"!/assets/images/favicons/**",
"!/**/_unused/**"
]
}
}, {
"name": "assets-lazy",
"installMode": "lazy",
"updateMode": "prefetch",
"resources": {
"files": [
"/assets/images/favicons/**",
"/generated/ie-polyfills.min.js",
"!/**/_unused/**"
]
}
}, {
"name": "docs-index",
"installMode": "prefetch",
"updateMode": "prefetch",
"resources": {
"files": [
"/generated/*.json",
"/generated/docs/*.json",
"/generated/docs/api/api-list.json",
"/generated/docs/app/search-data.json"
]
}
}, {
"name": "docs-lazy",
"installMode": "lazy",
"updateMode": "lazy",
"resources": {
"files": [
"/generated/docs/**/*.json",
"/generated/images/**",
"!/**/_unused/**"
]
}
}
],
"navigationUrls": [
"/**",
"!/**/*.*",
"!/**/*__*",
"!/**/*__*/**",
"!/**/stackblitz",
"!/**/stackblitz.html",
"!/api/*/**/*-*",
"!/api/**/AnimationStateDeclarationMetadata*",
"!/api/**/CORE_DIRECTIVES*",
"!/api/**/DirectiveMetadata*",
"!/api/**/HTTP_PROVIDERS*",
"!/api/**/NgFor",
"!/api/**/NgFor-*",
"!/api/**/PLATFORM_PIPES*",
"!/api/animate/**",
"!/api/api/**",
"!/api/common/Control*",
"!/api/common/MaxLengthValidator*",
"!/api/common/NgModel*",
"!/api/platform-browser/AnimationDriver*",
"!/api/testing/**",
"!/docs/?*",
"!/docs/*/**",
"!/guide/cli-quickstart",
"!/guide/cli-quickstart.html",
"!/guide/cli-quickstart/",
"!/guide/learning-angular",
"!/guide/learning-angular.html",
"!/guide/learning-angular/",
"!/guide/metadata",
"!/guide/metadata.html",
"!/guide/metadata/",
"!/guide/ngmodule",
"!/guide/ngmodule.html",
"!/guide/ngmodule/",
"!/guide/service-worker-getstart",
"!/guide/service-worker-getstart.html",
"!/guide/service-worker-getstart/",
"!/guide/service-worker-comm",
"!/guide/service-worker-comm.html",
"!/guide/service-worker-comm/",
"!/guide/service-worker-configref",
"!/guide/service-worker-configref.html",
"!/guide/service-worker-configref/",
"!/guide/webpack",
"!/guide/webpack.html",
"!/guide/webpack/",
"!/news",
"!/news.html",
"!/news/",
"!/styleguide",
"!/styleguide/**",
"!/testing",
"!/testing/**"
]
}

26
aio/ngsw-manifest.json Normal file
View File

@ -0,0 +1,26 @@
{
"external": {
"urls": [
{"url": "https://fonts.googleapis.com/css?family=Droid+Sans+Mono"},
{"url": "https://fonts.gstatic.com/s/droidsansmono/v7/ns-m2xQYezAtqh7ai59hJYdJ2JT0J65PSe7wdxAnx_I.woff2"},
{"url": "https://fonts.googleapis.com/icon?family=Material+Icons"},
{"url": "https://fonts.gstatic.com/s/materialicons/v22/2fcrYFNaTjcS6g4U3t-Y5ZjZjT5FdEJ140U2DJYC3mY.woff2"},
{"url": "https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"}
]
},
"static.ignore": [
"\\.js\\.map$",
"^(?:/|\\\\)generated(?:/|\\\\)(?:docs(?:/|\\\\)(?!api(?:/|\\\\)api-list\\.json).*|images(?:/|\\\\)(?!marketing(?:/|\\\\)).*|live-examples|zips)(?:/|\\\\)"
],
"static.versioned": [
"\\.[0-9a-z]{20}\\."
],
"routing": {
"index": "/index.html",
"routes": {
"^(?!/styleguide|/docs/.|(?:/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref)|learning-angular|webpack)|/news)(?:\\.html|/)?$|/testing|/api/(?:.+/[^/]+-|platform-browser/AnimationDriver|testing/|api/|animate/|(?:common/(?:NgModel|Control|MaxLengthValidator))|(?:[^/]+/)?(?:NgFor(?:$|-)|AnimationStateDeclarationMetadata|CORE_DIRECTIVES|PLATFORM_PIPES|DirectiveMetadata|HTTP_PROVIDERS))|.*/stackblitz(?:\\.html)?(?:\\?.*)?$|.*\\.[^\/.]+$)": {
"match": "regex"
}
}
}
}

View File

@ -8,7 +8,7 @@
"scripts": {
"preinstall": "node ../tools/yarn/check-yarn.js",
"postinstall": "node tools/cli-patches/patch.js && uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug",
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug --ignore-packages @angular/service-worker",
"aio-use-npm": "node tools/ng-packages-installer restore .",
"aio-check-local": "node tools/ng-packages-installer check .",
"ng": "yarn check-env && ng",
@ -22,7 +22,7 @@
"test": "yarn check-env && ng test",
"pree2e": "yarn check-env && yarn update-webdriver",
"e2e": "ng e2e --no-webdriver-update",
"presetup": "yarn --cwd .. install && yarn install --frozen-lockfile && yarn ~~check-env && yarn boilerplate:remove",
"presetup": "yarn install --frozen-lockfile && yarn ~~check-env && yarn boilerplate:remove",
"setup": "yarn aio-use-npm && yarn example-use-npm",
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn docs",
"presetup-local": "yarn presetup",
@ -55,12 +55,14 @@
"boilerplate:test": "node tools/examples/test.js",
"generate-stackblitz": "node ./tools/stackblitz-builder/generateStackblitz",
"generate-zips": "node ./tools/example-zipper/generateZips",
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
"build-404-page": "node scripts/build-404-page",
"build-ie-polyfills": "yarn webpack-cli src/ie-polyfills.js -o src/generated/ie-polyfills.min.js --mode production",
"update-webdriver": "webdriver-manager update --standalone false --gecko false $CHROMEDRIVER_VERSION_ARG",
"~~check-env": "node scripts/check-environment",
"~~build": "ng build",
"post~~build": "yarn build-404-page"
"post~~build": "yarn build-404-page && yarn sw-manifest && yarn sw-copy"
},
"engines": {
"node": ">=8.9.1 <9.0.0",
@ -68,31 +70,36 @@
},
"private": true,
"dependencies": {
"@angular/animations": "6.1.0-rc.3",
"@angular/cdk": "6.0.2",
"@angular/common": "6.1.0-rc.3",
"@angular/core": "6.1.0-rc.3",
"@angular/elements": "6.1.0-rc.3",
"@angular/forms": "6.1.0-rc.3",
"@angular/material": "6.0.2",
"@angular/platform-browser": "6.1.0-rc.3",
"@angular/platform-browser-dynamic": "6.1.0-rc.3",
"@angular/router": "6.1.0-rc.3",
"@angular/service-worker": "6.1.0-rc.3",
"@webcomponents/custom-elements": "^1.2.0",
"@angular/animations": "6.0.0",
"@angular/cdk": "6.0.0",
"@angular/common": "6.0.0",
"@angular/core": "6.0.0",
"@angular/elements": "6.0.0",
"@angular/forms": "6.0.0",
"@angular/http": "6.0.0",
"@angular/material": "6.0.0",
"@angular/platform-browser": "6.0.0",
"@angular/platform-browser-dynamic": "6.0.0",
"@angular/platform-server": "6.0.0",
"@angular/router": "6.0.0",
"@angular/service-worker": "^1.0.0-beta.16",
"@webcomponents/custom-elements": "^1.0.8",
"classlist.js": "^1.1.20150312",
"core-js": "^2.4.1",
"rxjs": "6.2.2",
"jasmine": "^2.6.0",
"ng-pwa-tools": "^0.0.10",
"rxjs": "6.0.0-uncanny-rc.7",
"rxjs-compat": "6.0.0-uncanny-rc.7",
"tslib": "^1.9.0",
"web-animations-js": "^2.2.5",
"zone.js": "^0.8.26"
},
"devDependencies": {
"@angular-devkit/build-angular": "0.6.7",
"@angular/cli": "^6.1.0-rc.3",
"@angular/compiler": "6.1.0-rc.3",
"@angular/compiler-cli": "6.1.0-rc.3",
"@angular/language-service": "6.1.0-rc.3",
"@angular-devkit/build-angular": "^0.6.0",
"@angular/cli": "^6.0.0",
"@angular/compiler": "6.0.0",
"@angular/compiler-cli": "6.0.0",
"@angular/language-service": "6.0.0",
"@types/jasmine": "^2.5.52",
"@types/jasminewd2": "^2.0.3",
"@types/node": "~6.0.60",
@ -118,7 +125,6 @@
"http-server": "^0.9.0",
"ignore": "^3.3.3",
"image-size": "^0.5.1",
"jasmine": "^2.6.0",
"jasmine-core": "^2.8.0",
"jasmine-marbles": "^0.3.1",
"jasmine-spec-reporter": "^4.1.0",
@ -144,7 +150,7 @@
"tree-kill": "^1.1.0",
"ts-node": "^3.3.0",
"tslint": "~5.9.1",
"typescript": "^2.9.2",
"typescript": "~2.7.2",
"uglify-js": "^3.0.15",
"unist-util-filter": "^0.2.1",
"unist-util-source": "^1.0.1",

View File

@ -3,8 +3,8 @@
"master": {
"uncompressed": {
"runtime": 2768,
"main": 476338,
"polyfills": 53922,
"main": 475855,
"polyfills": 38453,
"prettify": 14913
}
}

View File

@ -2,7 +2,6 @@ import { BrowserModule } from '@angular/platform-browser';
import { ErrorHandler, NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { ServiceWorkerModule } from '@angular/service-worker';
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
@ -41,71 +40,51 @@ import { CustomElementsModule } from 'app/custom-elements/custom-elements.module
import { SharedModule } from 'app/shared/shared.module';
import { SwUpdatesModule } from 'app/sw-updates/sw-updates.module';
import {environment} from '../environments/environment';
// These are the hardcoded inline svg sources to be used by the `<mat-icon>` component.
// tslint:disable: max-line-length
// These are the hardcoded inline svg sources to be used by the `<mat-icon>` component
export const svgIconProviders = [
{
provide: SVG_ICONS,
useValue: {
name: 'close',
svgSource:
'<svg fill="#ffffff" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z" />' +
'<path d="M0 0h24v24H0z" fill="none" />' +
'</svg>',
name: 'keyboard_arrow_right',
svgSource: '<svg xmlns="http://www.w3.org/2000/svg" focusable="false" ' +
'viewBox="0 0 24 24"><path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z"/></svg>'
},
multi: true,
multi: true
},
{
provide: SVG_ICONS,
useValue: {
name: 'error_outline',
svgSource:
'<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M0 0h24v24H0V0z" fill="none" />' +
'<path d="M11 15h2v2h-2zm0-8h2v6h-2zm.99-5C6.47 2 2 6.48 2 12s4.47 10 9.99 10C17.52 22 22 17.52 22 12S17.52 2 11.99 2zM12 20c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z" />' +
'</svg>',
name: 'menu',
svgSource: '<svg xmlns="http://www.w3.org/2000/svg" focusable="false" ' +
'viewBox="0 0 24 24"><path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z"/></svg>'
},
multi: true,
multi: true
},
{
provide: SVG_ICONS,
useValue: {
name: 'insert_comment',
svgSource:
'<svg fill="#ffffff" focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z" />' +
'<path d="M0 0h24v24H0z" fill="none" />' +
'</svg>',
'<svg fill="#FFFFFF" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"/>' +
'<path d="M0 0h24v24H0z" fill="none"/>' +
'</svg>'
},
multi: true,
multi: true
},
{
provide: SVG_ICONS,
useValue: {
name: 'keyboard_arrow_right',
name: 'close',
svgSource:
'<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M8.59 16.34l4.58-4.59-4.58-4.59L10 5.75l6 6-6 6z" />' +
'</svg>',
'<svg fill="#ffffff" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>' +
'<path d="M0 0h24v24H0z" fill="none"/>' +
'</svg>'
},
multi: true,
},
{
provide: SVG_ICONS,
useValue: {
name: 'menu',
svgSource:
'<svg focusable="false" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">' +
'<path d="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />' +
'</svg>',
},
multi: true,
},
multi: true
}
];
// tslint:enable: max-line-length
@NgModule({
imports: [
@ -120,7 +99,6 @@ export const svgIconProviders = [
MatToolbarModule,
SwUpdatesModule,
SharedModule,
ServiceWorkerModule.register('/ngsw-worker.js', {enabled: environment.production}),
],
declarations: [
AppComponent,

View File

@ -10,7 +10,6 @@
<aio-select (change)="setStatus($event.option)"
[options]="statuses"
[selected]="status"
[disabled]="type.value === 'package'"
label="Status:">
</aio-select>

View File

@ -5,7 +5,7 @@
var SEARCH_TERMS_URL = '/generated/docs/app/search-data.json';
// NOTE: This needs to be kept in sync with `ngsw-config.json`.
// NOTE: This needs to be kept in sync with `ngsw-manifest.json`.
importScripts('/assets/js/lunr.min.js');
var index;

View File

@ -1,5 +1,5 @@
<div class="form-select-menu">
<button class="form-select-button" (click)="toggleOptions()" [disabled]="disabled">
<button class="form-select-button" (click)="toggleOptions()">
<strong>{{label}}</strong><span *ngIf="showSymbol" class="symbol {{selected?.value}}"></span>{{selected?.title}}
</button>
<ul class="form-select-dropdown" *ngIf="showOptions">

View File

@ -67,28 +67,6 @@ describe('SelectComponent', () => {
fixture.detectChanges();
expect(getOptionContainer()).toEqual(null);
});
it('should be disabled if the component is disabled', () => {
host.options = options;
fixture.detectChanges();
expect(getButton().disabled).toBe(false);
expect(getButton().getAttribute('disabled')).toBe(null);
host.disabled = true;
fixture.detectChanges();
expect(getButton().disabled).toBe(true);
expect(getButton().getAttribute('disabled')).toBeDefined();
});
it('should not toggle the visibility of the options list if disabled', () => {
host.options = options;
host.disabled = true;
fixture.detectChanges();
getButton().click();
fixture.detectChanges();
expect(getOptionContainer()).toEqual(null);
});
});
describe('options list', () => {
@ -160,8 +138,7 @@ describe('SelectComponent', () => {
[options]="options"
[selected]="selected"
[label]="label"
[showSymbol]="showSymbol"
[disabled]="disabled">
[showSymbol]="showSymbol">
</aio-select>`
})
class HostComponent {
@ -170,7 +147,6 @@ class HostComponent {
selected: Option;
label: string;
showSymbol: boolean;
disabled: boolean;
}
function getButton(): HTMLButtonElement {

View File

@ -25,9 +25,6 @@ export class SelectComponent implements OnInit {
@Input()
label: string;
@Input()
disabled: boolean;
showOptions = false;
constructor(private hostElement: ElementRef) {}

View File

@ -1,9 +1,13 @@
import { NgModule } from '@angular/core';
import { ServiceWorkerModule } from '@angular/service-worker';
import { SwUpdatesService } from './sw-updates.service';
@NgModule({
imports: [
ServiceWorkerModule
],
providers: [
SwUpdatesService
]

View File

@ -1,41 +1,41 @@
import { ApplicationRef, ReflectiveInjector } from '@angular/core';
import { discardPeriodicTasks, fakeAsync, tick } from '@angular/core/testing';
import { SwUpdate } from '@angular/service-worker';
import { fakeAsync, tick } from '@angular/core/testing';
import { NgServiceWorker } from '@angular/service-worker';
import { Subject } from 'rxjs';
import { take } from 'rxjs/operators';
import { Logger } from 'app/shared/logger.service';
import { SwUpdatesService } from './sw-updates.service';
describe('SwUpdatesService', () => {
let injector: ReflectiveInjector;
let appRef: MockApplicationRef;
let service: SwUpdatesService;
let swu: MockSwUpdate;
let sw: MockNgServiceWorker;
let checkInterval: number;
// Helpers
// NOTE:
// Because `SwUpdatesService` uses the `interval` operator, it needs to be instantiated and
// Because `SwUpdatesService` uses the `debounceTime` operator, it needs to be instantiated and
// destroyed inside the `fakeAsync` zone (when `fakeAsync` is used for the test). Thus, we can't
// run `setup()`/`tearDown()` in `beforeEach()`/`afterEach()` blocks. We use the `run()` helper
// to call them inside each test's zone.
const setup = (isSwUpdateEnabled: boolean) => {
const setup = () => {
injector = ReflectiveInjector.resolveAndCreate([
{ provide: ApplicationRef, useClass: MockApplicationRef },
{ provide: Logger, useClass: MockLogger },
{ provide: SwUpdate, useFactory: () => new MockSwUpdate(isSwUpdateEnabled) },
{ provide: NgServiceWorker, useClass: MockNgServiceWorker },
SwUpdatesService
]);
appRef = injector.get(ApplicationRef);
service = injector.get(SwUpdatesService);
swu = injector.get(SwUpdate);
sw = injector.get(NgServiceWorker);
checkInterval = (service as any).checkInterval;
};
const tearDown = () => service.ngOnDestroy();
const run = (specFn: VoidFunction, isSwUpdateEnabled = true) => () => {
setup(isSwUpdateEnabled);
const run = (specFn: VoidFunction) => () => {
setup();
specFn();
tearDown();
};
@ -46,153 +46,109 @@ describe('SwUpdatesService', () => {
}));
it('should start checking for updates when instantiated (once the app stabilizes)', run(() => {
expect(swu.checkForUpdate).not.toHaveBeenCalled();
expect(sw.checkForUpdate).not.toHaveBeenCalled();
appRef.isStable.next(false);
expect(swu.checkForUpdate).not.toHaveBeenCalled();
expect(sw.checkForUpdate).not.toHaveBeenCalled();
appRef.isStable.next(true);
expect(swu.checkForUpdate).toHaveBeenCalled();
expect(sw.checkForUpdate).toHaveBeenCalled();
}));
it('should periodically check for updates', fakeAsync(run(() => {
it('should schedule a new check if there is no update available', fakeAsync(run(() => {
appRef.isStable.next(true);
swu.checkForUpdate.calls.reset();
sw.checkForUpdate.calls.reset();
sw.$$checkForUpdateSubj.next(false);
expect(sw.checkForUpdate).not.toHaveBeenCalled();
tick(checkInterval);
expect(swu.checkForUpdate).toHaveBeenCalledTimes(1);
tick(checkInterval);
expect(swu.checkForUpdate).toHaveBeenCalledTimes(2);
appRef.isStable.next(false);
tick(checkInterval);
expect(swu.checkForUpdate).toHaveBeenCalledTimes(3);
discardPeriodicTasks();
expect(sw.checkForUpdate).toHaveBeenCalled();
expect(sw.activateUpdate).not.toHaveBeenCalled();
})));
it('should activate available updates immediately', fakeAsync(run(() => {
it('should activate new updates immediately', fakeAsync(run(() => {
appRef.isStable.next(true);
expect(swu.activateUpdate).not.toHaveBeenCalled();
sw.checkForUpdate.calls.reset();
swu.$$availableSubj.next({available: {hash: 'foo'}});
expect(swu.activateUpdate).toHaveBeenCalled();
sw.$$checkForUpdateSubj.next(true);
expect(sw.checkForUpdate).not.toHaveBeenCalled();
tick(checkInterval);
expect(sw.checkForUpdate).not.toHaveBeenCalled();
expect(sw.activateUpdate).toHaveBeenCalled();
})));
it('should keep periodically checking for updates even after one is available/activated', fakeAsync(run(() => {
it('should not pass a specific version to `NgServiceWorker.activateUpdate()`', fakeAsync(run(() => {
appRef.isStable.next(true);
swu.checkForUpdate.calls.reset();
sw.$$checkForUpdateSubj.next(true);
tick(checkInterval);
expect(sw.activateUpdate).toHaveBeenCalledWith(null);
})));
it('should schedule a new check after activating the update', fakeAsync(run(() => {
appRef.isStable.next(true);
sw.checkForUpdate.calls.reset();
sw.$$checkForUpdateSubj.next(true);
tick(checkInterval);
expect(swu.checkForUpdate).toHaveBeenCalledTimes(1);
expect(sw.checkForUpdate).not.toHaveBeenCalled();
swu.$$availableSubj.next({available: {hash: 'foo'}});
sw.$$activateUpdateSubj.next();
expect(sw.checkForUpdate).not.toHaveBeenCalled();
tick(checkInterval);
expect(swu.checkForUpdate).toHaveBeenCalledTimes(2);
tick(checkInterval);
expect(swu.checkForUpdate).toHaveBeenCalledTimes(3);
discardPeriodicTasks();
expect(sw.checkForUpdate).toHaveBeenCalled();
})));
it('should emit on `updateActivated` when an update has been activated', run(() => {
const activatedVersions: (string|undefined)[] = [];
service.updateActivated.subscribe(v => activatedVersions.push(v));
swu.$$availableSubj.next({available: {hash: 'foo'}});
swu.$$activatedSubj.next({current: {hash: 'bar'}});
swu.$$availableSubj.next({available: {hash: 'baz'}});
swu.$$activatedSubj.next({current: {hash: 'qux'}});
sw.$$updatesSubj.next({type: 'pending', version: 'foo'});
sw.$$updatesSubj.next({type: 'activation', version: 'bar'});
sw.$$updatesSubj.next({type: 'pending', version: 'baz'});
sw.$$updatesSubj.next({type: 'activation', version: 'qux'});
expect(activatedVersions).toEqual(['bar', 'qux']);
}));
describe('when `SwUpdate` is not enabled', () => {
const runDeactivated = (specFn: VoidFunction) => run(specFn, false);
it('should not check for updates', fakeAsync(runDeactivated(() => {
appRef.isStable.next(true);
tick(checkInterval);
tick(checkInterval);
swu.$$availableSubj.next({available: {hash: 'foo'}});
swu.$$activatedSubj.next({current: {hash: 'bar'}});
tick(checkInterval);
tick(checkInterval);
expect(swu.checkForUpdate).not.toHaveBeenCalled();
})));
it('should not activate available updates', fakeAsync(runDeactivated(() => {
swu.$$availableSubj.next({available: {hash: 'foo'}});
expect(swu.activateUpdate).not.toHaveBeenCalled();
})));
it('should never emit on `updateActivated`', runDeactivated(() => {
const activatedVersions: (string|undefined)[] = [];
service.updateActivated.subscribe(v => activatedVersions.push(v));
swu.$$availableSubj.next({available: {hash: 'foo'}});
swu.$$activatedSubj.next({current: {hash: 'bar'}});
swu.$$availableSubj.next({available: {hash: 'baz'}});
swu.$$activatedSubj.next({current: {hash: 'qux'}});
expect(activatedVersions).toEqual([]);
}));
});
describe('when destroyed', () => {
it('should not schedule a new check for update (after current check)', fakeAsync(run(() => {
appRef.isStable.next(true);
expect(swu.checkForUpdate).toHaveBeenCalled();
sw.checkForUpdate.calls.reset();
service.ngOnDestroy();
swu.checkForUpdate.calls.reset();
tick(checkInterval);
sw.$$checkForUpdateSubj.next(false);
tick(checkInterval);
expect(swu.checkForUpdate).not.toHaveBeenCalled();
expect(sw.checkForUpdate).not.toHaveBeenCalled();
})));
it('should not schedule a new check for update (after activating an update)', fakeAsync(run(() => {
appRef.isStable.next(true);
expect(swu.checkForUpdate).toHaveBeenCalled();
sw.checkForUpdate.calls.reset();
sw.$$checkForUpdateSubj.next(true);
expect(sw.activateUpdate).toHaveBeenCalled();
service.ngOnDestroy();
swu.checkForUpdate.calls.reset();
swu.$$availableSubj.next({available: {hash: 'foo'}});
swu.$$activatedSubj.next({current: {hash: 'baz'}});
tick(checkInterval);
sw.$$activateUpdateSubj.next();
tick(checkInterval);
expect(swu.checkForUpdate).not.toHaveBeenCalled();
})));
it('should not activate available updates', fakeAsync(run(() => {
service.ngOnDestroy();
swu.$$availableSubj.next({available: {hash: 'foo'}});
expect(swu.activateUpdate).not.toHaveBeenCalled();
expect(sw.checkForUpdate).not.toHaveBeenCalled();
})));
it('should stop emitting on `updateActivated`', run(() => {
const activatedVersions: (string|undefined)[] = [];
service.updateActivated.subscribe(v => activatedVersions.push(v));
swu.$$availableSubj.next({available: {hash: 'foo'}});
swu.$$activatedSubj.next({current: {hash: 'bar'}});
sw.$$updatesSubj.next({type: 'pending', version: 'foo'});
sw.$$updatesSubj.next({type: 'activation', version: 'bar'});
service.ngOnDestroy();
swu.$$availableSubj.next({available: {hash: 'baz'}});
swu.$$activatedSubj.next({current: {hash: 'qux'}});
sw.$$updatesSubj.next({type: 'pending', version: 'baz'});
sw.$$updatesSubj.next({type: 'activation', version: 'qux'});
expect(activatedVersions).toEqual(['bar']);
}));
@ -208,18 +164,16 @@ class MockLogger {
log = jasmine.createSpy('MockLogger.log');
}
class MockSwUpdate {
$$availableSubj = new Subject<{available: {hash: string}}>();
$$activatedSubj = new Subject<{current: {hash: string}}>();
class MockNgServiceWorker {
$$activateUpdateSubj = new Subject<boolean>();
$$checkForUpdateSubj = new Subject<boolean>();
$$updatesSubj = new Subject<{type: string, version: string}>();
available = this.$$availableSubj.asObservable();
activated = this.$$activatedSubj.asObservable();
updates = this.$$updatesSubj.asObservable();
activateUpdate = jasmine.createSpy('MockSwUpdate.activateUpdate')
.and.callFake(() => Promise.resolve());
activateUpdate = jasmine.createSpy('MockNgServiceWorker.activateUpdate')
.and.callFake(() => this.$$activateUpdateSubj.pipe(take(1)));
checkForUpdate = jasmine.createSpy('MockSwUpdate.checkForUpdate')
.and.callFake(() => Promise.resolve());
constructor(public isEnabled: boolean) {}
checkForUpdate = jasmine.createSpy('MockNgServiceWorker.checkForUpdate')
.and.callFake(() => this.$$checkForUpdateSubj.pipe(take(1)));
}

View File

@ -1,7 +1,7 @@
import { ApplicationRef, Injectable, OnDestroy } from '@angular/core';
import { SwUpdate } from '@angular/service-worker';
import { concat, interval, NEVER, Observable, Subject } from 'rxjs';
import { first, map, takeUntil, tap } from 'rxjs/operators';
import { NgServiceWorker } from '@angular/service-worker';
import { concat, Subject } from 'rxjs';
import { debounceTime, defaultIfEmpty, filter, first, map, startWith, takeUntil, tap } from 'rxjs/operators';
import { Logger } from 'app/shared/logger.service';
@ -11,55 +11,63 @@ import { Logger } from 'app/shared/logger.service';
*
* @description
* 1. Checks for available ServiceWorker updates once instantiated.
* 2. Re-checks every 6 hours.
* 3. Whenever an update is available, it activates the update.
* 2. As long as there is no update available, re-checks every 6 hours.
* 3. As soon as an update is detected, it activates the update and notifies interested parties.
* 4. It continues to check for available updates.
*
* @property
* `updateActivated` {Observable<string>} - Emit the version hash whenever an update is activated.
*/
@Injectable()
export class SwUpdatesService implements OnDestroy {
private checkInterval = 1000 * 60 * 60 * 6; // 6 hours
private checkInterval = 1000 * 60 * 60 * 6; // 6 hours
private onDestroy = new Subject<void>();
updateActivated: Observable<string>;
private checkForUpdateSubj = new Subject<void>();
updateActivated = this.sw.updates.pipe(
takeUntil(this.onDestroy),
tap(evt => this.log(`Update event: ${JSON.stringify(evt)}`)),
filter(({type}) => type === 'activation'),
map(({version}) => version),
);
constructor(appRef: ApplicationRef, private logger: Logger, private swu: SwUpdate) {
if (!swu.isEnabled) {
this.updateActivated = NEVER.pipe(takeUntil(this.onDestroy));
return;
}
constructor(appRef: ApplicationRef, private logger: Logger, private sw: NgServiceWorker) {
const appIsStable$ = appRef.isStable.pipe(first(v => v));
const checkForUpdates$ = this.checkForUpdateSubj.pipe(debounceTime(this.checkInterval), startWith<void>(undefined));
// Periodically check for updates (after the app is stabilized).
const appIsStable = appRef.isStable.pipe(first(v => v));
concat(appIsStable, interval(this.checkInterval))
.pipe(
tap(() => this.log('Checking for update...')),
takeUntil(this.onDestroy),
)
.subscribe(() => this.swu.checkForUpdate());
// Activate available updates.
this.swu.available
.pipe(
tap(evt => this.log(`Update available: ${JSON.stringify(evt)}`)),
takeUntil(this.onDestroy),
)
.subscribe(() => this.swu.activateUpdate());
// Notify about activated updates.
this.updateActivated = this.swu.activated.pipe(
tap(evt => this.log(`Update activated: ${JSON.stringify(evt)}`)),
map(evt => evt.current.hash),
takeUntil(this.onDestroy),
);
concat(appIsStable$, checkForUpdates$)
.pipe(takeUntil(this.onDestroy))
.subscribe(() => this.checkForUpdate());
}
ngOnDestroy() {
this.onDestroy.next();
}
private activateUpdate() {
this.log('Activating update...');
this.sw.activateUpdate(null as any) // expects a non-null string
.subscribe(() => this.scheduleCheckForUpdate());
}
private checkForUpdate() {
this.log('Checking for update...');
this.sw.checkForUpdate()
.pipe(
// Temp workaround for https://github.com/angular/mobile-toolkit/pull/137.
// TODO (gkalpak): Remove once #137 is fixed.
defaultIfEmpty(false),
first(),
tap(v => this.log(`Update available: ${v}`)),
)
.subscribe(v => v ? this.activateUpdate() : this.scheduleCheckForUpdate());
}
private log(message: string) {
const timestamp = (new Date).toISOString();
this.logger.log(`[SwUpdates - ${timestamp}]: ${message}`);
}
private scheduleCheckForUpdate() {
this.checkForUpdateSubj.next();
}
}

View File

Before

Width:  |  Height:  |  Size: 4.6 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 908 B

After

Width:  |  Height:  |  Size: 908 B

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 757 B

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 815 B

After

Width:  |  Height:  |  Size: 815 B

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 876 B

After

Width:  |  Height:  |  Size: 876 B

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 916 B

After

Width:  |  Height:  |  Size: 916 B

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 950 B

After

Width:  |  Height:  |  Size: 950 B

View File

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 766 B

After

Width:  |  Height:  |  Size: 766 B

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 858 B

After

Width:  |  Height:  |  Size: 858 B

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

Before

Width:  |  Height:  |  Size: 833 B

After

Width:  |  Height:  |  Size: 833 B

View File

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 936 B

After

Width:  |  Height:  |  Size: 936 B

View File

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

Before

Width:  |  Height:  |  Size: 786 B

After

Width:  |  Height:  |  Size: 786 B

View File

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

Before

Width:  |  Height:  |  Size: 1014 B

After

Width:  |  Height:  |  Size: 1014 B

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

Before

Width:  |  Height:  |  Size: 805 B

After

Width:  |  Height:  |  Size: 805 B

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 612 B

After

Width:  |  Height:  |  Size: 612 B

View File

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

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