mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
Compare commits
229 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 263bbd43c1 | |||
| 73a3ff11cb | |||
| 4a32579a4d | |||
| 8beead1db2 | |||
| 0d5fbea5dc | |||
| 3a39fef6b6 | |||
| 48d42e57e4 | |||
| ff86f919ab | |||
| 3b884ed1fc | |||
| f1c64b3f38 | |||
| dcbf600bbc | |||
| 9aa0ef964e | |||
| 181037cd21 | |||
| 36f20919b3 | |||
| af8c6d39a5 | |||
| 8bbe791c75 | |||
| 01a3bff576 | |||
| 9d04484b10 | |||
| 2c6ec052c7 | |||
| cb1400d28f | |||
| f926b25e07 | |||
| 3464a99f18 | |||
| 44aa0d71be | |||
| fff93ae8f3 | |||
| 54abe9f66c | |||
| fc07b71d13 | |||
| 0457aa1b6e | |||
| 3e813a1618 | |||
| f8055f6fb8 | |||
| c8233a74b8 | |||
| 4e84b2f3ac | |||
| 170692c18c | |||
| 972eddc1ff | |||
| 808bded9b0 | |||
| 7bf201ebe8 | |||
| 501e15eb98 | |||
| 9a887e35c6 | |||
| a970c19f02 | |||
| c8a2e3ae1c | |||
| e74365dd35 | |||
| 572e3a356c | |||
| 29a99e806e | |||
| 5f81eae238 | |||
| fead4648a2 | |||
| cc99aec703 | |||
| 44319c4d82 | |||
| 3f6aed69a4 | |||
| 4943dba322 | |||
| 53865c4517 | |||
| 5296ed6d40 | |||
| 6794456a1c | |||
| 4906bf6a50 | |||
| 5412cdfe43 | |||
| 8bb3550fe2 | |||
| d39f39115e | |||
| b7d9761008 | |||
| 9db442fe56 | |||
| 4a23b2bcb1 | |||
| fb0ce2178a | |||
| ab8ea87078 | |||
| c20e3047a5 | |||
| bc816de4b5 | |||
| 8773385d8a | |||
| d876e4e6c3 | |||
| 7b29cf7326 | |||
| fb4cd6f39c | |||
| 2e52350c63 | |||
| 96bf63fec5 | |||
| 6c3a8d9564 | |||
| e005228e5b | |||
| 6bdc11e8cc | |||
| 39638a768f | |||
| 7f09945771 | |||
| 867c5aec09 | |||
| f434eadc0d | |||
| 0b401cce51 | |||
| 52144948fa | |||
| 84b01127a7 | |||
| 7b4a8c898e | |||
| 378bf62938 | |||
| 91c68f498e | |||
| b5f20700c2 | |||
| 7e3815cdc9 | |||
| d3d02f8897 | |||
| a2fba9e2a5 | |||
| aa5c42af7e | |||
| 76dfcfbf4b | |||
| 1516b5d391 | |||
| ebbcf0cc04 | |||
| 8225bb5b57 | |||
| f41637c459 | |||
| 9482a4df4c | |||
| 87ac05290a | |||
| 4c6d557ca9 | |||
| 75fb76c63b | |||
| ac645ec951 | |||
| 925e2b7434 | |||
| b42497144d | |||
| 0227ca83ae | |||
| 85a5336efb | |||
| bba1981c6e | |||
| a2a5b4add5 | |||
| ce80d5ee19 | |||
| 6ee5cdce9d | |||
| cadee6d63f | |||
| 006a523815 | |||
| 9cbd9bed55 | |||
| 636b6b4f94 | |||
| c23d029473 | |||
| e3f09ce845 | |||
| cfbb1faa6f | |||
| e2bb954a7a | |||
| 5795c980b5 | |||
| e99c4e230f | |||
| d36890c854 | |||
| 5adb47ff35 | |||
| 7e0ff8cbb6 | |||
| bf85f04baf | |||
| fffefd12e5 | |||
| 9ae21ee118 | |||
| 95446fb15c | |||
| ba6d8d44b1 | |||
| 3b0c51e045 | |||
| 80c21bf6b0 | |||
| c0d1e34d53 | |||
| 6973a9919b | |||
| 415ad8df13 | |||
| 9aa3e9f0fc | |||
| 67333a98b9 | |||
| 24600ff77d | |||
| 74cb759c2a | |||
| a4e396dbcf | |||
| 1e064074ac | |||
| 621c32d22a | |||
| 305d41c0b0 | |||
| 36a3a6af7f | |||
| 52da337ca5 | |||
| ceafef1a6a | |||
| bc7be8f1ab | |||
| cd1b76478f | |||
| 57d46d4293 | |||
| 13857519e4 | |||
| 35c3d80f1d | |||
| 900bc87d20 | |||
| 86a6be753d | |||
| 22f734f102 | |||
| 63f6273346 | |||
| 98defb6e68 | |||
| 345eccad8e | |||
| 49c108e33c | |||
| adcba1b9b2 | |||
| b282a94c31 | |||
| d79e62292c | |||
| 8505865587 | |||
| d05f157209 | |||
| 9bab583c58 | |||
| ea3ec5361e | |||
| afce2987d1 | |||
| 4e78e2196a | |||
| 5766c2d52f | |||
| 7e6595b59b | |||
| 58ee0133e4 | |||
| b8d6e15447 | |||
| 7a68915d18 | |||
| 1e4c39ada8 | |||
| b12e798685 | |||
| b0613b7329 | |||
| 9738878fbf | |||
| 81c496d0a4 | |||
| 9675260a56 | |||
| 8216de27ed | |||
| 7a45153266 | |||
| c3ed990f47 | |||
| 07e071cec0 | |||
| 3c1d9860d0 | |||
| ba16665073 | |||
| d11cb25f4d | |||
| b01153cf84 | |||
| 698efeed63 | |||
| eda6c6d5c9 | |||
| 506b6a2ab6 | |||
| 3989a0a240 | |||
| 6345f89825 | |||
| 4d05f533a8 | |||
| 192de59ddc | |||
| 7c1453614b | |||
| 11cc4c9dcf | |||
| 737bc7a3e5 | |||
| b1d589712c | |||
| 58f8ba0a8c | |||
| 6ac05c05e9 | |||
| 7c38a4a037 | |||
| 0b1f1930b9 | |||
| 4877f687b5 | |||
| 5c547675b1 | |||
| 1fbbb1e34d | |||
| 4cc15fe78e | |||
| 07a3d7a16b | |||
| 21994ee985 | |||
| f25a1b9597 | |||
| fa3f310745 | |||
| e66b89a4e4 | |||
| 95d8f58393 | |||
| 2fc618471a | |||
| 08252b2c5e | |||
| 5f9053405a | |||
| f1d2afa496 | |||
| 6d5e6cf1cf | |||
| 746792b838 | |||
| da3dc29477 | |||
| caafac2248 | |||
| ff93a6723e | |||
| 46d43267e3 | |||
| 4c5f18dfda | |||
| 5fcf2f9a5f | |||
| 4d1b3177d8 | |||
| f261c1c66b | |||
| 552009ec07 | |||
| 9d396f837c | |||
| daa9b7e608 | |||
| 47fe75e19d | |||
| 44298dfb65 | |||
| 0463578944 | |||
| 584fd4513c | |||
| 5b0147512f | |||
| 7a6d1e2bf2 | |||
| cabe8bec40 | |||
| e11e6cf8c4 | |||
| f44a8e2cf6 |
@@ -20,11 +20,4 @@ build --local_ram_resources=32768
|
||||
# All build executed remotely should be done using our RBE configuration.
|
||||
build:remote --google_default_credentials
|
||||
|
||||
# Upload to GCP's Build Status viewer to allow for us to have better viewing of execution/build
|
||||
# logs. This is only done on CI as the BES (GCP's Build Status viewer) API requires credentials
|
||||
# from service accounts, rather than end user accounts.
|
||||
build:remote --bes_backend=buildeventservice.googleapis.com
|
||||
build:remote --bes_timeout=30s
|
||||
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
||||
|
||||
build --config=remote
|
||||
|
||||
+7
-15
@@ -479,6 +479,7 @@ groups:
|
||||
users:
|
||||
- alxhub
|
||||
- IgorMinar
|
||||
- petebacondarwin
|
||||
|
||||
|
||||
# =========================================================
|
||||
@@ -681,7 +682,7 @@ groups:
|
||||
- *can-be-global-approved
|
||||
- *can-be-global-docs-approved
|
||||
- >
|
||||
contains_any_globs(files.exclude('packages/compiler-cli/**'), [
|
||||
contains_any_globs(files.exclude('packages/compiler-cli/**').exclude('packages/language-service/**'), [
|
||||
'packages/**/testing/**',
|
||||
'aio/content/guide/testing.md',
|
||||
'aio/content/guide/test-debugging.md',
|
||||
@@ -919,7 +920,9 @@ groups:
|
||||
'aio/content/examples/getting-started-v0/**',
|
||||
'aio/content/examples/getting-started/**',
|
||||
'aio/content/start/**',
|
||||
'aio/content/images/guide/start/**'
|
||||
'aio/content/images/guide/start/**',
|
||||
'aio/content/examples/what-is-angular/**',
|
||||
'aio/content/guide/what-is-angular.md'
|
||||
])
|
||||
reviewers:
|
||||
users:
|
||||
@@ -1166,19 +1169,7 @@ groups:
|
||||
'.vscode/**',
|
||||
'.yarn/**',
|
||||
'dev-infra/**',
|
||||
'docs/BAZEL.md',
|
||||
'docs/CARETAKER.md',
|
||||
'docs/CODING_STANDARDS.md',
|
||||
'docs/COMMITTER.md',
|
||||
'docs/DEBUG.md',
|
||||
'docs/DEBUG_COMPONENTS_REPO_IVY.md',
|
||||
'docs/DEVELOPER.md',
|
||||
'docs/FIXUP_COMMITS.md',
|
||||
'docs/GITHUB_PROCESS.md',
|
||||
'docs/PR_REVIEW.md',
|
||||
'docs/SAVED_REPLIES.md',
|
||||
'docs/TOOLS.md',
|
||||
'docs/TRIAGE_AND_LABELS.md',
|
||||
'docs/*.md',
|
||||
'docs/images/**',
|
||||
'goldens/*',
|
||||
'modules/*',
|
||||
@@ -1234,6 +1225,7 @@ groups:
|
||||
'aio/content/errors/*.md',
|
||||
'aio/content/guide/glossary.md',
|
||||
'aio/content/guide/styleguide.md',
|
||||
'aio/content/examples/errors/**',
|
||||
'aio/content/examples/styleguide/**',
|
||||
'aio/content/images/guide/styleguide/*'
|
||||
])
|
||||
|
||||
+97
-2
@@ -1,14 +1,109 @@
|
||||
## 11.2.2 (2021-02-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** error when setting position before starting animation ([#28255](https://github.com/angular/angular/issues/28255)) ([f1c64b3](https://github.com/angular/angular/commit/f1c64b3f38ed38c2d0c7369f6ee5dea5bbc24cf3))
|
||||
* **http:** emit error on XMLHttpRequest abort event ([#40786](https://github.com/angular/angular/issues/40786)) ([4a32579](https://github.com/angular/angular/commit/4a32579a4d660b774c380314033e15787dfe4631)), closes [#22324](https://github.com/angular/angular/issues/22324)
|
||||
* **http:** ignore question mark when params are parsed ([#40610](https://github.com/angular/angular/issues/40610)) ([3b884ed](https://github.com/angular/angular/commit/3b884ed1fc1b1c89c1da9aa6ee0e6b21ff4aa024)), closes [#28722](https://github.com/angular/angular/issues/28722)
|
||||
* **platform-browser:** ensure that Hammer loader is called only once ([#40911](https://github.com/angular/angular/issues/40911)) ([f8055f6](https://github.com/angular/angular/commit/f8055f6fb839e5817a83c484d96afb77bae0aed5)), closes [#25995](https://github.com/angular/angular/issues/25995)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **core:** use `ngDevMode` to tree-shake warning ([#40876](https://github.com/angular/angular/issues/40876)) ([c8a2e3a](https://github.com/angular/angular/commit/c8a2e3ae1c12cadd505cdd5a12e26a7231682415))
|
||||
* **language-service:** short-circuit LS operations when we know there is no Angular information to provide ([#40946](https://github.com/angular/angular/issues/40946)) ([73a3ff1](https://github.com/angular/angular/commit/73a3ff11cbca21ec0a64e064c7968c93c0bd8e0a))
|
||||
|
||||
|
||||
|
||||
## 11.2.1 (2021-02-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** handle `:host-context` and `:host` in the same selector ([#40494](https://github.com/angular/angular/issues/40494)) ([ab8ea87](https://github.com/angular/angular/commit/ab8ea8707842b98b8154f5cbe942b4b24724b6ca)), closes [#14349](https://github.com/angular/angular/issues/14349)
|
||||
* **compiler:** support multiple `:host-context()` selectors ([#40494](https://github.com/angular/angular/issues/40494)) ([c20e304](https://github.com/angular/angular/commit/c20e3047a58ce2404551c0b51d539e77687e7283)), closes [#19199](https://github.com/angular/angular/issues/19199)
|
||||
* **compiler:** support multiple selectors in `:host-context()` ([#40494](https://github.com/angular/angular/issues/40494)) ([fb0ce21](https://github.com/angular/angular/commit/fb0ce2178add49f204486c41ef94936f0f178086))
|
||||
* **compiler-cli:** set TS original node on imported namespace identifiers ([#40711](https://github.com/angular/angular/issues/40711)) ([87ac052](https://github.com/angular/angular/commit/87ac05290ac12ccca10580d10d092d747dae9b40))
|
||||
* **core:** address Trusted Types bug in Chrome 83 ([#40815](https://github.com/angular/angular/issues/40815)) ([b5f2070](https://github.com/angular/angular/commit/b5f20700c2b9c5edebbfeac8c656f290e4d5d7ea))
|
||||
* **http:** complete the request on timeout ([#40771](https://github.com/angular/angular/issues/40771)) ([a2a5b4a](https://github.com/angular/angular/commit/a2a5b4add5e68cebe89a4d2b6eff2d75e4cd23c7))
|
||||
* **router:** fix load interaction of navigation and preload strategies ([#40389](https://github.com/angular/angular/issues/40389)) ([4906bf6](https://github.com/angular/angular/commit/4906bf6a50f307fb69080d276c3b91a60ba7fbae)), closes [#26557](https://github.com/angular/angular/issues/26557) [#22842](https://github.com/angular/angular/issues/22842) [#26557](https://github.com/angular/angular/issues/26557) [#26557](https://github.com/angular/angular/issues/26557) [#22842](https://github.com/angular/angular/issues/22842) [#26557](https://github.com/angular/angular/issues/26557)
|
||||
* **router:** fix load interaction of navigation and preload strategies ([#40389](https://github.com/angular/angular/issues/40389)) ([bba1981](https://github.com/angular/angular/commit/bba1981c6e1963e5cd1f2c704bb656ca8fcec531)), closes [#26557](https://github.com/angular/angular/issues/26557) [#22842](https://github.com/angular/angular/issues/22842) [#26557](https://github.com/angular/angular/issues/26557) [#26557](https://github.com/angular/angular/issues/26557) [#22842](https://github.com/angular/angular/issues/22842) [#26557](https://github.com/angular/angular/issues/26557)
|
||||
|
||||
|
||||
|
||||
<a name="11.2.0"></a>
|
||||
# 11.2.0 (2021-02-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** properly track listeners for a removed element ([#40712](https://github.com/angular/angular/issues/40712)) ([74cb759](https://github.com/angular/angular/commit/74cb759))
|
||||
* **common:** date is not correctly formatted when year is between 0 and 99 ([#40448](https://github.com/angular/angular/issues/40448)) ([bf85f04](https://github.com/angular/angular/commit/bf85f04)), closes [#40377](https://github.com/angular/angular/issues/40377)
|
||||
* **compiler:** include parenthesis in expression source spans ([#40740](https://github.com/angular/angular/issues/40740)) ([e99c4e2](https://github.com/angular/angular/commit/e99c4e2)), closes [#40721](https://github.com/angular/angular/issues/40721)
|
||||
* **compiler:** throw error for duplicate template references ([#40538](https://github.com/angular/angular/issues/40538)) ([7e0ff8c](https://github.com/angular/angular/commit/7e0ff8c)), closes [#40536](https://github.com/angular/angular/issues/40536)
|
||||
* **compiler-cli:** don't crash when we can't resolve a resource ([#40660](https://github.com/angular/angular/issues/40660)) ([95446fb](https://github.com/angular/angular/commit/95446fb)), closes [angular/vscode-ng-language-service#1079](https://github.com/angular/vscode-ng-language-service/issues/1079)
|
||||
* **core:** properly move embedded views of dynamic component's projectable nodes ([#37167](https://github.com/angular/angular/issues/37167)) ([9ae21ee](https://github.com/angular/angular/commit/9ae21ee)), closes [#37120](https://github.com/angular/angular/issues/37120)
|
||||
* **localize:** improve invalid syntax in extraction error message ([#40724](https://github.com/angular/angular/issues/40724)) ([24600ff](https://github.com/angular/angular/commit/24600ff))
|
||||
* **localize:** support downleveled inlined helper localize calls ([#40754](https://github.com/angular/angular/issues/40754)) ([415ad8d](https://github.com/angular/angular/commit/415ad8d)), closes [#40702](https://github.com/angular/angular/issues/40702)
|
||||
* **router:** stop emitting to event observable on destroy ([#40638](https://github.com/angular/angular/issues/40638)) ([737bc7a](https://github.com/angular/angular/commit/737bc7a)), closes [#40502](https://github.com/angular/angular/issues/40502)
|
||||
|
||||
|
||||
|
||||
<a name="11.2.0-rc.0"></a>
|
||||
# 11.2.0-rc.0 (2021-02-03)
|
||||
|
||||
|
||||
|
||||
<a name="11.2.0-next.1"></a>
|
||||
# 11.2.0-next.1 (2021-02-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** parse `YYYY-MM` strings as UTC dates ([#40620](https://github.com/angular/angular/issues/40620)) ([4c5f18d](https://github.com/angular/angular/commit/4c5f18d)), closes [#33944](https://github.com/angular/angular/issues/33944)
|
||||
* **common:** parse `YYYY` strings as UTC dates ([#40629](https://github.com/angular/angular/issues/40629)) ([46d4326](https://github.com/angular/angular/commit/46d4326)), closes [#33944](https://github.com/angular/angular/issues/33944) [#40620](https://github.com/angular/angular/issues/40620)
|
||||
* **compiler:** Don't set expression text to synthetic `$implicit` when empty ([#40583](https://github.com/angular/angular/issues/40583)) ([9d396f8](https://github.com/angular/angular/commit/9d396f8))
|
||||
* **compiler:** exclude trailing whitespace from element source spans ([#40513](https://github.com/angular/angular/issues/40513)) ([7a6d1e2](https://github.com/angular/angular/commit/7a6d1e2)), closes [#39148](https://github.com/angular/angular/issues/39148)
|
||||
* **compiler-cli:** preserve user line endings in diagnostic template parse ([#40597](https://github.com/angular/angular/issues/40597)) ([caafac2](https://github.com/angular/angular/commit/caafac2))
|
||||
* **core:** ensure the type `T` of `EventEmitter<T>` can be inferred ([#40644](https://github.com/angular/angular/issues/40644)) ([5c54767](https://github.com/angular/angular/commit/5c54767)), closes [#40637](https://github.com/angular/angular/issues/40637)
|
||||
* **core:** remove duplicated EMPTY_ARRAY constant ([#40587](https://github.com/angular/angular/issues/40587)) ([cabe8be](https://github.com/angular/angular/commit/cabe8be))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **language-service:** update NgCompiler via resource-only path when able ([#40585](https://github.com/angular/angular/issues/40585)) ([07a3d7a](https://github.com/angular/angular/commit/07a3d7a))
|
||||
|
||||
|
||||
|
||||
<a name="11.2.0-next.0"></a>
|
||||
# 11.2.0-next.0 (2021-01-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **core:** make DefaultIterableDiffer keep the order of duplicates ([#23941](https://github.com/angular/angular/issues/23941)) ([a826926](https://github.com/angular/angular/commit/a826926)), closes [#23815](https://github.com/angular/angular/issues/23815)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** implement `appendAll()` method on `HttpParams` ([#20930](https://github.com/angular/angular/issues/20930)) ([575a2d1](https://github.com/angular/angular/commit/575a2d1)), closes [#20798](https://github.com/angular/angular/issues/20798)
|
||||
* **language-service:** Add diagnostics to suggest turning on strict mode ([#40423](https://github.com/angular/angular/issues/40423)) ([ecae75f](https://github.com/angular/angular/commit/ecae75f))
|
||||
* **language-service:** Implement `getRenameInfo` ([#40439](https://github.com/angular/angular/issues/40439)) ([4e8198d](https://github.com/angular/angular/commit/4e8198d))
|
||||
* **language-service:** initial implementation for `findRenameLocations` ([#40140](https://github.com/angular/angular/issues/40140)) ([9a5ac47](https://github.com/angular/angular/commit/9a5ac47))
|
||||
* **language-service:** view template typecheck block ([#39974](https://github.com/angular/angular/issues/39974)) ([d482f5c](https://github.com/angular/angular/commit/d482f5c))
|
||||
|
||||
|
||||
|
||||
<a name="11.1.1"></a>
|
||||
## 11.1.1 (2021-01-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** support multiple `:host-context()` selectors ([#40494](https://github.com/angular/angular/issues/40494)) ([72c0ba6](https://github.com/angular/angular/commit/72c0ba6)), closes [#19199](https://github.com/angular/angular/issues/19199)
|
||||
* **compiler-cli:** handle pseudo cycles in inline source-maps ([#40435](https://github.com/angular/angular/issues/40435)) ([566206b](https://github.com/angular/angular/commit/566206b)), closes [#40408](https://github.com/angular/angular/issues/40408)
|
||||
* **compiler-cli:** use `Map` rather than `object` for map of partial linkers ([#40563](https://github.com/angular/angular/issues/40563)) ([33e0f2b](https://github.com/angular/angular/commit/33e0f2b))
|
||||
* **core:** fix possible XSS attack in development through SSR ([#40525](https://github.com/angular/angular/issues/40525)) ([97ec6e4](https://github.com/angular/angular/commit/97ec6e4))
|
||||
* **core:** fix possible XSS attack in development through SSR ([#40525](https://github.com/angular/angular/issues/40525)) ([3bf6a99](https://github.com/angular/angular/commit/3bf6a99))
|
||||
* **core:** improve injector debug information in `ngDevMode` ([#40476](https://github.com/angular/angular/issues/40476)) ([4bb38c9](https://github.com/angular/angular/commit/4bb38c9))
|
||||
* **forms:** allow `patchValue()` method of `FormGroup` and `FormArray` classes to skip `null` values ([#40534](https://github.com/angular/angular/issues/40534)) ([2fab148](https://github.com/angular/angular/commit/2fab148)), closes [#36672](https://github.com/angular/angular/issues/36672) [#21021](https://github.com/angular/angular/issues/21021)
|
||||
* **forms:** properly cleanup in cases when FormControlName has no CVA ([#40526](https://github.com/angular/angular/issues/40526)) ([72fc6aa](https://github.com/angular/angular/commit/72fc6aa)), closes [#39235](https://github.com/angular/angular/issues/39235) [#40521](https://github.com/angular/angular/issues/40521)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2010-2020 Google LLC. https://angular.io/license
|
||||
Copyright (c) 2010-2021 Google LLC. https://angular.io/license
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
||||
# Fetch rules_nodejs so we can install our npm dependencies
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
sha256 = "9d93d4e1340c43dbf6b2fd66b683d89630a6310bf8be3bf40ec96685dcacc26c",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/2.3.3/rules_nodejs-2.3.3.tar.gz"],
|
||||
sha256 = "dd4dc46066e2ce034cba0c81aa3e862b27e8e8d95871f567359f7a534cccb666",
|
||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/3.1.0/rules_nodejs-3.1.0.tar.gz"],
|
||||
)
|
||||
|
||||
# Check the rules_nodejs version and download npm dependencies
|
||||
@@ -44,11 +44,6 @@ load("@npm//@bazel/protractor:package.bzl", "npm_bazel_protractor_dependencies")
|
||||
|
||||
npm_bazel_protractor_dependencies()
|
||||
|
||||
# Load karma dependencies
|
||||
load("@npm//@bazel/karma:package.bzl", "npm_bazel_karma_dependencies")
|
||||
|
||||
npm_bazel_karma_dependencies()
|
||||
|
||||
# Setup the rules_webtesting toolchain
|
||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories")
|
||||
|
||||
@@ -59,7 +54,7 @@ load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
|
||||
browser_repositories()
|
||||
|
||||
# Setup the rules_sass toolchain
|
||||
load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories")
|
||||
load("@io_bazel_rules_sass//:defs.bzl", "sass_repositories")
|
||||
|
||||
sass_repositories()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@shortDescription Expression has changed after it was checked
|
||||
|
||||
@description
|
||||
Angular throws an `ExpressionChangedAfterItHasBeenCheckedError` when an expression value has been changed after change detection has completed. Angular only throws this error in development mode.
|
||||
Angular throws an `ExpressionChangedAfterItHasBeenCheckedError` when an expression value has been changed after change detection has completed. Angular only throws this error in development mode.
|
||||
|
||||
In dev mode, Angular performs an additional check after each change detection run, to ensure the bindings haven’t changed. This catches errors where the view is left in an inconsistent state. This can occur, for example, if a method or getter returns a different value each time it is called, or if a child component changes values on its parent. If either of these occur, this is a sign that change detection is not stabilized. Angular throws the error to ensure data is always reflected correctly in the view, which prevents erratic UI behavior or a possible infinite loop.
|
||||
|
||||
@@ -13,8 +13,8 @@ This error commonly occurs when you’ve added template expressions or begun to
|
||||
@debugging
|
||||
The [source maps](https://developer.mozilla.org/en-US/docs/Tools/Debugger/How_to/Use_a_source_map) generated by the CLI are very useful when debugging. Navigate up the call stack until you find a template expression where the value displayed in the error has changed.
|
||||
|
||||
Ensure that there are no changes to the bindings in the template after change detection is run. This often means refactoring to use the correct [component lifecycle hook](https://angular.io/guide/lifecycle-hooks) for your use case. If the issue exists within `ngAfterViewInit`, the recommended solution is to use a constructor or `ngOnInit` to set initial values, or use `ngAfterContentInit` for other value bindings.
|
||||
Ensure that there are no changes to the bindings in the template after change detection is run. This often means refactoring to use the correct [component lifecycle hook](guide/lifecycle-hooks) for your use case. If the issue exists within `ngAfterViewInit`, the recommended solution is to use a constructor or `ngOnInit` to set initial values, or use `ngAfterContentInit` for other value bindings.
|
||||
|
||||
If you are binding to methods in the view, ensure that the invocation does not update any of the other bindings in the template.
|
||||
If you are binding to methods in the view, ensure that the invocation does not update any of the other bindings in the template.
|
||||
|
||||
Read more about which solution is right for you in ['Everything you need to know about the "ExpressionChangedAfterItHasBeenCheckedError" error'](https://indepth.dev/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error/) and why this is useful at ['Angular Debugging "Expression has changed after it was checked": Simple Explanation (and Fix)'](https://blog.angular-university.io/angular-debugging/).
|
||||
Read more about which solution is right for you in ['Everything you need to know about the "ExpressionChangedAfterItHasBeenCheckedError" error'](https://indepth.dev/everything-you-need-to-know-about-the-expressionchangedafterithasbeencheckederror-error/) and why this is useful at ['Angular Debugging "Expression has changed after it was checked": Simple Explanation (and Fix)'](https://blog.angular-university.io/angular-debugging/).
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
@shortDescription Circular dependency in DI detected while instantiating a provider
|
||||
|
||||
@description
|
||||
A cyclic dependency exists when a [dependency of a service](https://angular.io/guide/hierarchical-dependency-injection) directly or indirectly depends on the service itself. For example, if `UserService` depends on `EmployeeService`, which also depends on `UserService`. Angular will have to instantiate `EmployeeService` to create `UserService`, which depends on `UserService`, itself.
|
||||
A cyclic dependency exists when a [dependency of a service](guide/hierarchical-dependency-injection) directly or indirectly depends on the service itself. For example, if `UserService` depends on `EmployeeService`, which also depends on `UserService`. Angular will have to instantiate `EmployeeService` to create `UserService`, which depends on `UserService`, itself.
|
||||
|
||||
@debugging
|
||||
Use the call stack to determine where the cyclical dependency exists. You will be able to see if any child dependencies rely on the original file by [mapping out](https://angular.io/guide/dependency-injection-in-action) the component, module, or service’s dependencies and identify the loop causing the problem.
|
||||
Use the call stack to determine where the cyclical dependency exists. You will be able to see if any child dependencies rely on the original file by [mapping out](guide/dependency-injection-in-action) the component, module, or service’s dependencies and identify the loop causing the problem.
|
||||
|
||||
Break this loop (or circle) of dependency to resolve this error. This most commonly means removing or refactoring the dependencies to not be reliant on one another.
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
@shortDescription No provider for {token} found!
|
||||
|
||||
@description
|
||||
You see this error when you try to inject a service but have not declared a corresponding provider. A provider is a mapping that supplies a value that you can inject into the constructor of a class in your application.
|
||||
You see this error when you try to inject a service but have not declared a corresponding provider. A provider is a mapping that supplies a value that you can inject into the constructor of a class in your application.
|
||||
|
||||
Read more on providers in our [Dependency Injection guide](https://angular.io/guide/dependency-injection).
|
||||
Read more on providers in our [Dependency Injection guide](guide/dependency-injection).
|
||||
|
||||
@debugging
|
||||
Work backwards from the object where the error states that a [provider](https://angular.io/guide/architecture-services) is missing: `No provider for ${this}!`. This is commonly thrown in [services](https://angular.io/tutorial/toh-pt4), which require non-existing providers.
|
||||
Work backwards from the object where the error states that a [provider](guide/architecture-services) is missing: `No provider for ${this}!`. This is commonly thrown in [services](tutorial/toh-pt4), which require non-existing providers.
|
||||
|
||||
To fix the error ensure that your service is registered in the list of providers of an `NgModule` or has the `@Injectable` decorator with a `providedIn` property at top.
|
||||
|
||||
@@ -17,4 +17,4 @@ The most common solution is to add a provider in `@Injectable` using `providedIn
|
||||
|
||||
```typescript
|
||||
@Injectable({ providedIn: 'app' })
|
||||
```
|
||||
```
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
@name Selector Collision
|
||||
@name Selector Collision
|
||||
@category runtime
|
||||
@videoUrl https://www.youtube.com/embed/z_3Z5mOm59I
|
||||
@shortDescription Multiple components match with the same tagname
|
||||
|
||||
@description
|
||||
Two or more components use the same [element selector](https://angular.io/guide/component-overview#specifying-a-components-css-selector). Because there can only be a single component associated with an element, selectors must be unique strings to prevent ambiguity for Angular.
|
||||
Two or more components use the same [element selector](guide/component-overview#specifying-a-components-css-selector). Because there can only be a single component associated with an element, selectors must be unique strings to prevent ambiguity for Angular.
|
||||
|
||||
@debugging
|
||||
Use the element name from the error message to search for places where you’re using the same [selector declaration](https://angular.io/guide/architecture-components) in your codebase:
|
||||
Use the element name from the error message to search for places where you’re using the same [selector declaration](guide/architecture-components) in your codebase:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
@@ -18,4 +18,4 @@ Use the element name from the error message to search for places where you’re
|
||||
|
||||
Ensure that each component has a unique CSS selector. This will guarantee that Angular renders the component you expect.
|
||||
|
||||
If you’re having trouble finding multiple components with this selector tag name, check for components from imported component libraries, such as Angular Material. Make sure you're following the [best practices](https://angular.io/guide/styleguide#component-selectors) for your selectors to prevent collisions.
|
||||
If you’re having trouble finding multiple components with this selector tag name, check for components from imported component libraries, such as Angular Material. Make sure you're following the [best practices](guide/styleguide#component-selectors) for your selectors to prevent collisions.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@shortDescription Export not found!
|
||||
|
||||
@description
|
||||
Angular can’t find a directive with `{{ PLACEHOLDER }}` export name. The export name is specified in the `exportAs` property of the directive decorator. This is common when using FormsModule or Material modules in templates, and you’ve forgotten to [import the corresponding modules](https://angular.io/guide/sharing-ngmodules).
|
||||
Angular can’t find a directive with `{{ PLACEHOLDER }}` export name. The export name is specified in the `exportAs` property of the directive decorator. This is common when using FormsModule or Material modules in templates, and you’ve forgotten to [import the corresponding modules](guide/sharing-ngmodules).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -15,7 +15,7 @@ This is the runtime equivalent of a common compiler error [NG8003: No directive
|
||||
@debugging
|
||||
Use the export name to trace the templates or modules using this export.
|
||||
|
||||
Ensure that all dependencies are [properly imported and declared in your NgModules](https://angular.io/guide/sharing-ngmodules). For example, if the export not found is `ngForm`, we need to import `FormsModule` and declare it in the list of imports in `*.module.ts` to resolve the error.
|
||||
Ensure that all dependencies are [properly imported and declared in your NgModules](guide/sharing-ngmodules). For example, if the export not found is `ngForm`, we need to import `FormsModule` and declare it in the list of imports in `*.module.ts` to resolve the error.
|
||||
|
||||
```typescript
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
@name Pipe Not Found
|
||||
@category runtime
|
||||
@videoUrl https://www.youtube.com/embed/maI2u6Sxk9M
|
||||
@shortDescription Pipe not found!
|
||||
|
||||
@description
|
||||
Angular can't find a pipe with this name.
|
||||
The pipe referenced in the template has not been named or declared properly.
|
||||
A [pipe](guide/pipes) must be either declared or imported in the `NgModule` where it is used, and the name used in a template must match the name defined in the pipe decorator.
|
||||
|
||||
@debugging
|
||||
Use the pipe name to trace the templates or modules where this pipe is declared and used.
|
||||
|
||||
To resolve this error, ensure that:
|
||||
- A local custom pipe is uniquely named in the pipe's decorator, and declared in the `NgModule`, or
|
||||
- A pipe from another `NgModule` is added to the imports of the `NgModule` where it is used.
|
||||
|
||||
If you recently added an import or declaration, you may need to restart your server to see these changes.
|
||||
@@ -3,9 +3,9 @@
|
||||
@shortDescription No suitable injection token for parameter
|
||||
|
||||
@description
|
||||
There is no injection token for a constructor parameter at compile time. [InjectionTokens](https://angular.io/api/core/InjectionToken) are tokens that can be used in a Dependency Injection Provider.
|
||||
There is no injection token for a constructor parameter at compile time. [InjectionTokens](api/core/InjectionToken) are tokens that can be used in a Dependency Injection Provider.
|
||||
|
||||
@debugging
|
||||
Look at the parameter that throws the error and all uses of the class. This error is commonly thrown when a constructor defines parameters with primitive types like `string`, `number`, `boolean`, and `Object`.
|
||||
|
||||
Use the [@Injectable](https://angular.io/api/core/Injectable) method or [@Inject](https://angular.io/api/core/Inject) decorator from `@angular/core` to ensure that the type you are injecting is reified (has a runtime representation). Make sure to add a provider to this decorator so that you do not throw [NG0201: No Provider Found](https://angular.io/errors/NG0201).
|
||||
Use the [@Injectable](api/core/Injectable) method or [@Inject](api/core/Inject) decorator from `@angular/core` to ensure that the type you are injecting is reified (has a runtime representation). Make sure to add a provider to this decorator so that you do not throw [NG0201: No Provider Found](errors/NG0201).
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
@name Import Cycle Detected
|
||||
@category compiler
|
||||
@shortDescription Import cycles would need to be created to compile this component
|
||||
|
||||
@description
|
||||
|
||||
A component, directive or pipe that is referenced by this component would require the compiler
|
||||
to add an import that would lead to a cycle of imports. For example, consider a scenario where
|
||||
a `ParentComponent` references a `ChildComponent` in its template:
|
||||
|
||||
<code-example path="errors/cyclic-imports/parent.component.ts" header="parent.component.ts"></code-example>
|
||||
|
||||
<code-example path="errors/cyclic-imports/child.component.ts" header="child.component.ts"></code-example>
|
||||
|
||||
There is already an import from `child.component.ts` to `parent.component.ts` since the `ChildComponent`
|
||||
references the `ParentComponent` in its constructor.
|
||||
|
||||
But note that the parent component's template contains `<child></child>`. The generated code for this
|
||||
template must therefore contain a reference to the `ChildComponent` class. In order to make this reference
|
||||
the compiler would have to add an import from `parent.component.ts` to `child.component.ts`, which would
|
||||
cause an import cycle:
|
||||
|
||||
```
|
||||
parent.component.ts -> child.component.ts -> parent.component.ts
|
||||
```
|
||||
|
||||
### Remote Scoping
|
||||
|
||||
To avoid adding imports that create cycles, additional code is added to the `NgModule` class where
|
||||
the component is declared that wires up the dependencies. This is known as "remote scoping".
|
||||
|
||||
|
||||
### Libraries
|
||||
|
||||
Unfortunately, "remote scoping" code is side-effectful, which prevents tree shaking, and cannot
|
||||
be used in libraries. So when building libraries using the `"compilationMode": "partial"` setting,
|
||||
any component that would require a cyclic import will cause this `NG3003` compiler error to be raised.
|
||||
|
||||
|
||||
@debugging
|
||||
|
||||
The cycle that would be generated is shown as part of the error message. For example:
|
||||
|
||||
<code-example hideCopy="true">
|
||||
<span class="nocode">The component ChildComponent is used in the template but importing it would create a cycle:
|
||||
/parent.component.ts -> /child.component.ts -> /parent.component.ts</span>
|
||||
</code-example>
|
||||
|
||||
Use this to identify how the referenced component, pipe or directive has a dependency back to the
|
||||
component being compiled. Here are some ideas for fixing the problem:
|
||||
|
||||
* Try to re-arrange your dependencies to avoid the cycle. For example using an intermediate interface
|
||||
that is stored in an independent file that can be imported to both dependent files without
|
||||
causing an import cycle.
|
||||
* Move the classes that reference each other into the same file, to avoid any imports between them.
|
||||
@@ -12,10 +12,10 @@ This is the compiler equivalent of a common runtime error `NG0304: '${tagName}'
|
||||
</div>
|
||||
|
||||
@debugging
|
||||
Use the element name in the error to find the file(s) where the element is being used.
|
||||
Use the element name in the error to find the file(s) where the element is being used.
|
||||
|
||||
Check that the name and selector are correct. If the component is from a different module or import, check that the component is exported from its origin module and imported into the correct `*.modules.ts` file, and declared in the imports list.
|
||||
Check that the name and selector are correct. If the component is from a different module or import, check that the component is exported from its origin module and imported into the correct `*.modules.ts` file, and declared in the imports list.
|
||||
|
||||
When using custom elements or web components, ensure that you add [`CUSTOM_ELEMENTS_SCHEMA`](https://angular.io/api/core/CUSTOM_ELEMENTS_SCHEMA) to the app module.
|
||||
When using custom elements or web components, ensure that you add [`CUSTOM_ELEMENTS_SCHEMA`](api/core/CUSTOM_ELEMENTS_SCHEMA) to the app module.
|
||||
|
||||
If this does not resolve the error, check the imported libraries for any recent changes to the exports and properties you are using, and restart your server.
|
||||
|
||||
@@ -13,4 +13,4 @@ This error arises when attempting to bind to a property that does not exist. Any
|
||||
The runtime error for this is `NG0304: '${tagName}' is not a known element: …’`.
|
||||
|
||||
@debugging
|
||||
Look at documentation for the specific [binding syntax](https://angular.io/guide/binding-syntax) used. This is usually a typo or incorrect import. There may also be a missing direction with property selector ‘name’ or missing input.
|
||||
Look at documentation for the specific [binding syntax](guide/binding-syntax) used. This is usually a typo or incorrect import. There may also be a missing direction with property selector ‘name’ or missing input.
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
@shortDescription No directive found with export
|
||||
|
||||
@description
|
||||
Angular can’t find a directive with `{{ PLACEHOLDER }}` export name. This is common with a missing import or a missing [`exportAs`](https://angular.io/api/core/Directive#exportAs) on a directive.
|
||||
Angular can’t find a directive with `{{ PLACEHOLDER }}` export name. This is common with a missing import or a missing [`exportAs`](api/core/Directive#exportAs) on a directive.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
@@ -16,7 +16,7 @@ This is the compiler equivalent of a common runtime error [NG0301: Export Not Fo
|
||||
@debugging
|
||||
Use the string name of the export not found to trace the templates or modules using this export.
|
||||
|
||||
Ensure that all dependencies are properly imported and declared in our Modules. For example, if the export not found is `ngForm`, we will need to import `FormsModule` and declare it in our list of imports in `*.module.ts` to resolve the missing export error.
|
||||
Ensure that all dependencies are properly imported and declared in our Modules. For example, if the export not found is `ngForm`, we will need to import `FormsModule` and declare it in our list of imports in `*.module.ts` to resolve the missing export error.
|
||||
|
||||
```typescript
|
||||
import { FormsModule } from '@angular/forms';
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
@Component({
|
||||
selector: 'app-nav-bar',
|
||||
template: `
|
||||
<nav [style]='navStyle'>
|
||||
<a [style.text-decoration]="activeLinkStyle">Home Page</a>
|
||||
<a [style.text-decoration]="linkStyle">Login</a>
|
||||
</nav>`
|
||||
})
|
||||
export class NavBarComponent {
|
||||
navStyle = 'font-size: 1.2rem; color: cornflowerblue;';
|
||||
linkStyle = 'underline';
|
||||
activeLinkStyle = 'overline';
|
||||
/* . . . */
|
||||
}
|
||||
@@ -4,11 +4,7 @@
|
||||
<p appHighlight>Highlight me!</p>
|
||||
<!-- #enddocregion applied -->
|
||||
|
||||
<!-- #docregion color-1 -->
|
||||
<p appHighlight highlightColor="yellow">Highlighted in yellow</p>
|
||||
<p appHighlight [highlightColor]="'orange'">Highlighted in orange</p>
|
||||
<!-- #enddocregion color-1 -->
|
||||
|
||||
<!-- #docregion color-2 -->
|
||||
<p appHighlight [highlightColor]="color">Highlighted with parent component's color</p>
|
||||
<!-- #enddocregion color-2 -->
|
||||
@@ -1,8 +1,8 @@
|
||||
/* tslint:disable:no-unused-variable member-ordering */
|
||||
// #docplaster
|
||||
// #docregion imports,
|
||||
// #docregion imports
|
||||
import { Directive, ElementRef, HostListener } from '@angular/core';
|
||||
// #enddocregion imports,
|
||||
// #enddocregion imports
|
||||
import { Input } from '@angular/core';
|
||||
// #docregion
|
||||
|
||||
@@ -10,9 +10,8 @@ import { Input } from '@angular/core';
|
||||
selector: '[appHighlight]'
|
||||
})
|
||||
export class HighlightDirective {
|
||||
// #docregion ctor
|
||||
|
||||
constructor(private el: ElementRef) { }
|
||||
// #enddocregion ctor
|
||||
|
||||
// #docregion mouse-methods
|
||||
@HostListener('mouseenter') onMouseEnter() {
|
||||
@@ -26,16 +25,10 @@ export class HighlightDirective {
|
||||
private highlight(color: string) {
|
||||
this.el.nativeElement.style.backgroundColor = color;
|
||||
}
|
||||
// #enddocregion mouse-methods,
|
||||
|
||||
// #docregion color
|
||||
@Input() highlightColor: string;
|
||||
// #enddocregion color
|
||||
|
||||
// #enddocregion mouse-methods
|
||||
// #docregion color-2
|
||||
@Input() appHighlight: string;
|
||||
// #enddocregion color-2
|
||||
|
||||
// #docregion
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
@@ -12,11 +12,9 @@ export class HighlightDirective {
|
||||
|
||||
@Input('appHighlight') highlightColor: string;
|
||||
|
||||
// #docregion mouse-enter
|
||||
@HostListener('mouseenter') onMouseEnter() {
|
||||
this.highlight(this.highlightColor || 'red');
|
||||
}
|
||||
// #enddocregion mouse-enter
|
||||
|
||||
@HostListener('mouseleave') onMouseLeave() {
|
||||
this.highlight(null);
|
||||
|
||||
@@ -12,9 +12,7 @@ export class HighlightDirective {
|
||||
@Input() defaultColor: string;
|
||||
// #enddocregion defaultColor
|
||||
|
||||
// #docregion color
|
||||
@Input('appHighlight') highlightColor: string;
|
||||
// #enddocregion color
|
||||
|
||||
// #docregion mouse-enter
|
||||
@HostListener('mouseenter') onMouseEnter() {
|
||||
|
||||
@@ -7,11 +7,8 @@
|
||||
<fieldset><h4>NgModel examples</h4>
|
||||
<p>Current item name: {{currentItem.name}}</p>
|
||||
<p>
|
||||
<!-- #docregion without-NgModel -->
|
||||
<label for="without">without NgModel:</label>
|
||||
<input [value]="currentItem.name" (input)="currentItem.name=$event.target.value" id="without">
|
||||
<!-- #enddocregion without-NgModel -->
|
||||
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -27,10 +24,8 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<!-- #docregion NgModelChange -->
|
||||
<label for="example-change">(ngModelChange)="...name=$event":</label>
|
||||
<input [ngModel]="currentItem.name" (ngModelChange)="currentItem.name=$event" id="example-change">
|
||||
<!-- #enddocregion NgModelChange -->
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -77,12 +72,9 @@
|
||||
|
||||
<!-- NgStyle binding -->
|
||||
<hr><h3>NgStyle Binding</h3>
|
||||
<!-- #docregion without-ng-style -->
|
||||
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'">
|
||||
This div is x-large or smaller.
|
||||
</div>
|
||||
<!-- #enddocregion without-ng-style -->
|
||||
|
||||
|
||||
<h4>[ngStyle] binding to currentStyles - CSS property names</h4>
|
||||
<p>currentStyles is {{currentStyles | json}}</p>
|
||||
@@ -134,7 +126,6 @@
|
||||
<hr>
|
||||
|
||||
<h4>Show/hide vs. NgIf</h4>
|
||||
<!-- #docregion NgIf-3 -->
|
||||
<!-- isSpecial is true -->
|
||||
<div [class.hidden]="!isSpecial">Show with class</div>
|
||||
<div [class.hidden]="isSpecial">Hide with class</div>
|
||||
@@ -144,7 +135,6 @@
|
||||
|
||||
<div [style.display]="isSpecial ? 'block' : 'none'">Show with style</div>
|
||||
<div [style.display]="isSpecial ? 'none' : 'block'">Hide with style</div>
|
||||
<!-- #enddocregion NgIf-3 -->
|
||||
|
||||
|
||||
<hr>
|
||||
|
||||
@@ -21,7 +21,10 @@ export class AppComponent implements OnInit {
|
||||
item: Item; // defined to demonstrate template context precedence
|
||||
items: Item[];
|
||||
|
||||
// #docregion item
|
||||
currentItem: Item;
|
||||
// #enddocregion item
|
||||
|
||||
|
||||
|
||||
// trackBy change counting
|
||||
|
||||
@@ -5,9 +5,13 @@ import { Item } from './item';
|
||||
selector: 'app-stout-item',
|
||||
template: `I'm a little {{item.name}}, short and stout!`
|
||||
})
|
||||
|
||||
// #docregion input
|
||||
export class StoutItemComponent {
|
||||
@Input() item: Item;
|
||||
}
|
||||
// #enddocregion input
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-best-item',
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-not-great',
|
||||
})
|
||||
// #docregion not-great
|
||||
export class NotGreatComponent {
|
||||
|
||||
buggyFunction() {
|
||||
// things could be better here...
|
||||
}
|
||||
}
|
||||
// #enddocregion not-great
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ParentComponent } from './parent.component';
|
||||
|
||||
@Component({selector: 'app-child', template: 'The child!'})
|
||||
export class ChildComponent {
|
||||
constructor(private parent: ParentComponent) {}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ChildComponent } from './child.component';
|
||||
import { ParentComponent } from './parent.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [ParentComponent, ChildComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -0,0 +1,4 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({selector: 'app-parent', template: '<app-child></app-child>'})
|
||||
export class ParentComponent {}
|
||||
@@ -66,6 +66,7 @@ describe('Http Tests', () => {
|
||||
await checkLogForMessage('GET "assets/config.json"');
|
||||
expect(await page.configSpan.getText()).toContain('Heroes API URL is "api/heroes"');
|
||||
expect(await page.configSpan.getText()).toContain('Textfile URL is "assets/textfile.txt"');
|
||||
expect(await page.configSpan.getText()).toContain('Date is "Wed Jan 29 2020" (date)');
|
||||
});
|
||||
|
||||
it('can fetch the configuration JSON file with headers', async () => {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<span *ngIf="config">
|
||||
<p>Heroes API URL is "{{config.heroesUrl}}"</p>
|
||||
<p>Textfile URL is "{{config.textfile}}"</p>
|
||||
<p>Date is "{{config.date.toDateString()}}" ({{getType(config.date)}})</p>
|
||||
<div *ngIf="headers">
|
||||
Response headers:
|
||||
<ul>
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { Config, ConfigService } from './config.service';
|
||||
import { MessageService } from '../message.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-config',
|
||||
@@ -40,7 +39,8 @@ export class ConfigComponent {
|
||||
// #docregion v1
|
||||
.subscribe((data: Config) => this.config = {
|
||||
heroesUrl: data.heroesUrl,
|
||||
textfile: data.textfile
|
||||
textfile: data.textfile,
|
||||
date: data.date,
|
||||
});
|
||||
}
|
||||
// #enddocregion v1
|
||||
@@ -71,5 +71,9 @@ export class ConfigComponent {
|
||||
makeError() {
|
||||
this.configService.makeIntentionalError().subscribe(null, error => this.error = error );
|
||||
}
|
||||
|
||||
getType(val: any): string {
|
||||
return val instanceof Date ? 'date' : Array.isArray(val) ? 'array' : typeof val;
|
||||
}
|
||||
}
|
||||
// #enddocregion
|
||||
|
||||
@@ -14,6 +14,7 @@ import { catchError, retry } from 'rxjs/operators';
|
||||
export interface Config {
|
||||
heroesUrl: string;
|
||||
textfile: string;
|
||||
date: any;
|
||||
}
|
||||
// #enddocregion config-interface
|
||||
// #docregion proto
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest, HttpResponse } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
// #docregion custom-json-interceptor
|
||||
@Injectable()
|
||||
export class CustomJsonInterceptor implements HttpInterceptor {
|
||||
constructor(private jsonParser: JsonParser) {}
|
||||
|
||||
intercept(httpRequest: HttpRequest<any>, next: HttpHandler) {
|
||||
if (httpRequest.responseType === 'json') {
|
||||
// If the expected response type is JSON then handle it here.
|
||||
return this.handleJsonResponse(httpRequest, next);
|
||||
} else {
|
||||
return next.handle(httpRequest);
|
||||
}
|
||||
}
|
||||
|
||||
private handleJsonResponse(httpRequest: HttpRequest<any>, next: HttpHandler) {
|
||||
// Override the responseType to disable the default JSON parsing.
|
||||
httpRequest = httpRequest.clone({responseType: 'text'});
|
||||
// Handle the response using the custom parser.
|
||||
return next.handle(httpRequest).pipe(map(event => this.parseJsonResponse(event)));
|
||||
}
|
||||
|
||||
private parseJsonResponse(event: HttpEvent<any>) {
|
||||
if (event instanceof HttpResponse && typeof event.body === 'string') {
|
||||
return event.clone({body: this.jsonParser.parse(event.body)});
|
||||
} else {
|
||||
return event;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The JsonParser class acts as a base class for custom parsers and as the DI token.
|
||||
@Injectable()
|
||||
export abstract class JsonParser {
|
||||
abstract parse(text: string): any;
|
||||
}
|
||||
// #enddocregion custom-json-interceptor
|
||||
|
||||
// #docregion custom-json-parser
|
||||
@Injectable()
|
||||
export class CustomJsonParser implements JsonParser {
|
||||
parse(text: string): any {
|
||||
return JSON.parse(text, dateReviver);
|
||||
}
|
||||
}
|
||||
|
||||
function dateReviver(key: string, value: any) {
|
||||
// #enddocregion custom-json-parser
|
||||
if (typeof value !== 'string') {
|
||||
return value;
|
||||
}
|
||||
const match = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(value);
|
||||
if (!match) {
|
||||
return value;
|
||||
}
|
||||
return new Date(+match[1], +match[2] - 1, +match[3]);
|
||||
// #docregion custom-json-parser
|
||||
}
|
||||
// #enddocregion custom-json-parser
|
||||
@@ -6,6 +6,7 @@ import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
// #enddocregion interceptor-providers
|
||||
import { AuthInterceptor } from './auth-interceptor';
|
||||
import { CachingInterceptor } from './caching-interceptor';
|
||||
import { CustomJsonInterceptor , CustomJsonParser, JsonParser} from './custom-json-interceptor';
|
||||
import { EnsureHttpsInterceptor } from './ensure-https-interceptor';
|
||||
import { LoggingInterceptor } from './logging-interceptor';
|
||||
// #docregion interceptor-providers
|
||||
@@ -21,6 +22,10 @@ export const httpInterceptorProviders = [
|
||||
// #docregion noop-provider
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: NoopInterceptor, multi: true },
|
||||
// #enddocregion noop-provider, interceptor-providers
|
||||
// #docregion custom-json-interceptor
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: CustomJsonInterceptor, multi: true },
|
||||
{ provide: JsonParser, useClass: CustomJsonParser },
|
||||
// #enddocregion custom-json-interceptor
|
||||
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: EnsureHttpsInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: TrimNameInterceptor, multi: true },
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"heroesUrl": "api/heroes",
|
||||
"textfile": "assets/textfile.txt"
|
||||
"textfile": "assets/textfile.txt",
|
||||
"date": "2020-01-29"
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
.button {
|
||||
box-shadow: inset 0px 1px 0px 0px #ffffff;
|
||||
background: linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%);
|
||||
background-color: #ffffff;
|
||||
box-shadow: inset 0 1px 0 0 #ffffff;
|
||||
background: #ffffff linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%);
|
||||
border-radius: 6px;
|
||||
border: 1px solid #dcdcdc;
|
||||
display: inline-block;
|
||||
@@ -12,13 +11,12 @@
|
||||
font-weight: bold;
|
||||
padding: 6px 24px;
|
||||
text-decoration: none;
|
||||
text-shadow: 0px 1px 0px #ffffff;
|
||||
text-shadow: 0 1px 0 #ffffff;
|
||||
outline: 0;
|
||||
}
|
||||
.activebutton {
|
||||
box-shadow: inset 0px 1px 0px 0px #dcecfb;
|
||||
background: linear-gradient(to bottom, #bddbfa 5%, #80b5ea 100%);
|
||||
background-color: #bddbfa;
|
||||
box-shadow: inset 0 1px 0 0 #dcecfb;
|
||||
background: #bddbfa linear-gradient(to bottom, #bddbfa 5%, #80b5ea 100%);
|
||||
border-radius: 6px;
|
||||
border: 1px solid #84bbf3;
|
||||
display: inline-block;
|
||||
@@ -29,6 +27,6 @@
|
||||
font-weight: bold;
|
||||
padding: 6px 24px;
|
||||
text-decoration: none;
|
||||
text-shadow: 0px 1px 0px #528ecc;
|
||||
text-shadow: 0 1px 0 #528ecc;
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'sg-app',
|
||||
template: '<toh-hero-list></toh-hero-list>'
|
||||
})
|
||||
export class AppComponent { }
|
||||
@@ -0,0 +1,20 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeroComponent, HeroListComponent } from './heroes';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule,
|
||||
RouterModule.forChild([{ path: '05-18', component: AppComponent }])
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HeroComponent,
|
||||
HeroListComponent
|
||||
],
|
||||
exports: [AppComponent]
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -0,0 +1,24 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
import { Hero } from '../shared/hero.model';
|
||||
|
||||
@Component({
|
||||
selector: 'toh-hero-list',
|
||||
template: `
|
||||
<section>
|
||||
Our list of heroes:
|
||||
<toh-hero *ngFor="let hero of heroes">
|
||||
</toh-hero>
|
||||
Total powers: {{totalPowers}}<br>
|
||||
Average power: {{avgPower}}
|
||||
</section>
|
||||
`
|
||||
})
|
||||
export class HeroListComponent {
|
||||
heroes: Hero[] = [];
|
||||
totalPowers = 1;
|
||||
|
||||
get avgPower() {
|
||||
return this.totalPowers / this.heroes.length;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './hero-list.component';
|
||||
@@ -0,0 +1,15 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
// #docregion example
|
||||
@Component({
|
||||
selector: 'toh-hero',
|
||||
template: `...`
|
||||
})
|
||||
export class HeroComponent {
|
||||
// The exclamation mark suppresses errors that a property is
|
||||
// not initialized.
|
||||
// Ignoring this enforcement can prevent the type checker
|
||||
// from finding potential issues.
|
||||
@Input() id!: string;
|
||||
}
|
||||
// #enddocregion example
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
// #docregion example
|
||||
@Component({
|
||||
selector: 'toh-hero',
|
||||
template: `...`
|
||||
})
|
||||
export class HeroComponent {
|
||||
@Input() id?: string;
|
||||
|
||||
process() {
|
||||
if (this.id) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
// #enddocregion example
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
// #docregion example
|
||||
@Component({
|
||||
selector: 'toh-hero',
|
||||
template: `...`
|
||||
})
|
||||
export class HeroComponent {
|
||||
@Input() id = 'default_id';
|
||||
}
|
||||
// #enddocregion example
|
||||
@@ -0,0 +1 @@
|
||||
export * from './hero.component';
|
||||
@@ -0,0 +1,3 @@
|
||||
export * from './hero';
|
||||
export * from './hero-list';
|
||||
export * from './shared';
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface Hero {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './hero.model';
|
||||
@@ -0,0 +1,2 @@
|
||||
export * from './heroes';
|
||||
export * from './app.component';
|
||||
@@ -19,7 +19,7 @@ export class HeroesComponent implements OnInit {
|
||||
heroes = HEROES;
|
||||
// #enddocregion component
|
||||
// #docregion on-select
|
||||
selectedHero: Hero;
|
||||
selectedHero?: Hero;
|
||||
// #enddocregion on-select
|
||||
|
||||
constructor() { }
|
||||
@@ -35,4 +35,3 @@ export class HeroesComponent implements OnInit {
|
||||
// #docregion component
|
||||
}
|
||||
// #enddocregion component
|
||||
|
||||
|
||||
@@ -25,11 +25,11 @@ export class HeroService {
|
||||
|
||||
// #docregion getHeroes, getHeroes-1
|
||||
getHeroes(): Observable<Hero[]> {
|
||||
const heroes = of(HEROES);
|
||||
// #enddocregion getHeroes-1
|
||||
// TODO: send the message _after_ fetching the heroes
|
||||
this.messageService.add('HeroService: fetched heroes');
|
||||
// #docregion getHeroes-1
|
||||
return of(HEROES);
|
||||
return heroes;
|
||||
}
|
||||
// #enddocregion getHeroes, getHeroes-1
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { browser, element, by, logging } from 'protractor';
|
||||
|
||||
|
||||
describe('What is Angular', () => {
|
||||
|
||||
const paragraphs = element.all(by.css('p'));
|
||||
const buttons = element.all(by.css('button'));
|
||||
const templateButton = buttons.get(1);
|
||||
const templateText = paragraphs.get(3);
|
||||
const messageButton = buttons.get(0);
|
||||
const messageText = paragraphs.get(2);
|
||||
const ngIfButton = buttons.get(2);
|
||||
const ngIfText = paragraphs.get(4);
|
||||
const diButton = buttons.get(3);
|
||||
|
||||
beforeEach(() => browser.get(''));
|
||||
|
||||
// helper function to test what's logged to the console
|
||||
async function logChecker(contents) {
|
||||
const logs = await browser.manage().logs().get(logging.Type.BROWSER);
|
||||
const messages = logs.filter(({ message }) => message.indexOf(contents) !== -1 ? true : false);
|
||||
expect(messages.length).toBeGreaterThan(0);
|
||||
}
|
||||
|
||||
it('should display Hello World', async () => {
|
||||
expect(await element(by.css('hello-world h2')).getText()).toEqual('Hello World');
|
||||
});
|
||||
|
||||
// Test for alert
|
||||
it('should display js alert after button click', async () => {
|
||||
await messageButton.click();
|
||||
const alert = browser.switchTo().alert();
|
||||
expect(await alert.getText()).toEqual('Hello, World');
|
||||
await alert.accept();
|
||||
});
|
||||
|
||||
it('should display blue a sentence', async () => {
|
||||
expect(await messageText.getCssValue('color')).toEqual('rgba(0, 0, 255, 1)');
|
||||
});
|
||||
|
||||
// Hello World Template section
|
||||
it('should add 123 to editable p tag', async () => {
|
||||
await templateButton.click();
|
||||
await templateText.click();
|
||||
await templateText.sendKeys('123');
|
||||
expect(await templateText.getText()).toEqual('You can edit me!123');
|
||||
});
|
||||
|
||||
// Test for ngIf section
|
||||
it('should display edit instructions after button click', async () => {
|
||||
await ngIfButton.click();
|
||||
expect(await ngIfText.getText()).toEqual('You can edit the following paragraph.');
|
||||
});
|
||||
|
||||
// Test for DI section
|
||||
it('should log the count', async () => {
|
||||
await diButton.click();
|
||||
await logChecker(0);
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
<h1>Understanding Angular</h1>
|
||||
<!-- #docregion hello-world-selector -->
|
||||
<hello-world></hello-world>
|
||||
<!-- #enddocregion hello-world-selector -->
|
||||
<hello-world-interpolation></hello-world-interpolation>
|
||||
<hello-world-bindings></hello-world-bindings>
|
||||
<hello-world-template></hello-world-template>
|
||||
<hello-world-ngif></hello-world-ngif>
|
||||
<hello-world-di></hello-world-di>
|
||||
@@ -0,0 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html'
|
||||
})
|
||||
export class AppComponent { }
|
||||
@@ -0,0 +1,30 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { HelloWorldComponent } from './hello-world/hello-world.component';
|
||||
import { HelloWorldTemplateComponent } from './hello-world-template.component';
|
||||
import { HelloWorldNgIfComponent } from './hello-world-ngif/hello-world-ngif.component';
|
||||
import { HelloWorldDependencyInjectionComponent } from './hello-world-di/hello-world-di.component';
|
||||
import { HelloWorldInterpolationComponent } from './hello-world-interpolation/hello-world-interpolation.component';
|
||||
import { HelloWorldBindingsComponent } from './hello-world-bindings/hello-world-bindings.component';
|
||||
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
HelloWorldComponent,
|
||||
HelloWorldTemplateComponent,
|
||||
HelloWorldNgIfComponent,
|
||||
HelloWorldDependencyInjectionComponent,
|
||||
HelloWorldInterpolationComponent,
|
||||
HelloWorldBindingsComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
export class AppModule { }
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<!-- #docregion event-binding -->
|
||||
<button (click)="sayMessage()" [disabled]="canClick">Trigger alert message</button>
|
||||
<!-- #enddocregion -->
|
||||
<!-- #docregion bindings -->
|
||||
<p [id]="sayHelloId" [style.color]="fontColor">You can set my color in the component!</p>
|
||||
<!-- #enddocregion -->
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component ({
|
||||
selector: 'hello-world-bindings',
|
||||
templateUrl: './hello-world-bindings.component.html'
|
||||
})
|
||||
export class HelloWorldBindingsComponent {
|
||||
fontColor = 'blue';
|
||||
sayHelloId = 1;
|
||||
canClick = false;
|
||||
message = 'Hello, World';
|
||||
// #docregion method
|
||||
sayMessage() {
|
||||
alert(this.message);
|
||||
}
|
||||
// #enddocregion
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<h2>Hello World: Dependency Injection</h2>
|
||||
<button (click)="onLogMe()">Log me!</button>
|
||||
<p>Be sure to open the console to view the output!</p>
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { Logger } from '../logger.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-di',
|
||||
templateUrl: './hello-world-di.component.html'
|
||||
})
|
||||
export class HelloWorldDependencyInjectionComponent {
|
||||
count = 0;
|
||||
|
||||
constructor(private logger: Logger) {
|
||||
}
|
||||
|
||||
onLogMe(){
|
||||
this.logger.writeCount(this.count);
|
||||
this.count++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
<hello-world>
|
||||
<h2>Hello World</h2>
|
||||
<p>This is my first component!</p>
|
||||
</hello-world>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<!-- #docregion say-hello -->
|
||||
<p>{{ message }}</p>
|
||||
<!-- #enddocregion say-hello -->
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component ({
|
||||
selector: 'hello-world-interpolation',
|
||||
templateUrl: './hello-world-interpolation.component.html'
|
||||
})
|
||||
export class HelloWorldInterpolationComponent {
|
||||
message = 'Hello, World!';
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
<h2>Hello World: ngIf!</h2>
|
||||
<button (click)="onEditClick()">Make text editable!</button>
|
||||
<div *ngIf="canEdit; else noEdit">
|
||||
<p>You can edit the following paragraph.</p>
|
||||
</div>
|
||||
<ng-template #noEdit>
|
||||
<p>The following paragraph is read only. Try clicking the button!</p>
|
||||
</ng-template>
|
||||
<p [contentEditable]="canEdit">{{ message }}</p>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
// #docplaster
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-ngif',
|
||||
templateUrl: './hello-world-ngif.component.html'
|
||||
})
|
||||
export class HelloWorldNgIfComponent {
|
||||
message = 'I\'m read only!';
|
||||
canEdit = false;
|
||||
|
||||
onEditClick(){
|
||||
this.canEdit = !this.canEdit;
|
||||
if (this.canEdit) {
|
||||
this.message = 'You can edit me!';
|
||||
} else {
|
||||
this.message = 'I\'m read only!';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// #docplaster
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world-template',
|
||||
template: `
|
||||
<h2>Hello World Template</h2>
|
||||
<button (click)="onEditClick()">Make text editable!</button>
|
||||
<p [contentEditable]="canEdit">{{ message }}</p>
|
||||
`
|
||||
})
|
||||
export class HelloWorldTemplateComponent {
|
||||
message = 'I am read only!';
|
||||
canEdit = false;
|
||||
onEditClick(){
|
||||
this.canEdit = !this.canEdit;
|
||||
if (this.canEdit) {
|
||||
this.message = 'You can edit me!';
|
||||
} else {
|
||||
this.message = 'I am read only!';
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// #docplaster
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'hello-world',
|
||||
template: `
|
||||
<h2>Hello World</h2>
|
||||
<p>This is my first component!</p>
|
||||
`,
|
||||
})
|
||||
export class HelloWorldComponent {
|
||||
// The code in this class drives the component's behavior.
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable({providedIn: 'root'})
|
||||
export class Logger {
|
||||
writeCount(count: number) {
|
||||
console.warn(count);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Helloworld</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||
.catch(err => console.error(err));
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"description": "What is Angular?",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
],
|
||||
"tags":["overview", "angular"]
|
||||
}
|
||||
@@ -140,8 +140,7 @@ The following table lists some of the key AngularJS template features with their
|
||||
|
||||
Angular has true template input variables that are explicitly defined using the `let` keyword.
|
||||
|
||||
For more information, see the [ngFor micro-syntax](guide/built-in-directives#microsyntax)
|
||||
section of the [Built-in Directives](guide/built-in-directives) page.
|
||||
For more information, see the [Template input variables](guide/structural-directives#template-input-variable) section of [Structural Directives](guide/structural-directives).
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@@ -641,7 +640,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
For more information on style binding, see the [Style binding](guide/attribute-binding#style-binding) section of the
|
||||
[Attribute binding](guide/attribute-binding) page.
|
||||
|
||||
For more information on the `ngStyle` directive, see the [NgStyle](guide/built-in-directives#ngStyle)
|
||||
For more information on the `ngStyle` directive, see the [NgStyle](guide/built-in-directives#ngstyle)
|
||||
section of the [Built-in directives](guide/built-in-directives) page.
|
||||
</td>
|
||||
|
||||
|
||||
@@ -123,7 +123,7 @@ to the generated flat module index instead of the library index file.
|
||||
|
||||
Produces only one `.metadata.json` file, which contains all the metadata necessary
|
||||
for symbols exported from the library index. In the generated `.ngfactory.js` files, the flat
|
||||
module index is used to import symbols that includes both the public API from the library index
|
||||
module index is used to import symbols that include both the public API from the library index
|
||||
as well as shrowded internal symbols.
|
||||
|
||||
By default the `.ts` file supplied in the `files` field is assumed to be the library index.
|
||||
|
||||
@@ -15,8 +15,6 @@ The following are metadata errors you may encounter, with explanations and sugge
|
||||
[Tagged template expressions are not supported](#tagged-template-expressions-not-supported)<br>
|
||||
[Symbol reference expected](#symbol-reference-expected)<br>
|
||||
|
||||
<hr>
|
||||
|
||||
{@a expression-form-not-supported}
|
||||
## Expression form not supported
|
||||
|
||||
@@ -47,8 +45,6 @@ Avoid this error by sticking to the compiler's [restricted expression syntax](gu
|
||||
when writing Angular metadata
|
||||
and be wary of new or unusual TypeScript features.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a reference-to-a-local-symbol}
|
||||
## Reference to a local (non-exported) symbol
|
||||
|
||||
@@ -128,8 +124,6 @@ The compiler needs the value of the `template` property _right now_ to generate
|
||||
The variable reference alone is insufficient.
|
||||
Prefixing the declaration with `export` merely produces a new error, "[`Only initialized variables and constants can be referenced`](#only-initialized-variables)".
|
||||
|
||||
<hr>
|
||||
|
||||
{@a only-initialized-variables}
|
||||
## Only initialized variables and constants
|
||||
|
||||
@@ -187,8 +181,6 @@ export let someTemplate = '<h1>Greetings from Angular</h1>';
|
||||
export class MyComponent {}
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
{@a reference-to-a-non-exported-class}
|
||||
## Reference to a non-exported class
|
||||
|
||||
@@ -228,7 +220,6 @@ export abstract class MyStrategy { }
|
||||
]
|
||||
...
|
||||
```
|
||||
<hr>
|
||||
|
||||
{@a reference-to-a-non-exported-function}
|
||||
## Reference to a non-exported function
|
||||
@@ -266,7 +257,6 @@ export function myStrategy() { ... }
|
||||
]
|
||||
...
|
||||
```
|
||||
<hr>
|
||||
|
||||
{@a function-calls-not-supported}
|
||||
## Function calls are not supported
|
||||
@@ -322,8 +312,6 @@ export function someValueFactory() {
|
||||
...
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
{@a destructured-variable-not-supported}
|
||||
## Destructured variable or constant not supported
|
||||
|
||||
@@ -364,8 +352,6 @@ import { configuration } from './configuration';
|
||||
...
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
{@a could-not-resolve-type}
|
||||
## Could not resolve type
|
||||
|
||||
@@ -437,7 +423,6 @@ export class MyComponent {
|
||||
constructor (@Inject(DOCUMENT) private doc: Document) { ... }
|
||||
}
|
||||
```
|
||||
<hr>
|
||||
|
||||
{@a name-expected}
|
||||
## Name expected
|
||||
@@ -462,8 +447,6 @@ Change the name of the property to something non-numeric.
|
||||
provider: [{ provide: Foo, useValue: { '0': 'test' } }]
|
||||
```
|
||||
|
||||
<hr>
|
||||
|
||||
{@a unsupported-enum-member-name}
|
||||
## Unsupported enum member name
|
||||
|
||||
@@ -494,8 +477,6 @@ enum Colors {
|
||||
|
||||
Avoid referring to enums with complicated initializers or computed properties.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a tagged-template-expressions-not-supported}
|
||||
## Tagged template expressions are not supported
|
||||
|
||||
@@ -520,8 +501,6 @@ is a _tag function_ native to JavaScript ES2015.
|
||||
|
||||
The AOT compiler does not support tagged template expressions; avoid them in metadata expressions.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a symbol-reference-expected}
|
||||
## Symbol reference expected
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ The `ngModel` directive, which implements two-way data binding, is an example of
|
||||
Angular has more pre-defined directives that either alter the layout structure
|
||||
(for example, [ngSwitch](guide/built-in-directives#ngSwitch))
|
||||
or modify aspects of DOM elements and components
|
||||
(for example, [ngStyle](guide/built-in-directives#ngStyle) and [ngClass](guide/built-in-directives#ngClass)).
|
||||
(for example, [ngStyle](guide/built-in-directives#ngstyle) and [ngClass](guide/built-in-directives#ngClass)).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
|
||||
@@ -114,13 +114,12 @@ The router interprets a link URL according to your app's view navigation rules a
|
||||
|
||||
To define navigation rules, you associate *navigation paths* with your components. A path uses a URL-like syntax that integrates your program data, in much the same way that template syntax integrates your views with your program data. You can then apply program logic to choose which views to show or to hide, in response to user input and your own access rules.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For a more detailed discussion, see [Routing and navigation](guide/router).
|
||||
For a more detailed discussion, see [Routing and navigation](guide/router).
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
## What's next
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ Then, you set the attribute value with an expression that resolves to a string.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When the expression resolves to `null`, Angular removes the attribute altogether.
|
||||
When the expression resolves to `null` or `undefined`, Angular removes the attribute altogether.
|
||||
|
||||
</div>
|
||||
|
||||
@@ -74,10 +74,8 @@ For more information on how to bind to the `colSpan` property, see the [`colspan
|
||||
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a class-binding}
|
||||
|
||||
## Binding to the `class` attribute
|
||||
|
||||
You can use class binding to add and remove CSS class names from an element's `class` attribute.
|
||||
@@ -159,10 +157,8 @@ The following table summarizes class binding syntax.
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a style-binding}
|
||||
|
||||
## Binding to the style attribute
|
||||
|
||||
You can use style binding to set styles dynamically.
|
||||
@@ -178,6 +174,12 @@ Optionally, you can add a unit extension like `em` or `%`, which requires a numb
|
||||
You can write a style property name in either [dash-case](guide/glossary#dash-case), or
|
||||
[camelCase](guide/glossary#camelcase).
|
||||
|
||||
<code-example language="html">
|
||||
<nav [style.background-color]="expression"></nav>
|
||||
|
||||
<nav [style.backgroundColor]="expression"></nav>
|
||||
</code-example>
|
||||
|
||||
</div>
|
||||
|
||||
### Binding to multiple styles
|
||||
@@ -185,8 +187,8 @@ You can write a style property name in either [dash-case](guide/glossary#dash-ca
|
||||
To toggle multiple styles, bind to the `[style]` attribute—for example, `[style]="styleExpression"`.
|
||||
The `styleExpression` can be one of:
|
||||
|
||||
* A string list of styles such as `"width: 100px; height: 100px;"`.
|
||||
* An object with style names as the keys and style values as the values, such as `{width: '100px', height: '100px'}`.
|
||||
* A string list of styles such as `"width: 100px; height: 100px; background-color: cornflowerblue;"`.
|
||||
* An object with style names as the keys and style values as the values, such as `{width: '100px', height: '100px', backgroundColor: 'cornflowerblue'}`.
|
||||
|
||||
Note that binding an array to `[style]` is not supported.
|
||||
|
||||
@@ -197,6 +199,11 @@ Updating the property without changing object identity has no effect.
|
||||
|
||||
</div>
|
||||
|
||||
#### Single and multiple-style binding example
|
||||
|
||||
<code-example path="attribute-binding/src/app/single-and-multiple-style-binding.component.ts" header="nav-bar.component.ts">
|
||||
</code-example>
|
||||
|
||||
If there are multiple bindings to the same style attribute, Angular uses [styling precedence](guide/style-precedence) to determine which binding to use.
|
||||
|
||||
The following table summarizes style binding syntax.
|
||||
@@ -260,10 +267,7 @@ However, using the above style binding syntax without `NgStyle` is preferred bec
|
||||
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a styling-precedence}
|
||||
|
||||
## Styling Precedence
|
||||
|
||||
A single HTML element can have its CSS class list and style values bound to multiple sources (for example, host bindings from multiple directives).
|
||||
@@ -326,8 +330,6 @@ In that case, if `dirWithHostBinding` sets its binding to `undefined`, the `widt
|
||||
However, if `dirWithHostBinding` sets its binding to `null`, the `width` property will be removed entirely.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
## Injecting attribute values
|
||||
|
||||
There are cases where you need to differentiate the behavior of a [Component](api/core/Component) or [Directive](api/core/Directive) based on a static value set on the host element as an HTML attribute. For example, you might have a directive that needs to know the `type` of a `<button>` or `<input>` element.
|
||||
|
||||
@@ -1,411 +1,174 @@
|
||||
# Attribute directives
|
||||
|
||||
An **Attribute** directive changes the appearance or behavior of a DOM element.
|
||||
With attribute directives, you can change the appearance or behavior of DOM elements and Angular components.
|
||||
|
||||
Try the <live-example title="Attribute Directive example"></live-example>.
|
||||
<div class="alert is-helpful">
|
||||
|
||||
{@a directive-overview}
|
||||
See the <live-example></live-example> for a working example containing the code snippets in this guide.
|
||||
|
||||
## Directives overview
|
||||
</div>
|
||||
|
||||
There are three kinds of directives in Angular:
|
||||
## Building an attribute directive
|
||||
|
||||
1. Components—directives with a template.
|
||||
1. Structural directives—change the DOM layout by adding and removing DOM elements.
|
||||
1. Attribute directives—change the appearance or behavior of an element, component, or another directive.
|
||||
This section walks you through creating a highlight directive that sets the background color of the host element to yellow.
|
||||
|
||||
*Components* are the most common of the three directives.
|
||||
You saw a component for the first time in the [Getting Started](start "Getting Started with Angular") tutorial.
|
||||
1. To create a directive, use the CLI command [`ng generate directive`](cli/generate).
|
||||
|
||||
*Structural Directives* change the structure of the view.
|
||||
Two examples are [NgFor](guide/built-in-directives#ngFor) and [NgIf](guide/built-in-directives#ngIf).
|
||||
Learn about them in the [Structural Directives](guide/structural-directives) guide.
|
||||
|
||||
*Attribute directives* are used as attributes of elements.
|
||||
The built-in [NgStyle](guide/built-in-directives#ngStyle) directive in the
|
||||
[Built-in directives](guide/built-in-directives) guide, for example,
|
||||
can change several element styles at the same time.
|
||||
|
||||
## Build a simple attribute directive
|
||||
|
||||
An attribute directive minimally requires building a controller class annotated with
|
||||
`@Directive`, which specifies the selector that identifies
|
||||
the attribute.
|
||||
The controller class implements the desired directive behavior.
|
||||
|
||||
This page demonstrates building a simple _appHighlight_ attribute
|
||||
directive to set an element's background color
|
||||
when the user hovers over that element. You can apply it like this:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (applied)" region="applied"></code-example>
|
||||
|
||||
{@a write-directive}
|
||||
|
||||
Please note that directives _do not_ support namespaces.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.avoid.html" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
|
||||
|
||||
### Write the directive code
|
||||
|
||||
Create the directive class file in a terminal window with the CLI command [`ng generate directive`](cli/generate).
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng generate directive highlight
|
||||
</code-example>
|
||||
|
||||
The CLI creates `src/app/highlight.directive.ts`, a corresponding test file `src/app/highlight.directive.spec.ts`, and _declares_ the directive class in the root `AppModule`.
|
||||
The CLI creates `src/app/highlight.directive.ts`, a corresponding test file `src/app/highlight.directive.spec.ts`, and declares the directive class in the `AppModule`.
|
||||
|
||||
The CLI generates the default `src/app/highlight.directive.ts` as follows:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.0.ts" header="src/app/highlight.directive.ts"></code-example>
|
||||
|
||||
The `@Directive()` decorator's configuration property specifies the directive's CSS attribute selector, `[appHighlight]`.
|
||||
|
||||
1. Import `ElementRef` from `@angular/core`.
|
||||
`ElementRef` grants direct access to the host DOM element through its `nativeElement` property.
|
||||
|
||||
1. Add `ElementRef` in the directive's `constructor()` to [inject](guide/dependency-injection) a reference to the host DOM element, the element to which you apply `appHighlight`.
|
||||
|
||||
1. Add logic to the `HighlightDirective` class that sets the background to yellow.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.1.ts" header="src/app/highlight.directive.ts"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Directives_ must be declared in [Angular Modules](guide/ngmodules) in the same manner as _components_.
|
||||
Directives _do not_ support namespaces.
|
||||
|
||||
</div >
|
||||
|
||||
The generated `src/app/highlight.directive.ts` is as follows:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.0.ts" header="src/app/highlight.directive.ts"></code-example>
|
||||
|
||||
The imported `Directive` symbol provides Angular the `@Directive` decorator.
|
||||
|
||||
The `@Directive` decorator's lone configuration property specifies the directive's
|
||||
[CSS attribute selector](https://developer.mozilla.org/docs/Web/CSS/Attribute_selectors), `[appHighlight]`.
|
||||
|
||||
It's the brackets (`[]`) that make it an attribute selector.
|
||||
Angular locates each element in the template that has an attribute named `appHighlight` and applies the logic of this directive to that element.
|
||||
|
||||
The _attribute selector_ pattern explains the name of this kind of directive.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
#### Why not "highlight"?
|
||||
|
||||
Though *highlight* would be a more concise selector than *appHighlight* and it would work,
|
||||
the best practice is to prefix selector names to ensure
|
||||
they don't conflict with standard HTML attributes.
|
||||
This also reduces the risk of colliding with third-party directive names.
|
||||
The CLI added the `app` prefix for you.
|
||||
|
||||
Make sure you do **not** prefix the `highlight` directive name with **`ng`** because
|
||||
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
|
||||
<code-example path="attribute-directives/src/app/app.component.avoid.html" header="src/app/app.component.avoid.html (unsupported)" region="unsupported"></code-example>
|
||||
|
||||
</div>
|
||||
|
||||
After the `@Directive` metadata comes the directive's controller class,
|
||||
called `HighlightDirective`, which contains the (currently empty) logic for the directive.
|
||||
Exporting `HighlightDirective` makes the directive accessible.
|
||||
|
||||
Now edit the generated `src/app/highlight.directive.ts` to look as follows:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.1.ts" header="src/app/highlight.directive.ts"></code-example>
|
||||
|
||||
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
|
||||
|
||||
You use the `ElementRef` in the directive's constructor
|
||||
to [inject](guide/dependency-injection) a reference to the host DOM element,
|
||||
the element to which you applied `appHighlight`.
|
||||
|
||||
`ElementRef` grants direct access to the host DOM element
|
||||
through its `nativeElement` property.
|
||||
|
||||
This first implementation sets the background color of the host element to yellow.
|
||||
|
||||
{@a apply-directive}
|
||||
## Applying an attribute directive
|
||||
|
||||
## Apply the attribute directive
|
||||
1. To use the `HighlightDirective`, add a `<p>` element to the HTML template with the directive as an attribute.
|
||||
|
||||
To use the new `HighlightDirective`, add a paragraph (`<p>`) element to the template of the root `AppComponent` and apply the directive as an attribute.
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html" region="applied"></code-example>
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html" region="applied"></code-example>
|
||||
|
||||
Now run the application to see the `HighlightDirective` in action.
|
||||
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng serve
|
||||
</code-example>
|
||||
|
||||
To summarize, Angular found the `appHighlight` attribute on the **host** `<p>` element.
|
||||
It created an instance of the `HighlightDirective` class and
|
||||
injected a reference to the `<p>` element into the directive's constructor
|
||||
which sets the `<p>` element's background style to yellow.
|
||||
Angular creates an instance of the `HighlightDirective` class and injects a reference to the `<p>` element into the directive's constructor, which sets the `<p>` element's background style to yellow.
|
||||
|
||||
{@a respond-to-user}
|
||||
|
||||
## Respond to user-initiated events
|
||||
## Handling user events
|
||||
|
||||
Currently, `appHighlight` simply sets an element color.
|
||||
The directive could be more dynamic.
|
||||
It could detect when the user mouses into or out of the element
|
||||
and respond by setting or clearing the highlight color.
|
||||
This section shows you how to detect when a user mouses into or out of the element and to respond by setting or clearing the highlight color.
|
||||
|
||||
Begin by adding `HostListener` to the list of imported symbols.
|
||||
1. Import `HostListener` from '@angular/core'.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
Then add two event handlers that respond when the mouse enters or leaves,
|
||||
each adorned by the `HostListener` decorator.
|
||||
1. Add two event handlers that respond when the mouse enters or leaves, each with the `@HostListener()` decorator.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (mouse-methods)" region="mouse-methods"></code-example>
|
||||
|
||||
The `@HostListener` decorator lets you subscribe to events of the DOM
|
||||
element that hosts an attribute directive, the `<p>` in this case.
|
||||
With the `@HostListener()` decorator, you can subscribe to events of the DOM element that hosts an attribute directive, the `<p>` in this case.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
The handlers delegate to a helper method, `highlight()`, that sets the color on the host DOM element, `el`.
|
||||
|
||||
Of course you could reach into the DOM with standard JavaScript and attach event listeners manually.
|
||||
There are at least three problems with _that_ approach:
|
||||
|
||||
1. You have to write the listeners correctly.
|
||||
1. The code must *detach* the listener when the directive is destroyed to avoid memory leaks.
|
||||
1. Talking to DOM API directly isn't a best practice.
|
||||
|
||||
</div>
|
||||
|
||||
The handlers delegate to a helper method that sets the color on the host DOM element, `el`.
|
||||
|
||||
The helper method, `highlight`, was extracted from the constructor.
|
||||
The revised constructor simply declares the injected `el: ElementRef`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (constructor)" region="ctor"></code-example>
|
||||
|
||||
Here's the updated directive in full:
|
||||
The complete directive is as follows:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts"></code-example>
|
||||
|
||||
Run the app and confirm that the background color appears when
|
||||
the pointer hovers over the paragraph element and disappears as the pointer moves out.
|
||||
The background color appears when the pointer hovers over the paragraph element and disappears as the pointer moves out.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight">
|
||||
</div>
|
||||
|
||||
{@a bindings}
|
||||
## Passing values into an attribute directive
|
||||
|
||||
## Pass values into the directive with an _@Input_ data binding
|
||||
This section walks you through setting the highlight color while applying the `HighlightDirective`.
|
||||
|
||||
Currently the highlight color is hard-coded _within_ the directive. That's inflexible.
|
||||
In this section, you give the developer the power to set the highlight color while applying the directive.
|
||||
1. In `highlight.directive.ts`, import `Input` from `@angular/core`.
|
||||
|
||||
Begin by adding `Input` to the list of symbols imported from `@angular/core`.
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (imports)" region="imports"></code-example>
|
||||
|
||||
Add a `highlightColor` property to the directive class like this:
|
||||
1. Add an `appHighlight` `@Input()` property.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (highlightColor)" region="color"></code-example>
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts" region="color-2"></code-example>
|
||||
|
||||
{@a input}
|
||||
The `@Input()` decorator adds metadata to the class that makes the directive's `appHighlight` property available for binding.
|
||||
|
||||
### Binding to an `@Input()` property
|
||||
1. In `app.component.ts`, add a `color` property to the `AppComponent`.
|
||||
|
||||
Notice the `@Input()` decorator. It adds metadata to the class that makes the directive's `highlightColor` property available for binding.
|
||||
<code-example path="attribute-directives/src/app/app.component.1.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
It's called an *input* property because data flows from the binding expression _into_ the directive.
|
||||
Without that `@Input()` metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more information.
|
||||
1. To simultaneously apply the directive and the color, use property binding with the `appHighlight` directive selector, setting it equal to `color`.
|
||||
|
||||
Try it by adding the following directive binding variations to the `AppComponent` template:
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-1"></code-example>
|
||||
The `[appHighlight]` attribute binding performs two tasks:
|
||||
|
||||
Add a `color` property to the `AppComponent`.
|
||||
* applies the highlighting directive to the `<p>` element
|
||||
* sets the directive's highlight color with a property binding
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
### Setting the value with user input
|
||||
|
||||
Let it control the highlight color with a property binding.
|
||||
This section guides you through adding radio buttons to bind your color choice to the `appHighlight` directive.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.1.html" header="src/app/app.component.html (excerpt)" region="color-2"></code-example>
|
||||
1. Add markup to `app.component.html` for choosing a color as follows:
|
||||
|
||||
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (v2)" region="v2"></code-example>
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
1. Revise the `AppComponent.color` so that it has no initial value.
|
||||
|
||||
The `[appHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
|
||||
and sets the directive's highlight color with a property binding.
|
||||
You're re-using the directive's attribute selector (`[appHighlight]`) to do both jobs.
|
||||
That's a crisp, compact syntax.
|
||||
<code-example path="attribute-directives/src/app/app.component.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
You'll have to rename the directive's `highlightColor` property to `appHighlight` because that's now the color property binding name.
|
||||
1. Serve your application to verify that the user can choose the color with the radio buttons.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (renamed to match directive selector)" region="color-2"></code-example>
|
||||
|
||||
This is disagreeable. The word, `appHighlight`, is a terrible property name and it doesn't convey the property's intent.
|
||||
|
||||
{@a input-alias}
|
||||
|
||||
### Bind to an _@Input_ alias
|
||||
|
||||
Fortunately you can name the directive property whatever you want _and_ **_alias it_** for binding purposes.
|
||||
|
||||
Restore the original property name and specify the selector as the alias in the argument to `@Input()`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color property with alias)" region="color"></code-example>
|
||||
|
||||
_Inside_ the directive the property is known as `highlightColor`.
|
||||
_Outside_ the directive, where you bind to it, it's known as `appHighlight`.
|
||||
|
||||
You get the best of both worlds: the property name you want and the binding syntax you want:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
Now that you're binding via the alias to the `highlightColor`, modify the `onMouseEnter()` method to use that property.
|
||||
If someone neglects to bind to `appHighlight`, highlight the host element in red:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (mouse enter)" region="mouse-enter"></code-example>
|
||||
|
||||
Here's the latest version of the directive class.
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.3.ts" header="src/app/highlight.directive.ts (excerpt)"></code-example>
|
||||
|
||||
## Write a harness to try it
|
||||
|
||||
It may be difficult to imagine how this directive actually works.
|
||||
In this section, you'll turn `AppComponent` into a harness that
|
||||
lets you pick the highlight color with a radio button and bind your color choice to the directive.
|
||||
|
||||
Update <code>app.component.html</code> as follows:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (v2)" region="v2"></code-example>
|
||||
|
||||
Revise the `AppComponent.color` so that it has no initial value.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.ts" header="src/app/app.component.ts (class)" region="class"></code-example>
|
||||
|
||||
Here are the harness and directive in action.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2">
|
||||
</div>
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-v2-anim.gif" alt="Animated gif of the refactored highlight directive changing color according to the radio button the user selects">
|
||||
</div>
|
||||
|
||||
{@a second-property}
|
||||
|
||||
## Bind to a second property
|
||||
## Binding to a second property
|
||||
|
||||
This highlight directive has a single customizable property. In a real app, it may need more.
|
||||
This section guides you through configuring your application so the developer can set the default color.
|
||||
|
||||
At the moment, the default color—the color that prevails until
|
||||
the user picks a highlight color—is hard-coded as "red".
|
||||
Let the template developer set the default color.
|
||||
1. Add a second `Input()` property to `HighlightDirective` called `defaultColor`.
|
||||
|
||||
Add a second **input** property to `HighlightDirective` called `defaultColor`:
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (defaultColor)" region="defaultColor"></code-example>
|
||||
1. Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`, then with the `defaultColor`, and falls back to `red` if both properties are `undefined`.
|
||||
|
||||
Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
|
||||
then with the `defaultColor`, and falls back to "red" if both properties are undefined.
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (mouse-enter)" region="mouse-enter"></code-example>
|
||||
1. To bind to the `AppComponent.color` and fall back to "violet" as the default color, add the following HTML.
|
||||
In this case, the `defaultColor` binding doesn't use square brackets, `[]`, because it is static.
|
||||
|
||||
How do you bind to a second property when you're already binding to the `appHighlight` attribute name?
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
|
||||
|
||||
As with components, you can add as many directive property bindings as you need by stringing them along in the template.
|
||||
The developer should be able to write the following template HTML to both bind to the `AppComponent.color`
|
||||
and fall back to "violet" as the default color.
|
||||
As with components, you can add multiple directive property bindings to a host element.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (defaultColor)" region="defaultColor"></code-example>
|
||||
The default color is red if there is no default color binding.
|
||||
When the user chooses a color the selected color becomes the active highlight color.
|
||||
|
||||
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
|
||||
because you made it _public_ with the `@Input()` decorator.
|
||||
|
||||
Here's how the harness should work when you're done coding.
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight">
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/attribute-directives/highlight-directive-final-anim.gif" alt="Animated gif of final highlight directive that shows red color with no binding and violet with the default color set. When user selects color, the selection takes precedence.">
|
||||
</div>
|
||||
|
||||
{@a ngNonBindable}
|
||||
|
||||
## `ngNonBindable`
|
||||
## Deactivating Angular processing with `NgNonBindable`
|
||||
|
||||
With the built-in template primitive `ngNonBindable`, Angular won't
|
||||
evaluate expressions in elements. For example:
|
||||
To prevent expression evaluation in the browser, add `ngNonBindable` to the host element.
|
||||
`ngNonBindable` deactivates interpolation, directives, and binding in templates.
|
||||
|
||||
In the following example, the expression `{{ 1 + 1 }}` renders just as it does in your code editor, and does not display `2`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html" region="ngNonBindable"></code-example>
|
||||
|
||||
The expression `{{ 1 + 1 }}` will render just as it does in your code editor,
|
||||
and will not display `2`. This is helpful when you want to render code in the browser.
|
||||
|
||||
When you apply `ngNonBindable` to an element, it stops any binding starting at that element, including child elements. However, `ngNonBindable` still allows
|
||||
directives to work to the element where you apply `ngNonBindable`. In the following example, the `appHighlight` directive will still be active but Angular will not evaluate the expression `{{ 1 + 1 }}`.
|
||||
Applying `ngNonBindable` to an element stops binding for that element's child elements.
|
||||
However, `ngNonBindable` still allows directives to work on the element where you apply `ngNonBindable`.
|
||||
In the following example, the `appHighlight` directive is still active but Angular does not evaluate the expression `{{ 1 + 1 }}`.
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" linenums="false" header="src/app/app.component.html" region="ngNonBindable-with-directive"></code-example>
|
||||
|
||||
Additionally, if you apply `ngNonBindable` to a parent element, interpolation and binding of any sort, such as property binding, or event binding, is disabled for its children.
|
||||
|
||||
## Summary
|
||||
|
||||
This page covered how to:
|
||||
|
||||
* [Build an **attribute directive**](guide/attribute-directives#write-directive) that modifies the behavior of an element.
|
||||
* [Apply the directive](guide/attribute-directives#apply-directive) to an element in a template.
|
||||
* [Respond to **events**](guide/attribute-directives#respond-to-user) that change the directive's behavior.
|
||||
* [**Bind** values to the directive](guide/attribute-directives#bindings).
|
||||
* [Prevent expression evaluation](guide/attribute-directives#ngNonBindable).
|
||||
|
||||
The final source code follows:
|
||||
|
||||
<code-tabs>
|
||||
<code-pane header="app/app.component.ts" path="attribute-directives/src/app/app.component.ts"></code-pane>
|
||||
<code-pane header="app/app.component.html" path="attribute-directives/src/app/app.component.html"></code-pane>
|
||||
<code-pane header="app/highlight.directive.ts" path="attribute-directives/src/app/highlight.directive.ts"></code-pane>
|
||||
<code-pane header="app/app.module.ts" path="attribute-directives/src/app/app.module.ts"></code-pane>
|
||||
<code-pane header="main.ts" path="attribute-directives/src/main.ts"></code-pane>
|
||||
<code-pane header="index.html" path="attribute-directives/src/index.html"></code-pane>
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
You can also experience and download the <live-example title="Attribute Directive example"></live-example>.
|
||||
|
||||
{@a why-input}
|
||||
|
||||
### Appendix: Why add `@Input()`?
|
||||
|
||||
In this demo, the `highlightColor` property is an `@Input()` property of
|
||||
the `HighlightDirective`. You've seen it applied without an alias:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
|
||||
You've seen it with an alias:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.ts" header="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
|
||||
Either way, the `@Input()` decorator tells Angular that this property is
|
||||
_public_ and available for binding by a parent component.
|
||||
Without `@Input()`, Angular refuses to bind to the property.
|
||||
|
||||
You've bound template HTML to component properties before and never used `@Input()`.
|
||||
What's different?
|
||||
|
||||
The difference is a matter of trust.
|
||||
Angular treats a component's template as _belonging_ to the component.
|
||||
The component and its template trust each other implicitly.
|
||||
Therefore, the component's own template may bind to _any_ property of that component,
|
||||
with or without the `@Input()` decorator.
|
||||
|
||||
But a component or directive shouldn't blindly trust _other_ components and directives.
|
||||
The properties of a component or directive are hidden from binding by default.
|
||||
They are _private_ from an Angular binding perspective.
|
||||
When adorned with the `@Input()` decorator, the property becomes _public_ from an Angular binding perspective.
|
||||
Only then can it be bound by some other component or directive.
|
||||
|
||||
You can tell if `@Input()` is needed by the position of the property name in a binding.
|
||||
|
||||
* When it appears in the template expression to the ***right*** of the equals (=),
|
||||
it belongs to the template's component and does not require the `@Input()` decorator.
|
||||
|
||||
* When it appears in **square brackets** ([ ]) to the **left** of the equals (=),
|
||||
the property belongs to some _other_ component or directive;
|
||||
that property must be adorned with the `@Input()` decorator.
|
||||
|
||||
Now apply that reasoning to the following example:
|
||||
|
||||
<code-example path="attribute-directives/src/app/app.component.html" header="src/app/app.component.html (color)" region="color"></code-example>
|
||||
|
||||
* The `color` property in the expression on the right belongs to the template's component.
|
||||
The template and its component trust each other.
|
||||
The `color` property doesn't require the `@Input()` decorator.
|
||||
|
||||
* The `appHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`,
|
||||
not a property of the template's component.
|
||||
For security, the directive property must carry the `@Input()` decorator.
|
||||
If you apply `ngNonBindable` to a parent element, Angular disables interpolation and binding of any sort, such as property binding or event binding, for the element's children.
|
||||
|
||||
@@ -353,24 +353,6 @@ The following polyfills are used to test the framework itself. They are a good s
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
<a id='intl' href="https://github.com/andyearnshaw/Intl.js">Intl</a>
|
||||
|
||||
</td>
|
||||
|
||||
<td>
|
||||
MIT / Unicode license
|
||||
</td>
|
||||
|
||||
<td>
|
||||
13.5KB
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
|
||||
<td>
|
||||
|
||||
@@ -98,7 +98,7 @@ export class AppComponent {
|
||||
|
||||
## Configure target-specific file replacements
|
||||
|
||||
The main CLI configuration file, `angular.json`, contains a `fileReplacements` section in the configuration for each build target, which allows you to replace any file with a target-specific version of that file.
|
||||
The main CLI configuration file, `angular.json`, contains a `fileReplacements` section in the configuration for each build target, which allows you to replace any file in the TypeScript program with a target-specific version of that file.
|
||||
This is useful for including target-specific code or variables in a build that targets a specific environment, such as production or staging.
|
||||
|
||||
By default no files are replaced.
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# Built-in directives
|
||||
|
||||
Angular offers two kinds of built-in directives: [_attribute_ directives](guide/attribute-directives) and [_structural_ directives](guide/structural-directives).
|
||||
Directives are classes that add additional behavior to elements
|
||||
in your Angular applications.
|
||||
With Angular's built-in directives, you can manage forms, lists, styles, and what users see.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -8,35 +10,31 @@ See the <live-example></live-example> for a working example containing the code
|
||||
|
||||
</div>
|
||||
|
||||
For more detail, including how to build your own custom directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives).
|
||||
The different types of Angular directives are as follows:
|
||||
|
||||
<hr/>
|
||||
1. [Components](guide/component-overview)—directives with a template.
|
||||
This type of directive is the most common directive type.
|
||||
1. [Attribute directives](guide/built-in-directives#built-in-attribute-directives)—directives that change the appearance or behavior of an element, component, or another directive.
|
||||
1. [Structural directives](guide/built-in-directives#built-in-structural-directives)—directives that change the DOM layout by adding and removing DOM elements.
|
||||
|
||||
This guide covers built-in [attribute directives](guide/built-in-directives#built-in-attribute-directives) and [structural directives](guide/built-in-directives#built-in-structural-directives).
|
||||
|
||||
{@a attribute-directives}
|
||||
|
||||
## Built-in attribute directives
|
||||
|
||||
Attribute directives listen to and modify the behavior of
|
||||
other HTML elements, attributes, properties, and components.
|
||||
You usually apply them to elements as if they were HTML attributes, hence the name.
|
||||
Attribute directives listen to and modify the behavior of other HTML elements, attributes, properties, and components.
|
||||
|
||||
Many NgModules such as the [`RouterModule`](guide/router "Routing and Navigation")
|
||||
and the [`FormsModule`](guide/forms "Forms") define their own attribute directives.
|
||||
Many NgModules such as the [`RouterModule`](guide/router "Routing and Navigation") and the [`FormsModule`](guide/forms "Forms") define their own attribute directives.
|
||||
The most common attribute directives are as follows:
|
||||
|
||||
* [`NgClass`](guide/built-in-directives#ngClass)—adds and removes a set of CSS classes.
|
||||
* [`NgStyle`](guide/built-in-directives#ngStyle)—adds and removes a set of HTML styles.
|
||||
* [`NgStyle`](guide/built-in-directives#ngstyle)—adds and removes a set of HTML styles.
|
||||
* [`NgModel`](guide/built-in-directives#ngModel)—adds two-way data binding to an HTML form element.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a ngClass}
|
||||
## Adding and removing classes with `NgClass`
|
||||
|
||||
## `NgClass`
|
||||
|
||||
Add or remove several CSS classes simultaneously with `ngClass`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="special-div" header="src/app/app.component.html"></code-example>
|
||||
You can add or remove multiple CSS classes simultaneously with `ngClass`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
@@ -44,120 +42,72 @@ To add or remove a *single* class, use [class binding](guide/attribute-binding#c
|
||||
|
||||
</div>
|
||||
|
||||
Consider a `setCurrentClasses()` component method that sets a component property,
|
||||
`currentClasses`, with an object that adds or removes three classes based on the
|
||||
`true`/`false` state of three other component properties. Each key of the object is a CSS class name; its value is `true` if the class should be added,
|
||||
`false` if it should be removed.
|
||||
### Using `NgClass` with an expression
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="setClasses" header="src/app/app.component.ts"></code-example>
|
||||
On the element you'd like to style, add `[ngClass]` and set it equal to an expression.
|
||||
In this case, `isSpecial` is a boolean set to `true` in `app.component.ts`.
|
||||
Because `isSpecial` is true, `ngClass` applies the class of `special` to the `<div>`.
|
||||
|
||||
Adding an `ngClass` property binding to `currentClasses` sets the element's classes accordingly:
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="special-div" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgClass-1" header="src/app/app.component.html"></code-example>
|
||||
### Using `NgClass` with a method
|
||||
|
||||
<div class="alert is-helpful">
|
||||
1. To use `NgClass` with a method, add the method to the component class.
|
||||
In the following example, `setCurrentClasses()` sets the property `currentClasses` with an object that adds or removes three classes based on the `true` or `false` state of three other component properties.
|
||||
|
||||
Remember that in this situation you'd call `setCurrentClasses()`,
|
||||
both initially and when the dependent properties change.
|
||||
Each key of the object is a CSS class name.
|
||||
If a key is `true`, `ngClass` adds the class.
|
||||
If a key is `false`, `ngClass` removes the class.
|
||||
|
||||
</div>
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="setClasses" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
<hr/>
|
||||
1. In the template, add the `ngClass` property binding to `currentClasses` to set the element's classes:
|
||||
|
||||
{@a ngStyle}
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgClass-1" header="src/app/app.component.html"></code-example>
|
||||
|
||||
## `NgStyle`
|
||||
For this use case, Angular applies the classes on initialization and in case of changes.
|
||||
The full example calls `setCurrentClasses()` initially with `ngOnInit()` and when the dependent properties change through a button click.
|
||||
These steps are not necessary to implement `ngClass`.
|
||||
For more information, see the <live-example></live-example> `app.component.ts` and `app.component.html`.
|
||||
|
||||
Use `NgStyle` to set many inline styles simultaneously and dynamically, based on the state of the component.
|
||||
{@a ngstyle}
|
||||
## Setting inline styles with `NgStyle`
|
||||
|
||||
### Without `NgStyle`
|
||||
You can use `NgStyle` to set multiple inline styles simultaneously, based on the state of the component.
|
||||
|
||||
For context, consider setting a *single* style value with [style binding](guide/attribute-binding#style-binding), without `NgStyle`.
|
||||
1. To use `NgStyle`, add a method to the component class.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="without-ng-style" header="src/app/app.component.html"></code-example>
|
||||
In the following example, `setCurrentStyles()` sets the property `currentStyles` with an object that defines three styles, based on the state of three other component properties.
|
||||
|
||||
However, to set *many* inline styles at the same time, use the `NgStyle` directive.
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="setStyles" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
The following is a `setCurrentStyles()` method that sets a component
|
||||
property, `currentStyles`, with an object that defines three styles,
|
||||
based on the state of three other component properties:
|
||||
1. To set the element's styles, add an `ngStyle` property binding to `currentStyles`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="setStyles" header="src/app/app.component.ts"></code-example>
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgStyle-2" header="src/app/app.component.html"></code-example>
|
||||
|
||||
Adding an `ngStyle` property binding to `currentStyles` sets the element's styles accordingly:
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgStyle-2" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Remember to call `setCurrentStyles()`, both initially and when the dependent properties change.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<hr/>
|
||||
For this use case, Angular applies the styles upon initialization and in case of changes.
|
||||
To do this, the full example calls `setCurrentStyles()` initially with `ngOnInit()` and when the dependent properties change through a button click.
|
||||
However, these steps are not necessary to implement `ngStyle` on its own.
|
||||
See the <live-example></live-example> `app.component.ts` and `app.component.html` for this optional implementation.
|
||||
|
||||
{@a ngModel}
|
||||
## Displaying and updating properties with `ngModel`
|
||||
|
||||
## `[(ngModel)]`: Two-way binding
|
||||
You can use the `NgModel` directive to display a data property and update that property when the user makes changes.
|
||||
|
||||
The `NgModel` directive allows you to display a data property and
|
||||
update that property when the user makes changes. Here's an example:
|
||||
1. Import `FormsModule` and add it to the NgModule's `imports` list.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" header="src/app/app.component.html (NgModel example)" region="NgModel-1"></code-example>
|
||||
<code-example path="built-in-directives/src/app/app.module.ts" header="src/app/app.module.ts (FormsModule import)" region="import-forms-module"></code-example>
|
||||
|
||||
1. Add an `[(ngModel)]` binding on an HTML `<form>` element and set it equal to the property, here `name`.
|
||||
|
||||
### Import `FormsModule` to use `ngModel`
|
||||
<code-example path="built-in-directives/src/app/app.component.html" header="src/app/app.component.html (NgModel example)" region="NgModel-1"></code-example>
|
||||
|
||||
Before using the `ngModel` directive in a two-way data binding,
|
||||
you must import the `FormsModule` and add it to the NgModule's `imports` list.
|
||||
Learn more about the `FormsModule` and `ngModel` in [Forms](guide/forms#ngModel).
|
||||
This `[(ngModel)]` syntax can only set a data-bound property.
|
||||
|
||||
Remember to import the `FormsModule` to make `[(ngModel)]` available as follows:
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.module.ts" header="src/app/app.module.ts (FormsModule import)" region="import-forms-module"></code-example>
|
||||
|
||||
|
||||
You could achieve the same result with separate bindings to
|
||||
the `<input>` element's `value` property and `input` event:
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="without-NgModel" header="src/app/app.component.html"></code-example>
|
||||
|
||||
To streamline the syntax, the `ngModel` directive hides the details behind its own `ngModel` input and `ngModelChange` output properties:
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgModelChange" header="src/app/app.component.html"></code-example>
|
||||
|
||||
The `ngModel` data property sets the element's value property and the `ngModelChange` event property
|
||||
listens for changes to the element's value.
|
||||
|
||||
### `NgModel` and value accessors
|
||||
|
||||
The details are specific to each kind of element and therefore the `NgModel` directive only works for an element
|
||||
supported by a [ControlValueAccessor](api/forms/ControlValueAccessor)
|
||||
that adapts an element to this protocol.
|
||||
Angular provides *value accessors* for all of the basic HTML form elements and the
|
||||
[Forms](guide/forms) guide shows how to bind to them.
|
||||
|
||||
You can't apply `[(ngModel)]` to a non-form native element or a
|
||||
third-party custom component until you write a suitable value accessor. For more information, see
|
||||
the API documentation on [DefaultValueAccessor](api/forms/DefaultValueAccessor).
|
||||
|
||||
You don't need a value accessor for an Angular component that
|
||||
you write because you can name the value and event properties
|
||||
to suit Angular's basic [two-way binding syntax](guide/two-way-binding)
|
||||
and skip `NgModel` altogether.
|
||||
The `sizer` in the
|
||||
[Two-way Binding](guide/two-way-binding) section is an example of this technique.
|
||||
|
||||
Separate `ngModel` bindings are an improvement over binding to the
|
||||
element's native properties, but you can streamline the binding with a
|
||||
single declaration using the `[(ngModel)]` syntax:
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgModel-1" header="src/app/app.component.html"></code-example>
|
||||
|
||||
This `[(ngModel)]` syntax can only _set_ a data-bound property.
|
||||
If you need to do something more, you can write the expanded form;
|
||||
for example, the following changes the `<input>` value to uppercase:
|
||||
To customize your configuration, you can write the expanded form, which separates the property and event binding.
|
||||
Use [property binding](guide/property-binding) to set the property and [event binding](guide/event-binding) to respond to changes.
|
||||
The following example changes the `<input>` value to uppercase:
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="uppercase" header="src/app/app.component.html"></code-example>
|
||||
|
||||
@@ -167,206 +117,146 @@ Here are all variations in action, including the uppercase version:
|
||||
<img src='generated/images/guide/built-in-directives/ng-model-anim.gif' alt="NgModel variations">
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
### `NgModel` and value accessors
|
||||
|
||||
The `NgModel` directive works for an element supported by a [ControlValueAccessor](api/forms/ControlValueAccessor).
|
||||
Angular provides *value accessors* for all of the basic HTML form elements.
|
||||
For more information, see [Forms](guide/forms).
|
||||
|
||||
To apply `[(ngModel)]` to a non-form native element or a third-party custom component, you have to write a value accessor.
|
||||
For more information, see the API documentation on [DefaultValueAccessor](api/forms/DefaultValueAccessor).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
When you write an Angular component, you don't need a value accessor or `NgModel` if you name the value and event properties according to Angular's [two-way binding syntax](guide/two-way-binding#how-two-way-binding-works).
|
||||
|
||||
</div>
|
||||
|
||||
{@a structural-directives}
|
||||
|
||||
## Built-in _structural_ directives
|
||||
## Built-in structural directives
|
||||
|
||||
Structural directives are responsible for HTML layout.
|
||||
They shape or reshape the DOM's structure, typically by adding, removing, and manipulating
|
||||
the host elements to which they are attached.
|
||||
They shape or reshape the DOM's structure, typically by adding, removing, and manipulating the host elements to which they are attached.
|
||||
|
||||
This section is an introduction to the common built-in structural directives:
|
||||
This section introduces the most common built-in structural directives:
|
||||
|
||||
* [`NgIf`](guide/built-in-directives#ngIf)—conditionally creates or destroys subviews from the template.
|
||||
* [`NgIf`](guide/built-in-directives#ngIf)—conditionally creates or disposes of subviews from the template.
|
||||
* [`NgFor`](guide/built-in-directives#ngFor)—repeat a node for each item in a list.
|
||||
* [`NgSwitch`](guide/built-in-directives#ngSwitch)—a set of directives that switch among alternative views.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
For more information, see [Structural Directives](guide/structural-directives).
|
||||
|
||||
The deep details of structural directives are covered in the
|
||||
[Structural Directives](guide/structural-directives) guide,
|
||||
which explains the following:
|
||||
|
||||
* Why you
|
||||
[prefix the directive name with an asterisk (\*)](guide/structural-directives#the-asterisk--prefix).
|
||||
* Using [`<ng-container>`](guide/structural-directives#ngcontainer "<ng-container>")
|
||||
to group elements when there is no suitable host element for the directive.
|
||||
* How to write your own structural directive.
|
||||
* Why you [can only apply one structural directive](guide/structural-directives#one-per-element "one per host element") to an element.
|
||||
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a ngIf}
|
||||
## Adding or removing an element with `NgIf`
|
||||
|
||||
## NgIf
|
||||
You can add or remove an element by applying an `NgIf` directive to a host element.
|
||||
|
||||
You can add or remove an element from the DOM by applying an `NgIf` directive to
|
||||
a host element.
|
||||
Bind the directive to a condition expression like `isActive` in this example.
|
||||
When `NgIf` is `false`, Angular removes an element and its descendants from the DOM.
|
||||
Angular then disposes of their components, which frees up memory and resources.
|
||||
|
||||
To add or remove an element, bind `*ngIf` to a condition expression such as `isActive` in the following example.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-1" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
When the `isActive` expression returns a truthy value, `NgIf` adds the `ItemDetailComponent` to the DOM.
|
||||
When the expression is falsy, `NgIf` removes the `ItemDetailComponent` from the DOM and disposes of the component and all of its sub-components.
|
||||
|
||||
Don't forget the asterisk (`*`) in front of `ngIf`. For more information
|
||||
on the asterisk, see the [asterisk (*) prefix](guide/structural-directives#the-asterisk--prefix) section of
|
||||
[Structural Directives](guide/structural-directives).
|
||||
For more information on `NgIf` and `NgIfElse`, see the [NgIf API documentation](api/common/NgIf).
|
||||
|
||||
</div>
|
||||
### Guarding against `null`
|
||||
|
||||
When the `isActive` expression returns a truthy value, `NgIf` adds the
|
||||
`ItemDetailComponent` to the DOM.
|
||||
When the expression is falsy, `NgIf` removes the `ItemDetailComponent`
|
||||
from the DOM, destroying that component and all of its sub-components.
|
||||
By default, `NgIf` prevents display of an element bound to a null value.
|
||||
|
||||
|
||||
### Show/hide vs. `NgIf`
|
||||
|
||||
Hiding an element is different from removing it with `NgIf`.
|
||||
For comparison, the following example shows how to control
|
||||
the visibility of an element with a
|
||||
[class](guide/attribute-binding#class-binding) or [style](guide/attribute-binding#style-binding) binding.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-3" header="src/app/app.component.html"></code-example>
|
||||
|
||||
When you hide an element, that element and all of its descendants remain in the DOM.
|
||||
All components for those elements stay in memory and
|
||||
Angular may continue to check for changes.
|
||||
You could be holding onto considerable computing resources and degrading performance
|
||||
unnecessarily.
|
||||
|
||||
`NgIf` works differently. When `NgIf` is `false`, Angular removes the element and its descendants from the DOM.
|
||||
It destroys their components, freeing up resources, which
|
||||
results in a better user experience.
|
||||
|
||||
If you are hiding large component trees, consider `NgIf` as a more
|
||||
efficient alternative to showing/hiding.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
For more information on `NgIf` and `ngIfElse`, see the [API documentation about NgIf](api/common/NgIf).
|
||||
|
||||
</div>
|
||||
|
||||
### Guard against null
|
||||
|
||||
Another advantage of `ngIf` is that you can use it to guard against null. Show/hide
|
||||
is best suited for very simple use cases, so when you need a guard, opt instead for `ngIf`. Angular will throw an error if a nested expression tries to access a property of `null`.
|
||||
|
||||
The following shows `NgIf` guarding two `<div>`s.
|
||||
The `currentCustomer` name appears only when there is a `currentCustomer`.
|
||||
The `nullCustomer` will not be displayed as long as it is `null`.
|
||||
To use `NgIf` to guard a `<div>`, add `*ngIf="yourProperty"` to the `<div>`.
|
||||
In the following example, the `currentCustomer` name appears because there is a `currentCustomer`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2" header="src/app/app.component.html"></code-example>
|
||||
|
||||
However, if the property is `null`, Angular does not display the `<div>`.
|
||||
In this example, Angular does not display the `nullCustomer` because it is `null`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgIf-2b" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a ngFor}
|
||||
## `NgFor`
|
||||
## Listing items with `NgFor`
|
||||
|
||||
`NgFor` is a repeater directive—a way to present a list of items.
|
||||
You define a block of HTML that defines how a single item should be displayed
|
||||
and then you tell Angular to use that block as a template for rendering each item in the list.
|
||||
The text assigned to `*ngFor` is the instruction that guides the repeater process.
|
||||
You can use the `NgFor` directive to present a list of items.
|
||||
|
||||
The following example shows `NgFor` applied to a simple `<div>`.
|
||||
1. Define a block of HTML that determines how Angular renders a single item.
|
||||
|
||||
1. To list your items, assign the short hand `let item of items` to `*ngFor`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
The string `"let item of items"` instructs Angular to do the following:
|
||||
|
||||
Don't forget the asterisk (`*`) in front of `ngFor`. For more information
|
||||
on the asterisk, see the [asterisk (*) prefix](guide/structural-directives#the-asterisk--prefix) section of
|
||||
[Structural Directives](guide/structural-directives).
|
||||
* Store each item in the `items` array in the local `item` looping variable
|
||||
* Make each item available to the templated HTML for each iteration
|
||||
* Translate `"let item of items"` into an `<ng-template>` around the host element
|
||||
* Repeat the `<ng-template>` for each `item` in the list
|
||||
|
||||
</div>
|
||||
For more information see the [`<ng-template>` section](guide/structural-directives#the-ng-template) of [Structural directives](guide/structural-directives).
|
||||
### Repeating a component view
|
||||
|
||||
You can also apply an `NgFor` to a component element, as in the following example.
|
||||
To repeat a component element, apply `*ngFor` to the selector.
|
||||
In the following example, the selector is `<app-item-detail>`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-2" header="src/app/app.component.html"></code-example>
|
||||
|
||||
{@a microsyntax}
|
||||
You can reference a template input variable, such as `item`, in the following locations:
|
||||
|
||||
<div class="callout is-critical">
|
||||
<header>*ngFor microsyntax</header>
|
||||
* within the `ngFor` host element
|
||||
* within the host element descendants to access the item's properties
|
||||
|
||||
The string assigned to `*ngFor` is not a [template expression](guide/interpolation). Rather,
|
||||
it's a *microsyntax*—a little language of its own that Angular interprets.
|
||||
The string `"let item of items"` means:
|
||||
|
||||
> *Take each item in the `items` array, store it in the local `item` looping variable, and
|
||||
make it available to the templated HTML for each iteration.*
|
||||
|
||||
Angular translates this instruction into an `<ng-template>` around the host element,
|
||||
then uses this template repeatedly to create a new set of elements and bindings for each `item`
|
||||
in the list.
|
||||
For more information about microsyntax, see the [Structural Directives](guide/structural-directives#microsyntax) guide.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
{@a template-input-variable}
|
||||
|
||||
{@a template-input-variables}
|
||||
|
||||
### Template input variables
|
||||
|
||||
The `let` keyword before `item` creates a template input variable called `item`.
|
||||
The `ngFor` directive iterates over the `items` array returned by the parent component's `items` property
|
||||
and sets `item` to the current item from the array during each iteration.
|
||||
|
||||
Reference `item` within the `ngFor` host element
|
||||
as well as within its descendants to access the item's properties.
|
||||
The following example references `item` first in an interpolation
|
||||
and then passes in a binding to the `item` property of the `<app-item-detail>` component.
|
||||
The following example references `item` first in an interpolation and then passes in a binding to the `item` property of the `<app-item-detail>` component.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-1-2" header="src/app/app.component.html"></code-example>
|
||||
|
||||
For more information about template input variables, see
|
||||
[Structural Directives](guide/structural-directives#template-input-variable).
|
||||
For more information about template input variables, see [Structural Directives](guide/structural-directives#template-input-variable).
|
||||
|
||||
### `*ngFor` with `index`
|
||||
### Getting the `index` of `*ngFor`
|
||||
|
||||
The `index` property of the `NgFor` directive context
|
||||
returns the zero-based index of the item in each iteration.
|
||||
You can capture the `index` in a template input variable and use it in the template.
|
||||
You can get the `index` of `*ngFor` in a template input variable and use it in the template.
|
||||
|
||||
The next example captures the `index` in a variable named `i` and displays it with the item name.
|
||||
In the `*ngFor`, add a semicolon and `let i=index` to the short hand.
|
||||
The following example gets the `index` in a variable named `i` and displays it with the item name.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgFor-3" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
The index property of the `NgFor` directive context returns the zero-based index of the item in each iteration.
|
||||
|
||||
`NgFor` is implemented by the `NgForOf` directive. Read more about the other `NgForOf` context values such as `last`, `even`,
|
||||
and `odd` in the [NgForOf API reference](api/common/NgForOf).
|
||||
{@a one-per-element}
|
||||
|
||||
</div>
|
||||
## Repeating elements when a condition is true
|
||||
|
||||
{@a trackBy}
|
||||
### *ngFor with `trackBy`
|
||||
To repeat a block of HTML when a particular condition is true, put the `*ngIf` on a container element that wraps an `*ngFor` element.
|
||||
One or both elements can be an `<ng-container>` so you don't have to introduce extra levels of HTML.
|
||||
|
||||
If you use `NgFor` with large lists, a small change to one item, such as removing or adding an item, can trigger a cascade of DOM manipulations. For example, re-querying the server could reset a list with all new item objects, even when those items were previously displayed. In this case, Angular sees only a fresh list of new object references and has no choice but to replace the old DOM elements with all new DOM elements.
|
||||
Because structural directives add and remove nodes from the DOM, apply only one structural directive per element.
|
||||
|
||||
You can make this more efficient with `trackBy`.
|
||||
Add a method to the component that returns the value `NgFor` should track.
|
||||
In this case, that value is the hero's `id`. If the `id` has already been rendered,
|
||||
Angular keeps track of it and doesn't re-query the server for the same `id`.
|
||||
For more information about `NgFor` see the [NgForOf API reference](api/common/NgForOf).
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="trackByItems" header="src/app/app.component.ts"></code-example>
|
||||
{@a ngfor-with-trackby}
|
||||
### Tracking items with `*ngFor` `trackBy`
|
||||
|
||||
In the microsyntax expression, set `trackBy` to the `trackByItems()` method.
|
||||
By tracking changes to an item list, you can reduce the number of calls your application makes to the server.
|
||||
With the `*ngFor` `trackBy` property, Angular can change and re-render only those items that have changed, rather than reloading the entire list of items.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="trackBy" header="src/app/app.component.html"></code-example>
|
||||
1. Add a method to the component that returns the value `NgFor` should track.
|
||||
In this example, the value to track is the item's `id`.
|
||||
If the browser has already rendered `id`, Angular keeps track of it and doesn't re-query the server for the same `id`.
|
||||
|
||||
Here is an illustration of the `trackBy` effect.
|
||||
"Reset items" creates new items with the same `item.id`s.
|
||||
"Change ids" creates new items with new `item.id`s.
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="trackByItems" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
1. In the short hand expression, set `trackBy` to the `trackByItems()` method.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="trackBy" header="src/app/app.component.html"></code-example>
|
||||
|
||||
**Change ids** creates new items with new `item.id`s.
|
||||
In the following illustration of the `trackBy` effect, **Reset items** creates new items with the same `item.id`s.
|
||||
|
||||
* With no `trackBy`, both buttons trigger complete DOM element replacement.
|
||||
* With `trackBy`, only changing the `id` triggers element replacement.
|
||||
@@ -375,55 +265,82 @@ Here is an illustration of the `trackBy` effect.
|
||||
<img src="generated/images/guide/built-in-directives/ngfor-trackby.gif" alt="Animation of trackBy">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
Built-in directives use only public APIs; that is,
|
||||
they do not have special access to any private APIs that other directives can't access.
|
||||
Built-in directives use only public APIs.
|
||||
They do not have special access to any private APIs that other directives can't access.
|
||||
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
{@a ngcontainer}
|
||||
|
||||
{@a ngSwitch}
|
||||
## The `NgSwitch` directives
|
||||
## Hosting a directive without a DOM element
|
||||
|
||||
NgSwitch is like the JavaScript `switch` statement.
|
||||
It displays one element from among several possible elements, based on a switch condition.
|
||||
Angular puts only the selected element into the DOM.
|
||||
<!-- API Flagged -->
|
||||
`NgSwitch` is actually a set of three, cooperating directives:
|
||||
`NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault` as in the following example.
|
||||
The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.
|
||||
|
||||
You can use [`<ng-container>`](guide/structural-directives#ngcontainer) when there's no single element to host the directive.
|
||||
|
||||
Here's a conditional paragraph using `<ng-container>`.
|
||||
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (ngif-ngcontainer)" region="ngif-ngcontainer"></code-example>
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
|
||||
<img src='generated/images/guide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style">
|
||||
</div>
|
||||
|
||||
`NgSwitch` is the controller directive. Bind it to an expression that returns
|
||||
the *switch value*, such as `feature`. Though the `feature` value in this
|
||||
example is a string, the switch value can be of any type.
|
||||
1. Import the `ngModel` directive from `FormsModule`.
|
||||
|
||||
**Bind to `[ngSwitch]`**. You'll get an error if you try to set `*ngSwitch` because
|
||||
`NgSwitch` is an *attribute* directive, not a *structural* directive.
|
||||
Rather than touching the DOM directly, it changes the behavior of its companion directives.
|
||||
1. Add `FormsModule` to the imports section of the relevant Angular module.
|
||||
|
||||
**Bind to `*ngSwitchCase` and `*ngSwitchDefault`**.
|
||||
The `NgSwitchCase` and `NgSwitchDefault` directives are _structural_ directives
|
||||
because they add or remove elements from the DOM.
|
||||
1. To conditionally exclude an `<option>`, wrap the `<option>` in an `<ng-container>`.
|
||||
|
||||
* `NgSwitchCase` adds its element to the DOM when its bound value equals the switch value and removes
|
||||
its bound value when it doesn't equal the switch value.
|
||||
<code-example path="structural-directives/src/app/app.component.html" header="src/app/app.component.html (select-ngcontainer)" region="select-ngcontainer"></code-example>
|
||||
|
||||
* `NgSwitchDefault` adds its element to the DOM when there is no selected `NgSwitchCase`.
|
||||
<div class="lightbox">
|
||||
<img src='generated/images/guide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly">
|
||||
</div>
|
||||
|
||||
The switch directives are particularly useful for adding and removing *component elements*.
|
||||
This example switches among four `item` components defined in the `item-switch.components.ts` file.
|
||||
Each component has an `item` [input property](guide/inputs-outputs#input "Input property")
|
||||
which is bound to the `currentItem` of the parent component.
|
||||
{@a ngSwitch}
|
||||
## Switching cases with `NgSwitch`
|
||||
|
||||
Switch directives work as well with native elements and web components too.
|
||||
For example, you could replace the `<app-best-item>` switch case with the following.
|
||||
Like the JavaScript `switch` statement, `NgSwitch` displays one element from among several possible elements, based on a switch condition.
|
||||
Angular puts only the selected element into the DOM.
|
||||
<!-- API Flagged -->
|
||||
`NgSwitch` is a set of three directives:
|
||||
|
||||
* `NgSwitch`—an attribute directive that changes the behavior of its companion directives.
|
||||
* `NgSwitchCase`—structural directive that adds its element to the DOM when its bound value equals the switch value and removes its bound value when it doesn't equal the switch value.
|
||||
* `NgSwitchDefault`—structural directive that adds its element to the DOM when there is no selected `NgSwitchCase`.
|
||||
|
||||
1. On an element, such as a `<div>`, add `[ngSwitch]` bound to an expression that returns the switch value, such as `feature`.
|
||||
Though the `feature` value in this example is a string, the switch value can be of any type.
|
||||
|
||||
1. Bind to `*ngSwitchCase` and `*ngSwitchDefault` on the elements for the cases.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch" header="src/app/app.component.html"></code-example>
|
||||
|
||||
1. In the parent component, define `currentItem` so you can use it in the `[ngSwitch]` expression.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.ts" region="item" header="src/app/app.component.ts"></code-example>
|
||||
|
||||
1. In each child component, add an `item` [input property](guide/inputs-outputs#input "Input property") which is bound to the `currentItem` of the parent component.
|
||||
The following two snippets show the parent component and one of the child components.
|
||||
The other child components are identical to `StoutItemComponent`.
|
||||
|
||||
<code-example path="built-in-directives/src/app/item-switch.component.ts" region="input" header="In each child component, here StoutItemComponent"></code-example>
|
||||
|
||||
<div class="lightbox">
|
||||
<img src="generated/images/guide/built-in-directives/ngswitch.gif" alt="Animation of NgSwitch">
|
||||
</div>
|
||||
|
||||
Switch directives also work with native HTML elements and web components.
|
||||
For example, you could replace the `<app-best-item>` switch case with a `<div>` as follows.
|
||||
|
||||
<code-example path="built-in-directives/src/app/app.component.html" region="NgSwitch-div" header="src/app/app.component.html"></code-example>
|
||||
|
||||
<hr />
|
||||
|
||||
## What's next
|
||||
|
||||
For information on how to build your own custom directives, see [Attribute Directives](guide/attribute-directives) and [Structural Directives](guide/structural-directives).
|
||||
|
||||
@@ -90,11 +90,8 @@ The revised second version does depend on `Logger` service.
|
||||
|
||||
</code-tabs>
|
||||
|
||||
|
||||
In this example, the `getHeroes()` method uses the `Logger` service by logging a message when fetching heroes.
|
||||
|
||||
<hr />
|
||||
|
||||
## What's next
|
||||
|
||||
* [Dependency providers](guide/dependency-injection-providers)
|
||||
|
||||
@@ -156,10 +156,7 @@ Check out [angular-cli-ghpages](https://github.com/angular-buch/angular-cli-ghpa
|
||||
|
||||
</div>
|
||||
|
||||
<hr>
|
||||
|
||||
{@a server-configuration}
|
||||
|
||||
## Server configuration
|
||||
|
||||
This section covers changes you may have to make to the server or to files deployed on the server.
|
||||
@@ -299,10 +296,7 @@ The server must be configured to accept the application's requests.
|
||||
Read about how to enable CORS for specific servers at
|
||||
<a href="https://enable-cors.org/server.html" title="Enabling CORS server">enable-cors.org</a>.
|
||||
|
||||
<hr>
|
||||
|
||||
{@a optimize}
|
||||
|
||||
## Production optimizations
|
||||
|
||||
The `--prod` _meta-flag_ engages the following build optimization features.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,8 +52,6 @@ When your custom element is placed on a page, the browser creates an instance of
|
||||
<img src="generated/images/guide/elements/customElement1.png" alt="Custom element in browser" class="left">
|
||||
</div>
|
||||
|
||||
<hr class="clear">
|
||||
|
||||
## Transforming components to custom elements
|
||||
|
||||
Angular provides the `createCustomElement()` function for converting an Angular component,
|
||||
@@ -192,7 +190,7 @@ class MyDialog {
|
||||
}
|
||||
```
|
||||
|
||||
The most straight forward way to get accurate typings is to cast the return value of the relevant DOM methods to the correct type. For that, you can use the `NgElement` and `WithProperties` types (both exported from `@angular/elements`):
|
||||
The most straightforward way to get accurate typings is to cast the return value of the relevant DOM methods to the correct type. For that, you can use the `NgElement` and `WithProperties` types (both exported from `@angular/elements`):
|
||||
|
||||
```ts
|
||||
const aDialog = document.createElement('my-dialog') as NgElement & WithProperties<{content: string}>;
|
||||
|
||||
@@ -107,8 +107,6 @@ the tree shaker will throw it away. So, it's best to add only the components tha
|
||||
as trim as possible.
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
## More on Angular modules
|
||||
|
||||
You may also be interested in the following:
|
||||
|
||||
@@ -59,8 +59,6 @@ In the following example, Angular checks to see if `myClick` is an event on the
|
||||
|
||||
If the target event name, `myClick` fails to match an element event or an output property of `ClickDirective`, Angular reports an "unknown directive" error.
|
||||
|
||||
<hr />
|
||||
|
||||
## What's next
|
||||
|
||||
For more information on how event binding works, see [How event binding works](guide/event-binding-concepts).
|
||||
|
||||
@@ -106,8 +106,6 @@ Now, in addition to the title that renders by default, the `CustomerDashboardCom
|
||||
<img src="generated/images/guide/feature-modules/feature-module.png" alt="feature module component">
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
## More on NgModules
|
||||
|
||||
You may also be interested in the following:
|
||||
|
||||
@@ -115,8 +115,6 @@ Angular returns an error telling you to use `CommonModule` instead.
|
||||
<img src="generated/images/guide/frequent-ngmodules/browser-module-error.gif" width=750 alt="BrowserModule error">
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
## More on NgModules
|
||||
|
||||
|
||||
@@ -1183,8 +1183,6 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
|
||||
</div>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
## More on dependency injection
|
||||
|
||||
For more information on Angular dependency injection, see the [DI Providers](guide/dependency-injection-providers) and [DI in Action](guide/dependency-injection-in-action) guides.
|
||||
|
||||
@@ -389,7 +389,7 @@ In the following example, the `HeroesService` makes an HTTP POST request when ad
|
||||
The `HttpClient.post()` method is similar to `get()` in that it has a type parameter, which you can use to specify that you expect the server to return data of a given type. The method takes a resource URL and two additional parameters:
|
||||
|
||||
* *body* - The data to POST in the body of the request.
|
||||
* *options*` - An object containing method options which, in this case, [specify required headers](#adding-headers).
|
||||
* *options* - An object containing method options which, in this case, [specify required headers](#adding-headers).
|
||||
|
||||
The example catches errors as [described above](#error-details).
|
||||
|
||||
@@ -741,6 +741,10 @@ To do this, set the cloned request body to `null`.
|
||||
newReq = req.clone({ body: null }); // clear the body
|
||||
```
|
||||
|
||||
## Http interceptor use-cases
|
||||
|
||||
Below are a number of common uses for interceptors.
|
||||
|
||||
### Setting default headers
|
||||
|
||||
Apps often use an interceptor to set default headers on outgoing requests.
|
||||
@@ -768,7 +772,7 @@ An interceptor that alters headers can be used for a number of different operati
|
||||
* Caching behavior; for example, `If-Modified-Since`
|
||||
* XSRF protection
|
||||
|
||||
### Using interceptors for logging
|
||||
### Logging request and response pairs
|
||||
|
||||
Because interceptors can process the request and response _together_, they can perform tasks such as timing and logging an entire HTTP operation.
|
||||
|
||||
@@ -788,9 +792,42 @@ and reports the outcome to the `MessageService`.
|
||||
|
||||
Neither `tap` nor `finalize` touch the values of the observable stream returned to the caller.
|
||||
|
||||
{@a caching}
|
||||
{@a custom-json-parser}
|
||||
|
||||
### Using interceptors for caching
|
||||
### Custom JSON parsing
|
||||
|
||||
Interceptors can be used to replace the built-in JSON parsing with a custom implementation.
|
||||
|
||||
The `CustomJsonInterceptor` in the following example demonstrates how to achieve this.
|
||||
If the intercepted request expects a `'json'` response, the `reponseType` is changed to `'text'`
|
||||
to disable the built-in JSON parsing. Then the response is parsed via the injected `JsonParser`.
|
||||
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/custom-json-interceptor.ts"
|
||||
region="custom-json-interceptor"
|
||||
header="app/http-interceptors/custom-json-interceptor.ts">
|
||||
</code-example>
|
||||
|
||||
You can then implement your own custom `JsonParser`.
|
||||
Here is a custom JsonParser that has a special date reviver.
|
||||
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/custom-json-interceptor.ts"
|
||||
region="custom-json-parser"
|
||||
header="app/http-interceptors/custom-json-interceptor.ts">
|
||||
</code-example>
|
||||
|
||||
You provide the `CustomParser` along with the `CustomJsonInterceptor`.
|
||||
|
||||
<code-example
|
||||
path="http/src/app/http-interceptors/index.ts"
|
||||
region="custom-json-interceptor"
|
||||
header="app/http-interceptors/index.ts">
|
||||
</code-example>
|
||||
|
||||
|
||||
{@a caching}
|
||||
### Caching requests
|
||||
|
||||
Interceptors can handle requests by themselves, without forwarding to `next.handle()`.
|
||||
|
||||
|
||||
@@ -762,7 +762,7 @@ The HTML `base` tag with the `href` attribute specifies the base URI, or URL, fo
|
||||
"locales": {
|
||||
"fr": {
|
||||
"translation": "src/locale/messages.fr.xlf",
|
||||
"baseHref": ""
|
||||
"baseHref": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -806,11 +806,11 @@ server {
|
||||
server_name localhost;
|
||||
root /www/data;
|
||||
|
||||
# Fallback to default language if no preference defined by browser
|
||||
# Fallback to default language if no preference defined by browser
|
||||
if ($accept_language ~ "^$") {
|
||||
set $accept_language "fr";
|
||||
}
|
||||
|
||||
|
||||
# Redirect "/" to Angular app in browser's preferred language
|
||||
rewrite ^/$ /$accept_language permanent;
|
||||
|
||||
@@ -834,17 +834,17 @@ The following is an example of an Apache configuration.
|
||||
RewriteEngine on
|
||||
RewriteBase /
|
||||
RewriteRule ^../index\.html$ - [L]
|
||||
|
||||
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule (..) $1/index.html [L]
|
||||
|
||||
|
||||
RewriteCond %{HTTP:Accept-Language} ^de [NC]
|
||||
RewriteRule ^$ /de/ [R]
|
||||
|
||||
|
||||
RewriteCond %{HTTP:Accept-Language} ^en [NC]
|
||||
RewriteRule ^$ /en/ [R]
|
||||
|
||||
|
||||
RewriteCond %{HTTP:Accept-Language} !^en [NC]
|
||||
RewriteCond %{HTTP:Accept-Language} !^de [NC]
|
||||
RewriteRule ^$ /fr/ [R]
|
||||
@@ -979,3 +979,5 @@ Both elements now use the same translation (`Bonjour`) because they were defined
|
||||
<!-- ... -->
|
||||
<p>Bonjour</p>
|
||||
```
|
||||
|
||||
@reviewed 2020-04-10
|
||||
@@ -86,7 +86,7 @@ In the following snippet, the expression `recommended` and the expression `itemI
|
||||
|
||||
<code-example path="interpolation/src/app/app.component.html" region="component-context" header="src/app/app.component.html"></code-example>
|
||||
|
||||
An expression can also refer to properties of the _template's_ context such as a [template input variable](guide/built-in-directives#template-input-variables) or a [template reference variable](guide/template-reference-variables).
|
||||
An expression can also refer to properties of the _template's_ context such as a [template input variable](guide/structural-directives#template-input-variables) or a [template reference variable](guide/template-reference-variables).
|
||||
|
||||
The following example uses a template input variable of `customer`.
|
||||
|
||||
|
||||
@@ -54,13 +54,13 @@ You can then click "Go to definition" or press F12 to go directly to the definit
|
||||
## Angular Language Service in your editor
|
||||
|
||||
Angular Language Service is currently available as an extension for [Visual Studio Code](https://code.visualstudio.com/),
|
||||
[WebStorm](https://www.jetbrains.com/webstorm), and [Sublime Text](https://www.sublimetext.com/).
|
||||
[WebStorm](https://www.jetbrains.com/webstorm), [Sublime Text](https://www.sublimetext.com/) and [Eclipse IDE](https://www.eclipse.org/eclipseide/).
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
In [Visual Studio Code](https://code.visualstudio.com/), install the extension from the [Extensions: Marketplace](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template). You can open the marketplace from the editor using the Extensions icon on the left menu pane, or use VS Quick Open (⌘+P on Mac, CTRL+P on Windows) and type "? ext".
|
||||
In [Visual Studio Code](https://code.visualstudio.com/), install the extension from the [Extensions: Marketplace](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template). You can open the marketplace from the editor using the Extensions icon on the left menu pane, or use VS Quick Open (⌘+P on Mac, CTRL+P on Windows) and type "? ext". In the marketplace, search for Angular Language Service extension, and click the **Install** button.
|
||||
|
||||
In the marketplace, search for Angular Language Service extension, and click the **Install** button.
|
||||
The Visual Studio Code integration with the Angular language service is maintained and distributed by the Angular team.
|
||||
|
||||
### WebStorm
|
||||
|
||||
@@ -103,7 +103,9 @@ npm install --save-dev @angular/language-service
|
||||
|
||||
This allows the Angular Language Service to provide diagnostics and completions in `.ts` files.
|
||||
|
||||
### Eclipse IDE
|
||||
|
||||
Either directly install the "Eclipse IDE for Web and JavaScript developers" package which comes with the Angular Language Server included, or from other Eclipse IDE packages, use Help > Eclipse Marketplace to find and install [Eclipse Wild Web Developer](https://marketplace.eclipse.org/content/wild-web-developer-html-css-javascript-typescript-nodejs-angular-json-yaml-kubernetes-xml).
|
||||
|
||||
|
||||
## How the Language Service works
|
||||
@@ -123,8 +125,6 @@ The HTML AST can only tell the compiler that there is some text with the charact
|
||||
That's when the template parser produces an expression AST, which resides within the template AST.
|
||||
The Angular Language Services then looks at `data.---` within its context, asks the TypeScript Language Service what the members of `data` are, and returns the list of possibilities.
|
||||
|
||||
<hr>
|
||||
|
||||
## More information
|
||||
|
||||
* For more in-depth information on the implementation, see the
|
||||
|
||||
@@ -17,7 +17,7 @@ For the final sample app with two lazy-loaded modules that this page describes,
|
||||
This section introduces the basic procedure for configuring a lazy-loaded route.
|
||||
For a step-by-step example, see the [step-by-step setup](#step-by-step) section on this page.
|
||||
|
||||
To lazy load Angular modules, use `loadchildren` (instead of `component`) in your `AppRoutingModule` `routes` configuration as follows.
|
||||
To lazy load Angular modules, use `loadChildren` (instead of `component`) in your `AppRoutingModule` `routes` configuration as follows.
|
||||
|
||||
<code-example header="AppRoutingModule (excerpt)">
|
||||
|
||||
@@ -322,8 +322,13 @@ ngOnInit() {
|
||||
|
||||
For more information with a working example, see the [routing tutorial section on preloading](guide/router-tutorial-toh#preloading-background-loading-of-feature-areas).
|
||||
|
||||
## Troubleshooting lazy-loading modules
|
||||
|
||||
<hr>
|
||||
A common error when lazy-loading modules is importing common modules in multiple places within an application. You can test for this condition by first generating the module using the Angular CLI and including the `--route route-name` parameter, where `route-name` is the name of your module. Next, generate the module without the `--route` parameter. If the Angular CLI generates an error when you use the `--route` parameter, but runs correctly without it, you may have imported the same module in multiple places.
|
||||
|
||||
Remember, many common Angular modules should be imported at the base of your application.
|
||||
|
||||
For more information on Angular Modules, see [NgModules](guide/ngmodules).
|
||||
|
||||
## More on NgModules and routing
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Hooking into the component lifecycle
|
||||
# Lifecycle hooks
|
||||
|
||||
A component instance has a lifecycle that starts when Angular instantiates the component class and renders the component view along with its child views.
|
||||
The lifecycle continues with change detection, as Angular checks to see when data-bound properties change, and updates both the view and the component instance as needed.
|
||||
|
||||
@@ -152,7 +152,7 @@ abstract class LibHeaderToken {
|
||||
@Component({
|
||||
selector: 'lib-header',
|
||||
providers: [
|
||||
{provide: LibHeaderToken, useExisting: LibHeader}
|
||||
{provide: LibHeaderToken, useExisting: LibHeaderComponent}
|
||||
]
|
||||
...,
|
||||
})
|
||||
|
||||
@@ -220,8 +220,6 @@ The following table summarizes the `@NgModule` metadata properties.
|
||||
</table>
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
## More on NgModules
|
||||
|
||||
You may also be interested in the following:
|
||||
|
||||
@@ -12,17 +12,14 @@ Add [declarable](guide/bootstrapping#the-declarations-array) classes—compo
|
||||
Declare these classes in _exactly one_ module of the application.
|
||||
Declare them in a module if they belong to that particular module.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-declarable}
|
||||
|
||||
## What is a _declarable_?
|
||||
|
||||
Declarables are the class types—components, directives, and pipes—that
|
||||
you can add to a module's `declarations` list.
|
||||
They're the only classes that you can add to `declarations`.
|
||||
|
||||
<hr/>
|
||||
|
||||
## What classes should I _not_ add to `declarations`?
|
||||
|
||||
@@ -39,8 +36,6 @@ For example, don't declare `FORMS_DIRECTIVES` from `@angular/forms` because the
|
||||
* Non-Angular classes and objects, such as
|
||||
strings, numbers, functions, entity models, configurations, business logic, and helper classes.
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## Why list the same component in multiple `NgModule` properties?
|
||||
|
||||
@@ -56,7 +51,6 @@ Membership in one list doesn't imply membership in another list.
|
||||
* A component could be exported for inclusion in an external component's template
|
||||
as well as dynamically loaded in a pop-up dialog.
|
||||
|
||||
<hr/>
|
||||
|
||||
## What does "Can't bind to 'x' since it isn't a known property of 'y'" mean?
|
||||
|
||||
@@ -70,7 +64,6 @@ The "x" class isn't visible to other modules until you add it to the `exports` l
|
||||
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
## What should I import?
|
||||
|
||||
@@ -89,10 +82,8 @@ components, directives, and pipes.
|
||||
|
||||
Import [BrowserModule](guide/ngmodule-faq#q-browser-vs-common-module) only in the root `AppModule`.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-browser-vs-common-module}
|
||||
|
||||
## Should I import `BrowserModule` or `CommonModule`?
|
||||
|
||||
The root application module, `AppModule`, of almost every browser application
|
||||
@@ -110,10 +101,8 @@ They need the common directives. They don't need to re-install the app-wide prov
|
||||
|
||||
Importing `CommonModule` also frees feature modules for use on _any_ target platform, not just browsers.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-reimport}
|
||||
|
||||
## What if I import the same module twice?
|
||||
|
||||
That's not a problem. When three modules all import Module 'A',
|
||||
@@ -126,10 +115,8 @@ When Angular gets to the 'B' and 'A' in 'D', they're already cached and ready to
|
||||
|
||||
Angular doesn't like NgModules with circular references, so don't let Module 'A' import Module 'B', which imports Module 'A'.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-reexport}
|
||||
|
||||
## What should I export?
|
||||
|
||||
Export [declarable](guide/bootstrapping#the-declarations-array) classes that components in _other_ NgModules
|
||||
@@ -143,7 +130,6 @@ it's declared in this NgModule or in an imported NgModule.
|
||||
You _can_ re-export entire imported NgModules, which effectively re-export all of their exported classes.
|
||||
An NgModule can even export a module that it doesn't import.
|
||||
|
||||
<hr/>
|
||||
|
||||
## What should I *not* export?
|
||||
|
||||
@@ -159,9 +145,6 @@ While there's no harm in exporting them, there's also no benefit.
|
||||
For example, there's no point in re-exporting `HttpClientModule` because it doesn't export anything.
|
||||
Its only purpose is to add http service providers to the application as a whole.
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
|
||||
## Can I re-export classes and modules?
|
||||
|
||||
@@ -185,9 +168,6 @@ For example, there's no point in re-exporting `HttpClientModule` because it does
|
||||
Its only purpose is to add http service providers to the application as a whole.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## What is the `forRoot()` method?
|
||||
|
||||
The `forRoot()` static method is a convention that makes it easy for developers to configure services and providers that are intended to be singletons. A good example of `forRoot()` is the `RouterModule.forRoot()` method.
|
||||
@@ -211,9 +191,6 @@ configure services in root and feature modules respectively.
|
||||
Follow this convention when you write similar modules with configurable service providers.
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## Why is a service provided in a feature module visible everywhere?
|
||||
|
||||
Providers listed in the `@NgModule.providers` of a bootstrapped module have application scope.
|
||||
@@ -238,10 +215,8 @@ not just the classes declared in the `HeroModule`.
|
||||
|
||||
To limit access to a service, consider lazy loading the NgModule that provides that service. See [How do I restrict service scope to a module?](guide/ngmodule-faq#service-scope) for more information.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-lazy-loaded-module-provider-visibility}
|
||||
|
||||
## Why is a service provided in a lazy-loaded module visible only to that module?
|
||||
|
||||
Unlike providers of the modules loaded at launch,
|
||||
@@ -257,8 +232,6 @@ These providers are insulated from changes to application providers with the sam
|
||||
When the router creates a component within the lazy-loaded context,
|
||||
Angular prefers service instances created from these providers to the service instances of the application root injector.
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## What if two modules provide the same service?
|
||||
|
||||
@@ -277,10 +250,8 @@ that also provides a service for token 'X', then NgModule A's service definition
|
||||
The service provided by the root `AppModule` takes precedence over services provided by imported NgModules.
|
||||
The `AppModule` always wins.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a service-scope}
|
||||
|
||||
## How do I restrict service scope to a module?
|
||||
|
||||
When a module is loaded at application launch,
|
||||
@@ -329,11 +300,8 @@ Define child routes and let the router load module components into that outlet.
|
||||
|
||||
Though you can limit access to a service by providing it in a lazy loaded module or providing it in a component, providing services in a component can lead to multiple instances of those services. Thus, the lazy loading is preferable.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-root-component-or-module}
|
||||
|
||||
|
||||
## Should I add application-wide providers to the root `AppModule` or the root `AppComponent`?
|
||||
|
||||
Define application-wide providers by specifying `providedIn: 'root'` on its `@Injectable()` decorator (in the case of services) or at `InjectionToken` construction (in the case where tokens are provided). Providers that are created this way automatically are made available to the entire application and don't need to be listed in any module.
|
||||
@@ -366,10 +334,8 @@ But in routed applications, routing operates at the root level
|
||||
where `AppComponent` services don't exist.
|
||||
This means that lazy-loaded modules can't reach them.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-component-or-module}
|
||||
|
||||
## Should I add other providers to a module or a component?
|
||||
|
||||
Providers should be configured using `@Injectable` syntax. If possible, they should be provided in the application root (`providedIn: 'root'`). Services that are configured this way are lazily loaded if they are only used from a lazily loaded context.
|
||||
@@ -389,11 +355,8 @@ The changes that editor makes in its service don't touch the instances elsewhere
|
||||
[Always register _application-wide_ services with the root `AppModule`](guide/ngmodule-faq#q-root-component-or-module),
|
||||
not the root `AppComponent`.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-why-bad}
|
||||
|
||||
|
||||
## Why is it bad if a shared module provides a service to a lazy-loaded module?
|
||||
|
||||
### The eagerly loaded scenario
|
||||
@@ -426,10 +389,8 @@ Then toggle between the "Contact" and "Heroes" links a few times.
|
||||
The username goes bonkers as the Angular creates a new `UserService` instance each time.
|
||||
I'd like to see the error so I can include it.-->
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-why-child-injector}
|
||||
|
||||
## Why does lazy loading create a child injector?
|
||||
|
||||
Angular adds `@NgModule.providers` to the application root injector, unless the NgModule is lazy-loaded.
|
||||
@@ -453,10 +414,8 @@ Angular must add the lazy-loaded module's providers to an injector somewhere.
|
||||
It can't add them to the app root injector because that injector is closed to new providers.
|
||||
So Angular creates a new child injector for the lazy-loaded module context.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-is-it-loaded}
|
||||
|
||||
## How can I tell if an NgModule or service was previously loaded?
|
||||
|
||||
Some NgModules and their services should be loaded only once by the root `AppModule`.
|
||||
@@ -472,10 +431,8 @@ Here is a custom constructor for an NgModule called `GreetingModule`.
|
||||
|
||||
<code-example path="ngmodules/src/app/greeting/greeting.module.ts" region="ctor" header="src/app/greeting/greeting.module.ts (Constructor)"></code-example>
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-entry-component-defined}
|
||||
|
||||
## What is an `entry component`?
|
||||
|
||||
An entry component is any component that Angular loads _imperatively_ by type.
|
||||
@@ -498,7 +455,6 @@ loads the component dynamically into a `RouterOutlet`.
|
||||
|
||||
For more information, see [Entry Components](guide/entry-components).
|
||||
|
||||
<hr/>
|
||||
|
||||
## What's the difference between a _bootstrap_ component and an _entry component_?
|
||||
|
||||
@@ -514,7 +470,6 @@ although doing so is harmless.
|
||||
|
||||
For more information, see [Entry Components](guide/entry-components).
|
||||
|
||||
<hr/>
|
||||
|
||||
## When do I add components to _entryComponents_?
|
||||
|
||||
@@ -535,8 +490,6 @@ in the templates of other components.
|
||||
|
||||
For more information, see [Entry Components](guide/entry-components).
|
||||
|
||||
<hr/>
|
||||
|
||||
|
||||
## Why does Angular need _entryComponents_?
|
||||
|
||||
@@ -559,7 +512,6 @@ and every component reachable from an entry component.
|
||||
If a component isn't an _entry component_ or wasn't found in a template,
|
||||
the compiler omits it.
|
||||
|
||||
<hr/>
|
||||
|
||||
## What kinds of modules should I have and how should I use them?
|
||||
|
||||
@@ -593,7 +545,6 @@ For more information, see [Feature Modules](guide/feature-modules) and
|
||||
[Module Types](guide/module-types)
|
||||
|
||||
|
||||
|
||||
## What's the difference between NgModules and JavaScript Modules?
|
||||
|
||||
In an Angular app, NgModules and JavaScript modules work together.
|
||||
@@ -610,10 +561,8 @@ You _export_ this NgModule's classes so they can be imported and used by compone
|
||||
|
||||
For more information, see [JavaScript Modules vs. NgModules](guide/ngmodule-vs-jsmodule).
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-template-reference}
|
||||
|
||||
## How does Angular find components, directives, and pipes in a template?<br>What is a <i><b>template reference</b></i>?
|
||||
|
||||
The [Angular compiler](guide/ngmodule-faq#q-angular-compiler) looks inside component templates
|
||||
@@ -626,10 +575,8 @@ The compiler finds a pipe if the pipe's *name* appears within the pipe syntax of
|
||||
Angular only matches selectors and pipe names for classes that are declared by this module
|
||||
or exported by a module that this module imports.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-angular-compiler}
|
||||
|
||||
## What is the Angular compiler?
|
||||
|
||||
The Angular compiler converts the application code you write into highly performant JavaScript code.
|
||||
|
||||
@@ -57,8 +57,6 @@ The [Angular CLI](cli) generates the following basic `AppModule` when creating a
|
||||
|
||||
At the top are the import statements. The next section is where you configure the `@NgModule` by stating what components and directives belong to it (`declarations`) as well as which other modules it uses (`imports`). For more information on the structure of an `@NgModule`, be sure to read [Bootstrapping](guide/bootstrapping).
|
||||
|
||||
<hr />
|
||||
|
||||
## More on NgModules
|
||||
|
||||
You may also be interested in the following:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Using observables to pass values
|
||||
|
||||
Observables provide support for passing messages between parts of your application.
|
||||
They are used frequently in Angular and are the recommended technique for event handling, asynchronous programming, and handling multiple values.
|
||||
They are used frequently in Angular and are a technique for event handling, asynchronous programming, and handling multiple values.
|
||||
|
||||
The observer pattern is a software design pattern in which an object, called the *subject*, maintains a list of its dependents, called *observers*, and notifies them automatically of state changes.
|
||||
This pattern is similar (but not identical) to the [publish/subscribe](https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern) design pattern.
|
||||
@@ -11,7 +11,7 @@ The subscribed consumer then receives notifications until the function completes
|
||||
|
||||
An observable can deliver multiple values of any type—literals, messages, or events, depending on the context. The API for receiving values is the same whether the values are delivered synchronously or asynchronously. Because setup and teardown logic are both handled by the observable, your application code only needs to worry about subscribing to consume values, and when done, unsubscribing. Whether the stream was keystrokes, an HTTP response, or an interval timer, the interface for listening to values and stopping listening is the same.
|
||||
|
||||
Because of these advantages, observables are used extensively within Angular, and are recommended for app development as well.
|
||||
Because of these advantages, observables are used extensively within Angular, and for app development as well.
|
||||
|
||||
## Basic usage and terms
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user