Compare commits
231 Commits
patch_sync
...
4.4.1
Author | SHA1 | Date | |
---|---|---|---|
818f4a751e | |||
4e7d2bd5bf | |||
395ac510f7 | |||
b20c5d2c37 | |||
ea02b1ccfa | |||
0bafd03e85 | |||
e8d1858c64 | |||
d1efc5ae90 | |||
86f7b4170c | |||
9d93c859d7 | |||
5baa069b16 | |||
12b7d00747 | |||
d777d79c61 | |||
062a772e48 | |||
3618cc6d34 | |||
9413ca8a2e | |||
1302e54947 | |||
edf423af3d | |||
37086748bf | |||
6c3f1f70ba | |||
8a8c4d37aa | |||
f6a7183c52 | |||
c86e16db5f | |||
c3907893c1 | |||
d61c6f996a | |||
bd04cd61f8 | |||
96dcfafe45 | |||
991a802a8e | |||
48ae1a6574 | |||
dd2d1be006 | |||
5369de80d6 | |||
552dbfc2f1 | |||
0aa4cbdbc8 | |||
9f16c2620c | |||
9b256a9144 | |||
0f1476be33 | |||
769b2aada2 | |||
301236e1a5 | |||
aeb98dbcdf | |||
8036d05412 | |||
7d137d7f88 | |||
b8b551cf2b | |||
7ec28fe9af | |||
1cc3fe21b6 | |||
ba7d70e5e0 | |||
497e0178cc | |||
8821723526 | |||
a203a959ae | |||
dfe2bad663 | |||
f09a266e01 | |||
3853fff795 | |||
641be64544 | |||
bcf211bdb3 | |||
ee5591d583 | |||
1f43713506 | |||
325b9b4562 | |||
88abdbd50b | |||
14d34c9bdf | |||
e1f45a33b7 | |||
9a754f9f0f | |||
c3dcbf9cb3 | |||
5d68c830d2 | |||
ac58914b97 | |||
77ebd2b020 | |||
fec3b1a0e9 | |||
3b571a4f3d | |||
efee81eb57 | |||
a7a698c36f | |||
b5f1dc32d1 | |||
eef28144ce | |||
f9b290570e | |||
4852f55875 | |||
793f31b9b3 | |||
7e94405271 | |||
6076a8d7bb | |||
a1624f217c | |||
b2f4d53bf0 | |||
7662cefe6f | |||
1cb607697a | |||
1990c3c722 | |||
b589d85d6f | |||
03ec3a2169 | |||
a5baed6b97 | |||
259fc91305 | |||
a618d6e4ce | |||
b315a84ba0 | |||
972538be7a | |||
d7be4f12b5 | |||
b9c1c913c1 | |||
06e479ff66 | |||
0065868f37 | |||
77fa3c3e48 | |||
f4cb45345d | |||
9329bfb86a | |||
3efc88fb81 | |||
954b09022a | |||
71f5e78bcb | |||
f0c3ed0f14 | |||
c8fd3f5237 | |||
e0660b1b72 | |||
5a165ebcef | |||
3212f8c826 | |||
c421ccaae9 | |||
bbec7db7ba | |||
00134ae4e0 | |||
07bd459baa | |||
302adf1081 | |||
1a6a13425b | |||
072a772ca6 | |||
5f0e0a46fd | |||
c7b72aa575 | |||
732eb61957 | |||
e7e7622971 | |||
4176832266 | |||
71de92a189 | |||
e0021d4cf5 | |||
4e44102e31 | |||
111b70d108 | |||
5e4054b8f3 | |||
5afc7abcb0 | |||
65d0888708 | |||
adfd2373b8 | |||
3a82af3bde | |||
3af62306b4 | |||
afe339396f | |||
c4b51bf689 | |||
b65fe3e44e | |||
116ee334fb | |||
dbc5c5817a | |||
baf4ce0dd0 | |||
24db1ed938 | |||
82798e9d04 | |||
da8bb1b45b | |||
f5cbc2ee25 | |||
cbc1986c6f | |||
0982f993cb | |||
a5a29b0591 | |||
a8f3197f24 | |||
e6f37120fe | |||
6840b7bda9 | |||
68f458909a | |||
12acecf756 | |||
cfbed40ab6 | |||
fe1a6b8e42 | |||
13e29c4e89 | |||
fd52b178ed | |||
ca1f071b2e | |||
296adbbb72 | |||
c795ee1176 | |||
b550618afd | |||
d08d6eebff | |||
e9789abd05 | |||
f2ec2cbb99 | |||
8de2ace80a | |||
c977994864 | |||
12b8e1af55 | |||
9a188485f5 | |||
45a10419bc | |||
2245748c14 | |||
bcea196530 | |||
b9e32c833a | |||
be49e0ee93 | |||
bf95655a1a | |||
6bf5b84fa4 | |||
4836565ca7 | |||
750e4e8156 | |||
a0846194b7 | |||
bcf6b90c95 | |||
3ca2a0aa37 | |||
b4be96c65d | |||
434ff5fecb | |||
a1bb9c2d42 | |||
7e626bef0a | |||
a1e83a8ed2 | |||
cbeb197aa5 | |||
0330fa6b82 | |||
97135e8fd5 | |||
35bd07fc7b | |||
a8ac77b645 | |||
9ecd377a51 | |||
76171bd8b4 | |||
1f106d75bc | |||
a4fae8c405 | |||
33c07b3394 | |||
c9d06e676f | |||
c7c65d9fda | |||
257a9e3e6f | |||
c7c0a1688e | |||
7e95e2b0ba | |||
ddc286f4b5 | |||
3d17a3672e | |||
61d253f5fd | |||
54be25a7a1 | |||
b1757037fb | |||
f0476fcff0 | |||
a5c4bb5b96 | |||
4c1f32b0db | |||
383d8969ab | |||
333ffd8d32 | |||
d4679a0bc2 | |||
4ce29f3a5b | |||
17b7bc3e06 | |||
f19bd5f4f3 | |||
d503d25f29 | |||
5d275e994a | |||
d8c8b13bb8 | |||
4671168635 | |||
1ac78bfd5d | |||
4340beacea | |||
ec89f378fc | |||
4dd6863bc2 | |||
37c626e673 | |||
f0a110928b | |||
c39e7d1eb2 | |||
799bffb431 | |||
fda607cc2f | |||
cc3aa68123 | |||
306621d2d6 | |||
d204f7aa2a | |||
a94f5e8cbb | |||
1390afef23 | |||
b0346a6e45 | |||
e5da059994 | |||
ac92c3bb26 | |||
87157d7089 | |||
611dd12f0f | |||
969ce9dc2b | |||
34834a9e79 | |||
6e2ddccc2c | |||
55742e4737 | |||
0091b1e8db |
@ -41,8 +41,8 @@ jobs:
|
|||||||
- restore_cache:
|
- restore_cache:
|
||||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||||
|
|
||||||
- run: bazel run @nodejs//:npm install
|
- run: bazel run @build_bazel_rules_typescript_node//:bin/npm install
|
||||||
- run: bazel build packages/...
|
- run: bazel build ...
|
||||||
- save_cache:
|
- save_cache:
|
||||||
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
key: angular-{{ .Branch }}-{{ checksum "npm-shrinkwrap.json" }}
|
||||||
paths:
|
paths:
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,7 +5,6 @@ bazel-*
|
|||||||
e2e_test.*
|
e2e_test.*
|
||||||
node_modules
|
node_modules
|
||||||
bower_components
|
bower_components
|
||||||
tools/gulp-tasks/cldr/cldr-data/
|
|
||||||
|
|
||||||
# Include when developing application packages.
|
# Include when developing application packages.
|
||||||
pubspec.lock
|
pubspec.lock
|
||||||
|
@ -69,6 +69,7 @@ groups:
|
|||||||
- "*.lock"
|
- "*.lock"
|
||||||
- "tools/*"
|
- "tools/*"
|
||||||
exclude:
|
exclude:
|
||||||
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
- "tools/public_api_guard/*"
|
- "tools/public_api_guard/*"
|
||||||
- "aio/*"
|
- "aio/*"
|
||||||
users:
|
users:
|
||||||
@ -135,9 +136,8 @@ groups:
|
|||||||
compiler-cli:
|
compiler-cli:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/tsc-wrapped/*"
|
- "tools/@angular/tsc-wrapped/*"
|
||||||
- "packages/compiler-cli/*"
|
- "packages/compiler-cli/*"
|
||||||
- "packages/bazel/*"
|
|
||||||
users:
|
users:
|
||||||
- alexeagle
|
- alexeagle
|
||||||
- chuckjaz
|
- chuckjaz
|
||||||
|
208
CHANGELOG.md
208
CHANGELOG.md
@ -1,11 +1,63 @@
|
|||||||
|
<a name="4.4.0"></a>
|
||||||
|
# [4.4.0](https://github.com/angular/angular/compare/4.4.0-RC.0...4.4.0) (2017-09-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **tsc-wrapped:** deduplicate metadata for re-exported modules ([48ae1a6](https://github.com/angular/angular/commit/48ae1a6))
|
||||||
|
* **tsc-wrapped:** fix metadata symbol reference ([f6a7183](https://github.com/angular/angular/commit/f6a7183))
|
||||||
|
* **upgrade:** remove code setting id attribute. ([#19182](https://github.com/angular/angular/issues/19182)) ([b20c5d2](https://github.com/angular/angular/commit/b20c5d2)), closes [#18446](https://github.com/angular/angular/issues/18446)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="4.4.0-RC.0"></a>
|
||||||
|
# [4.4.0-RC.0](https://github.com/angular/angular/compare/4.3.6...4.4.0-RC.0) (2017-09-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** do not leak DOM nodes/styling for host triggered animations ([#18853](https://github.com/angular/angular/issues/18853)) ([1cc3fe2](https://github.com/angular/angular/commit/1cc3fe2)), closes [#18606](https://github.com/angular/angular/issues/18606)
|
||||||
|
* **common:** fix improper packaging for [@angular](https://github.com/angular)/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||||
|
* **common:** fix XSSI prefix stripping by using JSON.parse always ([#18466](https://github.com/angular/angular/issues/18466)) ([8821723](https://github.com/angular/angular/commit/8821723)), closes [#18396](https://github.com/angular/angular/issues/18396) [#18453](https://github.com/angular/angular/issues/18453)
|
||||||
|
* **compiler:** normalize the locale name ([#18963](https://github.com/angular/angular/issues/18963)) ([497e017](https://github.com/angular/angular/commit/497e017))
|
||||||
|
* **core:** complete EventEmitter in QueryList on component destroy ([#18902](https://github.com/angular/angular/issues/18902)) ([7d137d7](https://github.com/angular/angular/commit/7d137d7)), closes [#18741](https://github.com/angular/angular/issues/18741)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler:** allow multiple exportAs names ([#18723](https://github.com/angular/angular/issues/18723)) ([7ec28fe](https://github.com/angular/angular/commit/7ec28fe))
|
||||||
|
* **core:** add option to remove blank text nodes from compiled templates ([#18823](https://github.com/angular/angular/issues/18823)) ([b8b551c](https://github.com/angular/angular/commit/b8b551c))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="4.3.6"></a>
|
||||||
|
## [4.3.6](https://github.com/angular/angular/compare/4.3.5...4.3.6) (2017-08-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** ensure animations are disabled on the element containing the @.disabled flag ([#18714](https://github.com/angular/angular/issues/18714)) ([5d68c83](https://github.com/angular/angular/commit/5d68c83))
|
||||||
|
* **animations:** make sure @.disabled respects disabled parent/sub animation sequences ([#18715](https://github.com/angular/angular/issues/18715)) ([c3dcbf9](https://github.com/angular/angular/commit/c3dcbf9))
|
||||||
|
* **animations:** make sure animation cancellations respect AUTO style values ([#18787](https://github.com/angular/angular/issues/18787)) ([9a754f9](https://github.com/angular/angular/commit/9a754f9)), closes [#17450](https://github.com/angular/angular/issues/17450)
|
||||||
|
* **animations:** resolve error when using AnimationBuilder with platform-server ([#18642](https://github.com/angular/angular/issues/18642)) ([f9b2905](https://github.com/angular/angular/commit/f9b2905)), closes [#18635](https://github.com/angular/angular/issues/18635)
|
||||||
|
* **animations:** restore auto-style support for removed DOM nodes ([#18787](https://github.com/angular/angular/issues/18787)) ([e1f45a3](https://github.com/angular/angular/commit/e1f45a3))
|
||||||
|
* **core:** correct order in ContentChildren query result ([#18326](https://github.com/angular/angular/issues/18326)) ([fec3b1a](https://github.com/angular/angular/commit/fec3b1a)), closes [#16568](https://github.com/angular/angular/issues/16568)
|
||||||
|
* **core:** make sure onStable runs in the right zone ([#18706](https://github.com/angular/angular/issues/18706)) ([ee5591d](https://github.com/angular/angular/commit/ee5591d))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **animations:** allow @.disabled property to work without an expression ([#18713](https://github.com/angular/angular/issues/18713)) ([ac58914](https://github.com/angular/angular/commit/ac58914))
|
||||||
|
* **common:** add an empty DeprecatedI18NPipesModule module ([793f31b](https://github.com/angular/angular/commit/793f31b))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="5.0.0-beta.4"></a>
|
<a name="5.0.0-beta.4"></a>
|
||||||
# [5.0.0-beta.4](https://github.com/angular/angular/compare/5.0.0-beta.3...5.0.0-beta.4) (2017-08-16)
|
# [5.0.0-beta.4](https://github.com/angular/angular/compare/5.0.0-beta.3...5.0.0-beta.4) (2017-08-16)
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **aio:** fix compilation error by using the correct type for `providers` ([4d523fd](https://github.com/angular/angular/commit/4d523fd))
|
|
||||||
* **aio:** skip PWA test when redeploying non-public commit ([06faac8](https://github.com/angular/angular/commit/06faac8))
|
|
||||||
* **compiler:** Don't strip CSS source maps ([64b4be9](https://github.com/angular/angular/commit/64b4be9))
|
* **compiler:** Don't strip CSS source maps ([64b4be9](https://github.com/angular/angular/commit/64b4be9))
|
||||||
* **forms:** re-assigning options should not clear select ([32ff21c](https://github.com/angular/angular/commit/32ff21c)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
* **forms:** re-assigning options should not clear select ([32ff21c](https://github.com/angular/angular/commit/32ff21c)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
||||||
* **language-service:** remove tsickle dependency ([bc22ff1](https://github.com/angular/angular/commit/bc22ff1))
|
* **language-service:** remove tsickle dependency ([bc22ff1](https://github.com/angular/angular/commit/bc22ff1))
|
||||||
@ -20,7 +72,6 @@
|
|||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
|
||||||
* **aio:** update to new version of build-optimizer ([088532b](https://github.com/angular/angular/commit/088532b))
|
|
||||||
* **core:** add option to remove blank text nodes from compiled templates ([d2c0d98](https://github.com/angular/angular/commit/d2c0d98))
|
* **core:** add option to remove blank text nodes from compiled templates ([d2c0d98](https://github.com/angular/angular/commit/d2c0d98))
|
||||||
* **core:** Remove decorator DSL which depends on Reflect ([cac130e](https://github.com/angular/angular/commit/cac130e))
|
* **core:** Remove decorator DSL which depends on Reflect ([cac130e](https://github.com/angular/angular/commit/cac130e))
|
||||||
|
|
||||||
@ -32,17 +83,10 @@
|
|||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **aio:** skip PWA test when redeploying non-public commit ([b9c1c91](https://github.com/angular/angular/commit/b9c1c91))
|
|
||||||
* **core:** forbid destroyed views to be inserted or moved in VC ([972538b](https://github.com/angular/angular/commit/972538b)), closes [#18615](https://github.com/angular/angular/issues/18615)
|
* **core:** forbid destroyed views to be inserted or moved in VC ([972538b](https://github.com/angular/angular/commit/972538b)), closes [#18615](https://github.com/angular/angular/issues/18615)
|
||||||
* **forms:** re-assigning options should not clear select ([a1624f2](https://github.com/angular/angular/commit/a1624f2)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
* **forms:** re-assigning options should not clear select ([a1624f2](https://github.com/angular/angular/commit/a1624f2)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* **aio:** update to new version of build-optimizer ([d7be4f1](https://github.com/angular/angular/commit/d7be4f1))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="4.3.4"></a>
|
<a name="4.3.4"></a>
|
||||||
## [4.3.4](https://github.com/angular/angular/compare/4.3.3...4.3.4) (2017-08-10)
|
## [4.3.4](https://github.com/angular/angular/compare/4.3.3...4.3.4) (2017-08-10)
|
||||||
|
|
||||||
@ -54,90 +98,6 @@
|
|||||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([5e4054b](https://github.com/angular/angular/commit/5e4054b))
|
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([5e4054b](https://github.com/angular/angular/commit/5e4054b))
|
||||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([e7e7622](https://github.com/angular/angular/commit/e7e7622)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([e7e7622](https://github.com/angular/angular/commit/e7e7622)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||||
|
|
||||||
|
|
||||||
<a name="5.0.0-beta.3"></a>
|
|
||||||
# [5.0.0-beta.3](https://github.com/angular/angular/compare/5.0.0-beta.2...5.0.0-beta.3) (2017-08-09)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([c0c03dc](https://github.com/angular/angular/commit/c0c03dc))
|
|
||||||
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([05472cb](https://github.com/angular/angular/commit/05472cb)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
|
|
||||||
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([e25b3dd](https://github.com/angular/angular/commit/e25b3dd))
|
|
||||||
* **common:** don't recreate view when context shape doesn't change ([#18277](https://github.com/angular/angular/issues/18277)) ([685cc26](https://github.com/angular/angular/commit/685cc26)), closes [#13407](https://github.com/angular/angular/issues/13407)
|
|
||||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([7c47b62](https://github.com/angular/angular/commit/7c47b62))
|
|
||||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([1dca575](https://github.com/angular/angular/commit/1dca575)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
|
||||||
* **compiler-cli:** disable buggy expression lowering ([#18513](https://github.com/angular/angular/issues/18513)) ([ca695e0](https://github.com/angular/angular/commit/ca695e0))
|
|
||||||
* **compiler-cli:** fix and re-enable expression lowering ([#18570](https://github.com/angular/angular/issues/18570)) ([6f2038c](https://github.com/angular/angular/commit/6f2038c)), closes [#18388](https://github.com/angular/angular/issues/18388)
|
|
||||||
* **compiler-cli:** modified ngc to throw all errors, not just syntax ([#18388](https://github.com/angular/angular/issues/18388)) ([5651e4a](https://github.com/angular/angular/commit/5651e4a))
|
|
||||||
* **compiler-cli:** remove minimist dependency of compiler-cli/index ([#18532](https://github.com/angular/angular/issues/18532)) ([5b7432b](https://github.com/angular/angular/commit/5b7432b))
|
|
||||||
* **core:** fix platform-browser-dynamic ([#18576](https://github.com/angular/angular/issues/18576)) ([f0a5501](https://github.com/angular/angular/commit/f0a5501))
|
|
||||||
* **core:** forbid destroyed views to be inserted or moved in VC ([#18568](https://github.com/angular/angular/issues/18568)) ([e54bd59](https://github.com/angular/angular/commit/e54bd59)), closes [#17780](https://github.com/angular/angular/issues/17780)
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **core:** Create StaticInjector which does not depend on Reflect polyfill. ([d9d00bd](https://github.com/angular/angular/commit/d9d00bd))
|
|
||||||
* **forms:** add default updateOn values for groups and arrays ([#18536](https://github.com/angular/angular/issues/18536)) ([ff5c58b](https://github.com/angular/angular/commit/ff5c58b))
|
|
||||||
* **forms:** add updateOn blur option to FormControls ([#18408](https://github.com/angular/angular/issues/18408)) ([333a708](https://github.com/angular/angular/commit/333a708)), closes [#7113](https://github.com/angular/angular/issues/7113)
|
|
||||||
* **forms:** add updateOn submit option to FormControls ([#18514](https://github.com/angular/angular/issues/18514)) ([f69561b](https://github.com/angular/angular/commit/f69561b))
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* switch angular to use StaticInjector instead of ReflectiveInjector ([fcadbf4](https://github.com/angular/angular/commit/fcadbf4)), closes [#18496](https://github.com/angular/angular/issues/18496)
|
|
||||||
|
|
||||||
|
|
||||||
### BREAKING CHANGES
|
|
||||||
|
|
||||||
* `platformXXXX()` no longer accepts providers which depend on reflection.
|
|
||||||
Specifically the method signature when from `Provider[]` to
|
|
||||||
`StaticProvider[]`.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
Before:
|
|
||||||
```
|
|
||||||
[
|
|
||||||
MyClass,
|
|
||||||
{provide: ClassA, useClass: SubClassA}
|
|
||||||
]
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
After:
|
|
||||||
```
|
|
||||||
[
|
|
||||||
{provide: MyClass, deps: [Dep1,...]},
|
|
||||||
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
NOTE: This only applies to platform creation and providers for the JIT
|
|
||||||
compiler. It does not apply to `@Component` or `@NgModule` provides
|
|
||||||
declarations.
|
|
||||||
|
|
||||||
Benchpress note: Previously Benchpress also supported reflective
|
|
||||||
provides, which now require static providers.
|
|
||||||
|
|
||||||
DEPRECATION:
|
|
||||||
|
|
||||||
- `ReflectiveInjector` is now deprecated as it will be remove. Use
|
|
||||||
`Injector.create` as a replacement.
|
|
||||||
|
|
||||||
<a name="5.0.0-beta.2"></a>
|
|
||||||
# [5.0.0-beta.2](https://github.com/angular/angular/compare/5.0.0-beta.1...5.0.0-beta.2) (2017-08-02)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **compiler:** do not consider arguments when determining recursion ([e64b54b](https://github.com/angular/angular/commit/e64b54b))
|
|
||||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([381471d](https://github.com/angular/angular/commit/381471d)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **forms:** add options arg to abstract controls ([ebef5e6](https://github.com/angular/angular/commit/ebef5e6))
|
|
||||||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
|
||||||
|
|
||||||
|
|
||||||
<a name="4.3.3"></a>
|
<a name="4.3.3"></a>
|
||||||
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
|
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
|
||||||
|
|
||||||
@ -145,36 +105,6 @@ DEPRECATION:
|
|||||||
|
|
||||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
||||||
|
|
||||||
|
|
||||||
<a name="5.0.0-beta.1"></a>
|
|
||||||
# [5.0.0-beta.1](https://github.com/angular/angular/compare/5.0.0-beta.0...5.0.0-beta.1) (2017-07-27)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** export BrowserModule as apart of BrowserAnimationsModule ([#18263](https://github.com/angular/angular/issues/18263)) ([fd0cc01](https://github.com/angular/angular/commit/fd0cc01))
|
|
||||||
* **compiler:** add equiv & disp attributes to Xliff2 ICU placeholders ([#18283](https://github.com/angular/angular/issues/18283)) ([38ec05f](https://github.com/angular/angular/commit/38ec05f)), closes [#17344](https://github.com/angular/angular/issues/17344)
|
|
||||||
* **compiler:** allow numbers for ICU message cases in lexer ([#18095](https://github.com/angular/angular/issues/18095)) ([a3a5429](https://github.com/angular/angular/commit/a3a5429)), closes [#17799](https://github.com/angular/angular/issues/17799)
|
|
||||||
* **core:** invoke error handler outside of the Angular Zone ([#18269](https://github.com/angular/angular/issues/18269)) ([7ae7573](https://github.com/angular/angular/commit/7ae7573)), closes [#17073](https://github.com/angular/angular/issues/17073) [#7774](https://github.com/angular/angular/issues/7774)
|
|
||||||
* **platform-server:** don't clobber parse5 properties when setting ([#18237](https://github.com/angular/angular/issues/18237)) ([a094769](https://github.com/angular/angular/commit/a094769)), closes [#17050](https://github.com/angular/angular/issues/17050)
|
|
||||||
* **router:** child CanActivate guard should wait for parent to complete ([#18110](https://github.com/angular/angular/issues/18110)) ([086f4aa](https://github.com/angular/angular/commit/086f4aa)), closes [#15670](https://github.com/angular/angular/issues/15670)
|
|
||||||
* **router:** should throw when lazy loaded module doesn't define any routes ([#15001](https://github.com/angular/angular/issues/15001)) ([82923a3](https://github.com/angular/angular/commit/82923a3)), closes [#14596](https://github.com/angular/angular/issues/14596)
|
|
||||||
* **upgrade:** ensure downgraded components are created in the Angular zone ([#18209](https://github.com/angular/angular/issues/18209)) ([43c33d5](https://github.com/angular/angular/commit/43c33d5))
|
|
||||||
* **upgrade:** throw error if trying to get injector before setting ([#18209](https://github.com/angular/angular/issues/18209)) ([d31dc7b](https://github.com/angular/angular/commit/d31dc7b))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **compiler:** add representation of placeholders to xliff & xmb ([b3085e9](https://github.com/angular/angular/commit/b3085e9)), closes [#17345](https://github.com/angular/angular/issues/17345)
|
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
|
||||||
|
|
||||||
* latest tsickle to tree shake: abstract class methods & interfaces ([#18236](https://github.com/angular/angular/issues/18236)) ([b7a6f52](https://github.com/angular/angular/commit/b7a6f52))
|
|
||||||
* **core:** use native addEventListener for faster rendering. ([#18107](https://github.com/angular/angular/issues/18107)) ([6279e50](https://github.com/angular/angular/commit/6279e50))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="4.3.2"></a>
|
<a name="4.3.2"></a>
|
||||||
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
|
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
|
||||||
|
|
||||||
@ -192,35 +122,6 @@ DEPRECATION:
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="5.0.0-beta.0"></a>
|
|
||||||
# [5.0.0-beta.0](https://github.com/angular/angular/compare/4.3.0...5.0.0-beta.0) (2017-07-19)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** always camelcase style property names that contain auto styles ([d22f8f5](https://github.com/angular/angular/commit/d22f8f5)), closes [#17938](https://github.com/angular/angular/issues/17938)
|
|
||||||
* **animations:** capture cancelled animation styles within grouped animations ([23146c9](https://github.com/angular/angular/commit/23146c9)), closes [#17170](https://github.com/angular/angular/issues/17170)
|
|
||||||
* **animations:** do not crash animations if a nested component fires CD during CD ([5db6f38](https://github.com/angular/angular/commit/5db6f38)), closes [#18193](https://github.com/angular/angular/issues/18193)
|
|
||||||
* **animations:** make sure @.disabled works in non-animation components ([5344be5](https://github.com/angular/angular/commit/5344be5))
|
|
||||||
* **common:** send flushed body as error instead of null ([5c62e30](https://github.com/angular/angular/commit/5c62e30)), closes [#18181](https://github.com/angular/angular/issues/18181)
|
|
||||||
* **compiler:** ensure jit external id arguments names are unique ([95635c1](https://github.com/angular/angular/commit/95635c1))
|
|
||||||
* **compiler-cli:** don't generate empty <target/> when extracting xliff ([65c9e13](https://github.com/angular/angular/commit/65c9e13)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
|
||||||
* **platform-server:** provide XhrFactory for HttpClient ([8076482](https://github.com/angular/angular/commit/8076482))
|
|
||||||
* **router:** canDeactivate guards should run from bottom to top ([e20cfe1](https://github.com/angular/angular/commit/e20cfe1)), closes [#15657](https://github.com/angular/angular/issues/15657)
|
|
||||||
* **router:** should navigate to the same url when config changes ([eb6fb5f](https://github.com/angular/angular/commit/eb6fb5f)), closes [#15535](https://github.com/angular/angular/issues/15535)
|
|
||||||
* **router:** should run resolvers for the same route concurrently ([ad3029e](https://github.com/angular/angular/commit/ad3029e)), closes [#14279](https://github.com/angular/angular/issues/14279)
|
|
||||||
* **router:** terminal route in custom matcher ([b399cb2](https://github.com/angular/angular/commit/b399cb2))
|
|
||||||
* **upgrade:** allow accessing AngularJS injector from downgraded module ([a5205c6](https://github.com/angular/angular/commit/a5205c6))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **animations:** support :increment and :decrement transition aliases ([6f45519](https://github.com/angular/angular/commit/6f45519))
|
|
||||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
|
||||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="4.3.1"></a>
|
<a name="4.3.1"></a>
|
||||||
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)
|
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)
|
||||||
|
|
||||||
@ -1292,7 +1193,6 @@ templates is unaffected. We expect no or little impact on apps from this change,
|
|||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **aio:** add initial angular-cli scaffold ([#14118](https://github.com/angular/angular/issues/14118)) ([e130bc1](https://github.com/angular/angular/commit/e130bc1))
|
|
||||||
* **common:** rename underlying `NgFor` class and add a type parameter ([#14104](https://github.com/angular/angular/issues/14104)) ([86b2b25](https://github.com/angular/angular/commit/86b2b25))
|
* **common:** rename underlying `NgFor` class and add a type parameter ([#14104](https://github.com/angular/angular/issues/14104)) ([86b2b25](https://github.com/angular/angular/commit/86b2b25))
|
||||||
* **compiler:** allow missing translations ([#14113](https://github.com/angular/angular/issues/14113)) ([8775ab9](https://github.com/angular/angular/commit/8775ab9)), closes [#13861](https://github.com/angular/angular/issues/13861)
|
* **compiler:** allow missing translations ([#14113](https://github.com/angular/angular/issues/14113)) ([8775ab9](https://github.com/angular/angular/commit/8775ab9)), closes [#13861](https://github.com/angular/angular/issues/13861)
|
||||||
* **compiler:** do not parse xtb messages not needed by angular ([#14111](https://github.com/angular/angular/issues/14111)) ([f7fba74](https://github.com/angular/angular/commit/f7fba74)), closes [#14046](https://github.com/angular/angular/issues/14046)
|
* **compiler:** do not parse xtb messages not needed by angular ([#14111](https://github.com/angular/angular/issues/14111)) ([f7fba74](https://github.com/angular/angular/commit/f7fba74)), closes [#14046](https://github.com/angular/angular/issues/14046)
|
||||||
|
24
WORKSPACE
24
WORKSPACE
@ -1,21 +1,17 @@
|
|||||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||||
|
|
||||||
git_repository(
|
git_repository(
|
||||||
name = "build_bazel_rules_nodejs",
|
|
||||||
remote = "https://github.com/bazelbuild/rules_nodejs.git",
|
|
||||||
tag = "0.0.2",
|
|
||||||
)
|
|
||||||
|
|
||||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
|
||||||
|
|
||||||
node_repositories(package_json = ["//:package.json"])
|
|
||||||
|
|
||||||
local_repository(
|
|
||||||
name = "build_bazel_rules_typescript",
|
name = "build_bazel_rules_typescript",
|
||||||
path = "node_modules/@bazel/typescript",
|
remote = "https://github.com/bazelbuild/rules_typescript.git",
|
||||||
|
tag = "0.0.5",
|
||||||
)
|
)
|
||||||
|
|
||||||
local_repository(
|
load("@build_bazel_rules_typescript//:defs.bzl", "node_repositories")
|
||||||
name = "angular",
|
|
||||||
path = "packages/bazel",
|
node_repositories(package_json = "//:package.json")
|
||||||
|
|
||||||
|
git_repository(
|
||||||
|
name = "build_bazel_rules_angular",
|
||||||
|
remote = "https://github.com/bazelbuild/rules_angular.git",
|
||||||
|
tag = "0.0.1",
|
||||||
)
|
)
|
@ -1,6 +0,0 @@
|
|||||||
// #docregion import-locale
|
|
||||||
import { registerLocaleData } from '@angular/common';
|
|
||||||
import localeFr from '@angular/common/i18n_data/locale_fr';
|
|
||||||
|
|
||||||
registerLocaleData(localeFr);
|
|
||||||
// #enddocregion import-locale
|
|
@ -1,7 +0,0 @@
|
|||||||
// #docregion import-locale-extra
|
|
||||||
import { registerLocaleData } from '@angular/common';
|
|
||||||
import localeEnGB from '@angular/common/i18n_data/locale_en-GB';
|
|
||||||
import localeEnGBExtra from '@angular/common/i18n_data/extra/locale_en-GB';
|
|
||||||
|
|
||||||
registerLocaleData(localeEnGB, localeEnGBExtra);
|
|
||||||
// #enddocregion import-locale-extra
|
|
@ -1,15 +1,15 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion without-missing-translation
|
// #docregion without-missing-translation
|
||||||
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy, StaticProvider } from '@angular/core';
|
import { TRANSLATIONS, TRANSLATIONS_FORMAT, LOCALE_ID, MissingTranslationStrategy } from '@angular/core';
|
||||||
import { CompilerConfig } from '@angular/compiler';
|
import { CompilerConfig } from '@angular/compiler';
|
||||||
|
|
||||||
export function getTranslationProviders(): Promise<StaticProvider[]> {
|
export function getTranslationProviders(): Promise<Object[]> {
|
||||||
|
|
||||||
// Get the locale id from the global
|
// Get the locale id from the global
|
||||||
const locale = document['locale'] as string;
|
const locale = document['locale'] as string;
|
||||||
|
|
||||||
// return no providers if fail to get translation file for locale
|
// return no providers if fail to get translation file for locale
|
||||||
const noProviders: StaticProvider[] = [];
|
const noProviders: Object[] = [];
|
||||||
|
|
||||||
// No locale or U.S. English: no translation providers
|
// No locale or U.S. English: no translation providers
|
||||||
if (!locale || locale === 'en-US') {
|
if (!locale || locale === 'en-US') {
|
||||||
|
@ -28,7 +28,7 @@ describe('Pipes', function () {
|
|||||||
|
|
||||||
it('should be able to toggle birthday formats', function () {
|
it('should be able to toggle birthday formats', function () {
|
||||||
let birthDayEle = element(by.css('hero-birthday2 > p'));
|
let birthDayEle = element(by.css('hero-birthday2 > p'));
|
||||||
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/88`);
|
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/1988`);
|
||||||
let buttonEle = element(by.cssContainingText('hero-birthday2 > button', 'Toggle Format'));
|
let buttonEle = element(by.cssContainingText('hero-birthday2 > button', 'Toggle Format'));
|
||||||
expect(buttonEle.isDisplayed()).toBe(true);
|
expect(buttonEle.isDisplayed()).toBe(true);
|
||||||
buttonEle.click().then(function() {
|
buttonEle.click().then(function() {
|
||||||
|
@ -12,13 +12,13 @@ describe('Router', () => {
|
|||||||
beforeAll(() => browser.get(''));
|
beforeAll(() => browser.get(''));
|
||||||
|
|
||||||
function getPageStruct() {
|
function getPageStruct() {
|
||||||
const hrefEles = element.all(by.css('my-app a'));
|
const hrefEles = element.all(by.css('my-app > nav a'));
|
||||||
const crisisDetail = element.all(by.css('my-app > ng-component > ng-component > ng-component > div')).first();
|
const crisisDetail = element.all(by.css('my-app > ng-component > ng-component > ng-component > div')).first();
|
||||||
const heroDetail = element(by.css('my-app > ng-component > div'));
|
const heroDetail = element(by.css('my-app > ng-component > div'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hrefs: hrefEles,
|
hrefs: hrefEles,
|
||||||
activeHref: element(by.css('my-app a.active')),
|
activeHref: element(by.css('my-app > nav a.active')),
|
||||||
|
|
||||||
crisisHref: hrefEles.get(0),
|
crisisHref: hrefEles.get(0),
|
||||||
crisisList: element.all(by.css('my-app > ng-component > ng-component li')),
|
crisisList: element.all(by.css('my-app > ng-component > ng-component li')),
|
||||||
|
@ -15,6 +15,10 @@
|
|||||||
height: 1.6em;
|
height: 1.6em;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
.items li a {
|
||||||
|
display: block;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
.items li:hover {
|
.items li:hover {
|
||||||
color: #607D8B;
|
color: #607D8B;
|
||||||
background-color: #DDD;
|
background-color: #DDD;
|
||||||
|
@ -28,7 +28,7 @@ const appRoutes: Routes = [
|
|||||||
data: { preload: true }
|
data: { preload: true }
|
||||||
},
|
},
|
||||||
// #enddocregion preload-v2
|
// #enddocregion preload-v2
|
||||||
{ path: '', redirectTo: '/heroes', pathMatch: 'full' },
|
{ path: '', redirectTo: '/superheroes', pathMatch: 'full' },
|
||||||
{ path: '**', component: PageNotFoundComponent }
|
{ path: '**', component: PageNotFoundComponent }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
23
aio/content/examples/router/src/app/app.component.6.ts
Normal file
23
aio/content/examples/router/src/app/app.component.6.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
// #docregion template
|
||||||
|
template: `
|
||||||
|
<h1 class="title">Angular Router</h1>
|
||||||
|
<nav>
|
||||||
|
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
|
||||||
|
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
|
||||||
|
<a routerLink="/admin" routerLinkActive="active">Admin</a>
|
||||||
|
<a routerLink="/login" routerLinkActive="active">Login</a>
|
||||||
|
<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
|
||||||
|
</nav>
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
<router-outlet name="popup"></router-outlet>
|
||||||
|
`
|
||||||
|
// #enddocregion template
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
}
|
@ -9,7 +9,7 @@ import { Component } from '@angular/core';
|
|||||||
<h1 class="title">Angular Router</h1>
|
<h1 class="title">Angular Router</h1>
|
||||||
<nav>
|
<nav>
|
||||||
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
|
<a routerLink="/crisis-center" routerLinkActive="active">Crisis Center</a>
|
||||||
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
|
<a routerLink="/superheroes" routerLinkActive="active">Heroes</a>
|
||||||
<a routerLink="/admin" routerLinkActive="active">Admin</a>
|
<a routerLink="/admin" routerLinkActive="active">Admin</a>
|
||||||
<a routerLink="/login" routerLinkActive="active">Login</a>
|
<a routerLink="/login" routerLinkActive="active">Login</a>
|
||||||
<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
|
<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { CanDeactivate,
|
import { CanDeactivate,
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
RouterStateSnapshot } from '@angular/router';
|
RouterStateSnapshot } from '@angular/router';
|
||||||
@ -13,7 +14,7 @@ export class CanDeactivateGuard implements CanDeactivate<CrisisDetailComponent>
|
|||||||
component: CrisisDetailComponent,
|
component: CrisisDetailComponent,
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot
|
state: RouterStateSnapshot
|
||||||
): Promise<boolean> | boolean {
|
): Observable<boolean> | boolean {
|
||||||
// Get the Crisis Center ID
|
// Get the Crisis Center ID
|
||||||
console.log(route.paramMap.get('id'));
|
console.log(route.paramMap.get('id'));
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ export class CanDeactivateGuard implements CanDeactivate<CrisisDetailComponent>
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Otherwise ask the user with the dialog service and return its
|
// Otherwise ask the user with the dialog service and return its
|
||||||
// promise which resolves to true or false when the user decides
|
// observable which resolves to true or false when the user decides
|
||||||
return component.dialogService.confirm('Discard changes?');
|
return component.dialogService.confirm('Discard changes?');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import 'rxjs/add/operator/take';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Router, Resolve, RouterStateSnapshot,
|
import { Router, Resolve, RouterStateSnapshot,
|
||||||
ActivatedRouteSnapshot } from '@angular/router';
|
ActivatedRouteSnapshot } from '@angular/router';
|
||||||
|
|
||||||
@ -9,10 +12,10 @@ import { Crisis, CrisisService } from './crisis.service';
|
|||||||
export class CrisisDetailResolver implements Resolve<Crisis> {
|
export class CrisisDetailResolver implements Resolve<Crisis> {
|
||||||
constructor(private cs: CrisisService, private router: Router) {}
|
constructor(private cs: CrisisService, private router: Router) {}
|
||||||
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<Crisis> {
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Crisis> {
|
||||||
let id = route.paramMap.get('id');
|
let id = route.paramMap.get('id');
|
||||||
|
|
||||||
return this.cs.getCrisis(id).then(crisis => {
|
return this.cs.getCrisis(id).take(1).map(crisis => {
|
||||||
if (crisis) {
|
if (crisis) {
|
||||||
return crisis;
|
return crisis;
|
||||||
} else { // id not found
|
} else { // id not found
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
import { Component, OnInit, HostBinding } from '@angular/core';
|
import { Component, OnInit, HostBinding } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
|
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import { slideInDownAnimation } from '../animations';
|
import { slideInDownAnimation } from '../animations';
|
||||||
import { Crisis, CrisisService } from './crisis.service';
|
import { Crisis, CrisisService } from './crisis.service';
|
||||||
@ -45,7 +46,8 @@ export class CrisisDetailComponent implements OnInit {
|
|||||||
// #docregion ngOnInit
|
// #docregion ngOnInit
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.route.paramMap
|
this.route.paramMap
|
||||||
.switchMap((params: ParamMap) => this.service.getCrisis(params.get('id')))
|
.switchMap((params: ParamMap) =>
|
||||||
|
this.service.getCrisis(params.get('id')))
|
||||||
.subscribe((crisis: Crisis) => {
|
.subscribe((crisis: Crisis) => {
|
||||||
if (crisis) {
|
if (crisis) {
|
||||||
this.editName = crisis.name;
|
this.editName = crisis.name;
|
||||||
@ -66,13 +68,13 @@ export class CrisisDetailComponent implements OnInit {
|
|||||||
this.gotoCrises();
|
this.gotoCrises();
|
||||||
}
|
}
|
||||||
|
|
||||||
canDeactivate(): Promise<boolean> | boolean {
|
canDeactivate(): Observable<boolean> | boolean {
|
||||||
// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
|
// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
|
||||||
if (!this.crisis || this.crisis.name === this.editName) {
|
if (!this.crisis || this.crisis.name === this.editName) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Otherwise ask the user with the dialog service and return its
|
// Otherwise ask the user with the dialog service and return its
|
||||||
// promise which resolves to true or false when the user decides
|
// observable which resolves to true or false when the user decides
|
||||||
return this.dialogService.confirm('Discard changes?');
|
return this.dialogService.confirm('Discard changes?');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnInit, HostBinding } from '@angular/core';
|
import { Component, OnInit, HostBinding } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import { slideInDownAnimation } from '../animations';
|
import { slideInDownAnimation } from '../animations';
|
||||||
import { Crisis } from './crisis.service';
|
import { Crisis } from './crisis.service';
|
||||||
@ -62,13 +63,13 @@ export class CrisisDetailComponent implements OnInit {
|
|||||||
// #enddocregion cancel-save
|
// #enddocregion cancel-save
|
||||||
|
|
||||||
// #docregion canDeactivate
|
// #docregion canDeactivate
|
||||||
canDeactivate(): Promise<boolean> | boolean {
|
canDeactivate(): Observable<boolean> | boolean {
|
||||||
// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
|
// Allow synchronous navigation (`true`) if no crisis or the crisis is unchanged
|
||||||
if (!this.crisis || this.crisis.name === this.editName) {
|
if (!this.crisis || this.crisis.name === this.editName) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Otherwise ask the user with the dialog service and return its
|
// Otherwise ask the user with the dialog service and return its
|
||||||
// promise which resolves to true or false when the user decides
|
// observable which resolves to true or false when the user decides
|
||||||
return this.dialogService.confirm('Discard changes?');
|
return this.dialogService.confirm('Discard changes?');
|
||||||
}
|
}
|
||||||
// #enddocregion canDeactivate
|
// #enddocregion canDeactivate
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import 'rxjs/add/operator/do';
|
|
||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
|
||||||
import { Crisis, CrisisService } from './crisis.service';
|
import { Crisis, CrisisService } from './crisis.service';
|
||||||
import { Observable } from 'rxjs/Observable';
|
import { Observable } from 'rxjs/Observable';
|
||||||
@ -10,35 +10,34 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
// #docregion relative-navigation-router-link
|
// #docregion relative-navigation-router-link
|
||||||
template: `
|
template: `
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
<li *ngFor="let crisis of crises | async">
|
<li *ngFor="let crisis of crises$ | async"
|
||||||
<a [routerLink]="[crisis.id]"
|
[class.selected]="crisis.id === selectedId">
|
||||||
[class.selected]="isSelected(crisis)">
|
<a [routerLink]="[crisis.id]">
|
||||||
<span class="badge">{{ crisis.id }}</span>
|
<span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
|
||||||
{{ crisis.name }}
|
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>`
|
</ul>
|
||||||
|
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
`
|
||||||
// #enddocregion relative-navigation-router-link
|
// #enddocregion relative-navigation-router-link
|
||||||
})
|
})
|
||||||
export class CrisisListComponent implements OnInit {
|
export class CrisisListComponent implements OnInit {
|
||||||
crises: Observable<Crisis[]>;
|
crises$: Observable<Crisis[]>;
|
||||||
selectedId: number;
|
selectedId: number;
|
||||||
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private service: CrisisService,
|
private service: CrisisService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute
|
||||||
private router: Router
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.crises = this.route.paramMap
|
this.crises$ = this.route.paramMap
|
||||||
.switchMap((params: ParamMap) => {
|
.switchMap((params: ParamMap) => {
|
||||||
this.selectedId = +params.get('id');
|
this.selectedId = +params.get('id');
|
||||||
return this.service.getCrises();
|
return this.service.getCrises();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
isSelected(crisis: Crisis) {
|
|
||||||
return crisis.id === this.selectedId;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
|
||||||
import { Observable } from 'rxjs/Observable';
|
|
||||||
|
|
||||||
import { Crisis, CrisisService } from './crisis.service';
|
import { Crisis, CrisisService } from './crisis.service';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
<li *ngFor="let crisis of crises | async"
|
<li *ngFor="let crisis of crises$ | async"
|
||||||
(click)="onSelect(crisis)"
|
[class.selected]="crisis.id === selectedId">
|
||||||
[class.selected]="isSelected(crisis)">
|
<a [routerLink]="[crisis.id]">
|
||||||
<span class="badge">{{ crisis.id }}</span>
|
<span class="badge">{{ crisis.id }}</span>{{ crisis.name }}
|
||||||
{{ crisis.name }}
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -22,35 +21,21 @@ import { Crisis, CrisisService } from './crisis.service';
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class CrisisListComponent implements OnInit {
|
export class CrisisListComponent implements OnInit {
|
||||||
crises: Observable<Crisis[]>;
|
crises$: Observable<Crisis[]>;
|
||||||
selectedId: number;
|
selectedId: number;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(
|
constructor(
|
||||||
private service: CrisisService,
|
private service: CrisisService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute
|
||||||
private router: Router
|
|
||||||
) {}
|
) {}
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
isSelected(crisis: Crisis) {
|
|
||||||
return crisis.id === this.selectedId;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.crises = this.route.paramMap
|
this.crises$ = this.route.paramMap
|
||||||
.switchMap((params: ParamMap) => {
|
.switchMap((params: ParamMap) => {
|
||||||
this.selectedId = +params.get('id');
|
this.selectedId = +params.get('id');
|
||||||
return this.service.getCrises();
|
return this.service.getCrises();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion onSelect
|
|
||||||
onSelect(crisis: Crisis) {
|
|
||||||
this.selectedId = crisis.id;
|
|
||||||
|
|
||||||
// Navigate with relative link
|
|
||||||
this.router.navigate([crisis.id], { relativeTo: this.route });
|
|
||||||
}
|
|
||||||
// #enddocregion onSelect
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion , mock-crises
|
// #docregion , mock-crises
|
||||||
|
import 'rxjs/add/observable/of';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
|
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||||
|
|
||||||
export class Crisis {
|
export class Crisis {
|
||||||
constructor(public id: number, public name: string) { }
|
constructor(public id: number, public name: string) { }
|
||||||
}
|
}
|
||||||
@ -12,20 +16,18 @@ const CRISES = [
|
|||||||
];
|
];
|
||||||
// #enddocregion mock-crises
|
// #enddocregion mock-crises
|
||||||
|
|
||||||
let crisesPromise = Promise.resolve(CRISES);
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CrisisService {
|
export class CrisisService {
|
||||||
|
|
||||||
static nextCrisisId = 100;
|
static nextCrisisId = 100;
|
||||||
|
private crises$: BehaviorSubject<Crisis[]> = new BehaviorSubject<Crisis[]>(CRISES);
|
||||||
|
|
||||||
getCrises() { return crisesPromise; }
|
getCrises() { return this.crises$; }
|
||||||
|
|
||||||
getCrisis(id: number | string) {
|
getCrisis(id: number | string) {
|
||||||
return crisesPromise
|
return this.getCrises()
|
||||||
.then(crises => crises.find(crisis => crisis.id === +id));
|
.map(crises => crises.find(crisis => crisis.id === +id));
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
@ -33,7 +35,8 @@ export class CrisisService {
|
|||||||
name = name.trim();
|
name = name.trim();
|
||||||
if (name) {
|
if (name) {
|
||||||
let crisis = new Crisis(CrisisService.nextCrisisId++, name);
|
let crisis = new Crisis(CrisisService.nextCrisisId++, name);
|
||||||
crisesPromise.then(crises => crises.push(crisis));
|
CRISES.push(crisis);
|
||||||
|
this.crises$.next(CRISES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #docregion
|
// #docregion
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
|
import 'rxjs/add/observable/of';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Async modal dialog service
|
* Async modal dialog service
|
||||||
* DialogService makes this app easier to test by faking this service.
|
* DialogService makes this app easier to test by faking this service.
|
||||||
@ -9,11 +12,11 @@ import { Injectable } from '@angular/core';
|
|||||||
export class DialogService {
|
export class DialogService {
|
||||||
/**
|
/**
|
||||||
* Ask user to confirm an action. `message` explains the action and choices.
|
* Ask user to confirm an action. `message` explains the action and choices.
|
||||||
* Returns promise resolving to `true`=confirm or `false`=cancel
|
* Returns observable resolving to `true`=confirm or `false`=cancel
|
||||||
*/
|
*/
|
||||||
confirm(message?: string) {
|
confirm(message?: string): Observable<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
const confirmation = window.confirm(message || 'Is it OK?');
|
||||||
return resolve(window.confirm(message || 'Is it OK?'));
|
|
||||||
});
|
return Observable.of(confirmation);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
// #enddocregion rxjs-operator-import
|
// #enddocregion rxjs-operator-import
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
// #docregion imports
|
// #docregion imports
|
||||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
// #enddocregion imports
|
// #enddocregion imports
|
||||||
@ -13,7 +14,7 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<h2>HEROES</h2>
|
<h2>HEROES</h2>
|
||||||
<div *ngIf="hero">
|
<div *ngIf="hero$ | async as hero">
|
||||||
<h3>"{{ hero.name }}"</h3>
|
<h3>"{{ hero.name }}"</h3>
|
||||||
<div>
|
<div>
|
||||||
<label>Id: </label>{{ hero.id }}</div>
|
<label>Id: </label>{{ hero.id }}</div>
|
||||||
@ -28,7 +29,7 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class HeroDetailComponent implements OnInit {
|
export class HeroDetailComponent implements OnInit {
|
||||||
hero: Hero;
|
hero$: Observable<Hero>;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(
|
constructor(
|
||||||
@ -40,10 +41,9 @@ export class HeroDetailComponent implements OnInit {
|
|||||||
|
|
||||||
// #docregion ngOnInit
|
// #docregion ngOnInit
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.route.paramMap
|
this.hero$ = this.route.paramMap
|
||||||
.switchMap((params: ParamMap) =>
|
.switchMap((params: ParamMap) =>
|
||||||
this.service.getHero(params.get('id')))
|
this.service.getHero(params.get('id')));
|
||||||
.subscribe((hero: Hero) => this.hero = hero);
|
|
||||||
}
|
}
|
||||||
// #enddocregion ngOnInit
|
// #enddocregion ngOnInit
|
||||||
|
|
||||||
|
@ -2,13 +2,14 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import { Hero, HeroService } from './hero.service';
|
import { Hero, HeroService } from './hero.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<h2>HEROES</h2>
|
<h2>HEROES</h2>
|
||||||
<div *ngIf="hero">
|
<div *ngIf="hero$ | async as hero">
|
||||||
<h3>"{{ hero.name }}"</h3>
|
<h3>"{{ hero.name }}"</h3>
|
||||||
<div>
|
<div>
|
||||||
<label>Id: </label>{{ hero.id }}</div>
|
<label>Id: </label>{{ hero.id }}</div>
|
||||||
@ -23,7 +24,7 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
`
|
`
|
||||||
})
|
})
|
||||||
export class HeroDetailComponent implements OnInit {
|
export class HeroDetailComponent implements OnInit {
|
||||||
hero: Hero;
|
hero$: Observable<Hero>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@ -35,8 +36,7 @@ export class HeroDetailComponent implements OnInit {
|
|||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
let id = this.route.snapshot.paramMap.get('id');
|
let id = this.route.snapshot.paramMap.get('id');
|
||||||
|
|
||||||
this.service.getHero(id)
|
this.hero$ = this.service.getHero(id);
|
||||||
.then((hero: Hero) => this.hero = hero);
|
|
||||||
}
|
}
|
||||||
// #enddocregion snapshot
|
// #enddocregion snapshot
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
import 'rxjs/add/operator/switchMap';
|
import 'rxjs/add/operator/switchMap';
|
||||||
// #enddocregion rxjs-operator-import
|
// #enddocregion rxjs-operator-import
|
||||||
import { Component, OnInit, HostBinding } from '@angular/core';
|
import { Component, OnInit, HostBinding } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
|
|
||||||
import { slideInDownAnimation } from '../animations';
|
import { slideInDownAnimation } from '../animations';
|
||||||
@ -13,7 +14,7 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
@Component({
|
@Component({
|
||||||
template: `
|
template: `
|
||||||
<h2>HEROES</h2>
|
<h2>HEROES</h2>
|
||||||
<div *ngIf="hero">
|
<div *ngIf="hero$ | async as hero">
|
||||||
<h3>"{{ hero.name }}"</h3>
|
<h3>"{{ hero.name }}"</h3>
|
||||||
<div>
|
<div>
|
||||||
<label>Id: </label>{{ hero.id }}</div>
|
<label>Id: </label>{{ hero.id }}</div>
|
||||||
@ -22,7 +23,7 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||||
</div>
|
</div>
|
||||||
<p>
|
<p>
|
||||||
<button (click)="gotoHeroes()">Back</button>
|
<button (click)="gotoHeroes(hero)">Back</button>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
@ -35,7 +36,7 @@ export class HeroDetailComponent implements OnInit {
|
|||||||
@HostBinding('style.position') position = 'absolute';
|
@HostBinding('style.position') position = 'absolute';
|
||||||
// #enddocregion host-bindings
|
// #enddocregion host-bindings
|
||||||
|
|
||||||
hero: Hero;
|
hero$: Observable<Hero>;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(
|
constructor(
|
||||||
@ -47,16 +48,15 @@ export class HeroDetailComponent implements OnInit {
|
|||||||
|
|
||||||
// #docregion ngOnInit
|
// #docregion ngOnInit
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.route.paramMap
|
this.hero$ = this.route.paramMap
|
||||||
.switchMap((params: ParamMap) =>
|
.switchMap((params: ParamMap) =>
|
||||||
this.service.getHero(params.get('id')))
|
this.service.getHero(params.get('id')));
|
||||||
.subscribe((hero: Hero) => this.hero = hero);
|
|
||||||
}
|
}
|
||||||
// #enddocregion ngOnInit
|
// #enddocregion ngOnInit
|
||||||
|
|
||||||
// #docregion gotoHeroes
|
// #docregion gotoHeroes
|
||||||
gotoHeroes() {
|
gotoHeroes(hero: Hero) {
|
||||||
let heroId = this.hero ? this.hero.id : null;
|
let heroId = hero ? hero.id : null;
|
||||||
// Pass along the hero id if available
|
// Pass along the hero id if available
|
||||||
// so that the HeroList component can select that hero.
|
// so that the HeroList component can select that hero.
|
||||||
// Include a junk 'foo' property for fun.
|
// Include a junk 'foo' property for fun.
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
// TODO SOMEDAY: Feature Componetized like HeroCenter
|
// TODO SOMEDAY: Feature Componetized like HeroCenter
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
import { Router } from '@angular/router';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
import { Hero, HeroService } from './hero.service';
|
import { Hero, HeroService } from './hero.service';
|
||||||
|
|
||||||
@ -11,9 +12,12 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
template: `
|
template: `
|
||||||
<h2>HEROES</h2>
|
<h2>HEROES</h2>
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
<li *ngFor="let hero of heroes | async"
|
<li *ngFor="let hero of heroes$ | async">
|
||||||
(click)="onSelect(hero)">
|
// #docregion nav-to-detail
|
||||||
|
<a [routerLink]="['/hero', hero.id]">
|
||||||
<span class="badge">{{ hero.id }}</span>{{ hero.name }}
|
<span class="badge">{{ hero.id }}</span>{{ hero.name }}
|
||||||
|
</a>
|
||||||
|
// #enddocregion nav-to-detail
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -22,7 +26,7 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
// #enddocregion template
|
// #enddocregion template
|
||||||
})
|
})
|
||||||
export class HeroListComponent implements OnInit {
|
export class HeroListComponent implements OnInit {
|
||||||
heroes: Promise<Hero[]>;
|
heroes$: Observable<Hero[]>;
|
||||||
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
constructor(
|
constructor(
|
||||||
@ -32,16 +36,8 @@ export class HeroListComponent implements OnInit {
|
|||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.heroes = this.service.getHeroes();
|
this.heroes$ = this.service.getHeroes();
|
||||||
}
|
}
|
||||||
|
|
||||||
// #docregion select
|
|
||||||
onSelect(hero: Hero) {
|
|
||||||
// #docregion nav-to-detail
|
|
||||||
this.router.navigate(['/hero', hero.id]);
|
|
||||||
// #enddocregion nav-to-detail
|
|
||||||
}
|
|
||||||
// #enddocregion select
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import { Observable } from 'rxjs/Observable';
|
|||||||
// #enddocregion rxjs-imports
|
// #enddocregion rxjs-imports
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
// #docregion import-router
|
// #docregion import-router
|
||||||
import { Router, ActivatedRoute, ParamMap } from '@angular/router';
|
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||||
// #enddocregion import-router
|
// #enddocregion import-router
|
||||||
|
|
||||||
import { Hero, HeroService } from './hero.service';
|
import { Hero, HeroService } from './hero.service';
|
||||||
@ -17,10 +17,11 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
template: `
|
template: `
|
||||||
<h2>HEROES</h2>
|
<h2>HEROES</h2>
|
||||||
<ul class="items">
|
<ul class="items">
|
||||||
<li *ngFor="let hero of heroes | async"
|
<li *ngFor="let hero of heroes$ | async"
|
||||||
[class.selected]="isSelected(hero)"
|
[class.selected]="hero.id === selectedId">
|
||||||
(click)="onSelect(hero)">
|
<a [routerLink]="['/hero', hero.id]">
|
||||||
<span class="badge">{{ hero.id }}</span>{{ hero.name }}
|
<span class="badge">{{ hero.id }}</span>{{ hero.name }}
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
@ -30,18 +31,17 @@ import { Hero, HeroService } from './hero.service';
|
|||||||
})
|
})
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
export class HeroListComponent implements OnInit {
|
export class HeroListComponent implements OnInit {
|
||||||
heroes: Observable<Hero[]>;
|
heroes$: Observable<Hero[]>;
|
||||||
|
|
||||||
private selectedId: number;
|
private selectedId: number;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private service: HeroService,
|
private service: HeroService,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute
|
||||||
private router: Router
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.heroes = this.route.paramMap
|
this.heroes$ = this.route.paramMap
|
||||||
.switchMap((params: ParamMap) => {
|
.switchMap((params: ParamMap) => {
|
||||||
// (+) before `params.get()` turns the string into a number
|
// (+) before `params.get()` turns the string into a number
|
||||||
this.selectedId = +params.get('id');
|
this.selectedId = +params.get('id');
|
||||||
@ -49,16 +49,6 @@ export class HeroListComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
// #docregion isSelected
|
|
||||||
isSelected(hero: Hero) { return hero.id === this.selectedId; }
|
|
||||||
// #enddocregion isSelected
|
|
||||||
|
|
||||||
// #docregion select
|
|
||||||
onSelect(hero: Hero) {
|
|
||||||
this.router.navigate(['/hero', hero.id]);
|
|
||||||
}
|
|
||||||
// #enddocregion select
|
|
||||||
// #docregion ctor
|
// #docregion ctor
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
|
import 'rxjs/add/observable/of';
|
||||||
|
import 'rxjs/add/operator/map';
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
|
import { Observable } from 'rxjs/Observable';
|
||||||
|
|
||||||
export class Hero {
|
export class Hero {
|
||||||
constructor(public id: number, public name: string) { }
|
constructor(public id: number, public name: string) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
let HEROES = [
|
const HEROES = [
|
||||||
new Hero(11, 'Mr. Nice'),
|
new Hero(11, 'Mr. Nice'),
|
||||||
new Hero(12, 'Narco'),
|
new Hero(12, 'Narco'),
|
||||||
new Hero(13, 'Bombasto'),
|
new Hero(13, 'Bombasto'),
|
||||||
@ -14,15 +17,13 @@ let HEROES = [
|
|||||||
new Hero(16, 'RubberMan')
|
new Hero(16, 'RubberMan')
|
||||||
];
|
];
|
||||||
|
|
||||||
let heroesPromise = Promise.resolve(HEROES);
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HeroService {
|
export class HeroService {
|
||||||
getHeroes() { return heroesPromise; }
|
getHeroes() { return Observable.of(HEROES); }
|
||||||
|
|
||||||
getHero(id: number | string) {
|
getHero(id: number | string) {
|
||||||
return heroesPromise
|
return this.getHeroes()
|
||||||
// (+) before `id` turns the string into a number
|
// (+) before `id` turns the string into a number
|
||||||
.then(heroes => heroes.find(hero => hero.id === +id));
|
.map(heroes => heroes.find(hero => hero.id === +id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
// #docregion
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { RouterModule, Routes } from '@angular/router';
|
||||||
|
|
||||||
|
import { HeroListComponent } from './hero-list.component';
|
||||||
|
import { HeroDetailComponent } from './hero-detail.component';
|
||||||
|
|
||||||
|
const heroesRoutes: Routes = [
|
||||||
|
{ path: 'heroes', component: HeroListComponent },
|
||||||
|
// #docregion hero-detail-route
|
||||||
|
{ path: 'hero/:id', component: HeroDetailComponent }
|
||||||
|
// #enddocregion hero-detail-route
|
||||||
|
];
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [
|
||||||
|
RouterModule.forChild(heroesRoutes)
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
RouterModule
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class HeroRoutingModule { }
|
||||||
|
// #enddocregion
|
@ -6,10 +6,10 @@ import { HeroListComponent } from './hero-list.component';
|
|||||||
import { HeroDetailComponent } from './hero-detail.component';
|
import { HeroDetailComponent } from './hero-detail.component';
|
||||||
|
|
||||||
const heroesRoutes: Routes = [
|
const heroesRoutes: Routes = [
|
||||||
{ path: 'heroes', component: HeroListComponent },
|
{ path: 'heroes', redirectTo: '/superheroes' },
|
||||||
// #docregion hero-detail-route
|
{ path: 'hero/:id', redirectTo: '/superhero/:id' },
|
||||||
{ path: 'hero/:id', component: HeroDetailComponent }
|
{ path: 'superheroes', component: HeroListComponent },
|
||||||
// #enddocregion hero-detail-route
|
{ path: 'superhero/:id', component: HeroDetailComponent }
|
||||||
];
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -35,7 +35,7 @@ export class HeroDetailComponent {
|
|||||||
@Input() prefix = '';
|
@Input() prefix = '';
|
||||||
|
|
||||||
// #docregion deleteRequest
|
// #docregion deleteRequest
|
||||||
// This component make a request but it can't actually delete a hero.
|
// This component makes a request but it can't actually delete a hero.
|
||||||
deleteRequest = new EventEmitter<Hero>();
|
deleteRequest = new EventEmitter<Hero>();
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
|
@ -44,6 +44,7 @@ System.config({
|
|||||||
map: {
|
map: {
|
||||||
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
|
'@angular/core/testing': 'npm:@angular/core/bundles/core-testing.umd.js',
|
||||||
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
|
'@angular/common/testing': 'npm:@angular/common/bundles/common-testing.umd.js',
|
||||||
|
'@angular/common/http/testing': 'npm:@angular/common/bundles/common-http-testing.umd.js',
|
||||||
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
|
'@angular/compiler/testing': 'npm:@angular/compiler/bundles/compiler-testing.umd.js',
|
||||||
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
|
'@angular/platform-browser/testing': 'npm:@angular/platform-browser/bundles/platform-browser-testing.umd.js',
|
||||||
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
|
'@angular/platform-browser-dynamic/testing': 'npm:@angular/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js',
|
||||||
|
@ -52,6 +52,10 @@ module.exports = function(config) {
|
|||||||
{ pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
|
{ pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
|
||||||
{ pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
|
{ pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
|
||||||
|
|
||||||
|
// tslib (TS helper fns such as `__extends`)
|
||||||
|
{ pattern: 'node_modules/tslib/**/*.js', included: false, watched: false },
|
||||||
|
{ pattern: 'node_modules/tslib/**/*.js.map', included: false, watched: false },
|
||||||
|
|
||||||
// Paths loaded via module imports:
|
// Paths loaded via module imports:
|
||||||
// Angular itself
|
// Angular itself
|
||||||
{ pattern: 'node_modules/@angular/**/*.js', included: false, watched: false },
|
{ pattern: 'node_modules/@angular/**/*.js', included: false, watched: false },
|
||||||
|
@ -347,7 +347,7 @@ Here are the features which may require additional polyfills:
|
|||||||
|
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
If you use the following deprecated i18n pipes: [date](api/common/DeprecatedDatePipe), [currency](api/common/DeprecatedCurrencyPipe), [decimal](api/common/DeprecatedDecimalPipe) and [percent](api/common/DeprecatedPercentPipe)
|
[Date](api/common/DatePipe), [currency](api/common/CurrencyPipe), [decimal](api/common/DecimalPipe) and [percent](api/common/PercentPipe) pipes
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
|
@ -40,28 +40,6 @@ You need to build and deploy a separate version of the application for each supp
|
|||||||
|
|
||||||
{@a i18n-attribute}
|
{@a i18n-attribute}
|
||||||
|
|
||||||
## i18n pipes
|
|
||||||
|
|
||||||
Angular pipes can help you with internationalization: the `DatePipe`, `CurrencyPipe`, `DecimalPipe`
|
|
||||||
and `PercentPipe` use locale data to format your data based on your `LOCALE_ID`.
|
|
||||||
|
|
||||||
By default Angular only contains locale data for the language `en-US`, if you set the value of
|
|
||||||
`LOCALE_ID` to another locale, you will have to import new locale data for this language:
|
|
||||||
|
|
||||||
<code-example path="i18n/src/app/app.locale_data.ts" region="import-locale" title="src/app/app.locale_data.ts" linenums="false">
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
|
||||||
|
|
||||||
Note that the files in `@angular/common/i18n_data` contain most of the locale data that you will
|
|
||||||
need, but some advanced formatting options might only be available in the extra dataset that you can
|
|
||||||
import from `@angular/common/i18n_data/extra`:
|
|
||||||
|
|
||||||
<code-example path="i18n/src/app/app.locale_data_extra.ts" region="import-locale-extra" title="src/app/app.locale_data_extra.ts" linenums="false">
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Mark text with the _i18n_ attribute
|
## Mark text with the _i18n_ attribute
|
||||||
|
|
||||||
The Angular `i18n` attribute is a marker for translatable content.
|
The Angular `i18n` attribute is a marker for translatable content.
|
||||||
|
@ -1,176 +0,0 @@
|
|||||||
# Angular Language Service
|
|
||||||
|
|
||||||
The Angular Language Service is a way to get completions, errors,
|
|
||||||
hints, and navigation inside your Angular templates whether they
|
|
||||||
are external in an HTML file or embedded in annotations/decorators
|
|
||||||
in a string. The Angular Language Service autodetects that you are
|
|
||||||
opening an Angular file, reads your `tsconfig.json` file, finds all the
|
|
||||||
templates you have in your application, and then provides language
|
|
||||||
services for any templates that you open.
|
|
||||||
|
|
||||||
|
|
||||||
## Autocompletion
|
|
||||||
|
|
||||||
Autocompletion can speed up your development time by providing you with
|
|
||||||
contextual possibilities and hints as you type. This example shows
|
|
||||||
autocomplete in an interpolation. As you type it out,
|
|
||||||
you can hit tab to complete.
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<img src="generated/images/guide/language-service/language-completion.gif" alt="autocompletion">
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
There are also completions within
|
|
||||||
elements. Any elements you have as a component selector will
|
|
||||||
show up in the completion list.
|
|
||||||
|
|
||||||
## Error checking
|
|
||||||
|
|
||||||
The Angular Language Service can also forewarn you of mistakes in your code.
|
|
||||||
In this example, Angular doesn't know what `orders` is or where it comes from.
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<img src="generated/images/guide/language-service/language-error.gif" alt="error checking">
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
## Navigation
|
|
||||||
|
|
||||||
Navigation allows you to hover to
|
|
||||||
see where a component, directive, module, etc. is from and then
|
|
||||||
click and press F12 to go directly to its definition.
|
|
||||||
|
|
||||||
<figure>
|
|
||||||
<img src="generated/images/guide/language-service/language-navigation.gif" alt="navigation">
|
|
||||||
</figure>
|
|
||||||
|
|
||||||
|
|
||||||
## Angular Language Service in your editor
|
|
||||||
|
|
||||||
Angular Language Service is currently available for [Visual Studio Code](https://code.visualstudio.com/) and
|
|
||||||
[WebStorm](https://www.jetbrains.com/webstorm).
|
|
||||||
|
|
||||||
### Visual Studio Code
|
|
||||||
|
|
||||||
In Visual Studio Code, install Angular Language Service from the store,
|
|
||||||
which is accessible from the bottom icon on the left menu pane.
|
|
||||||
You can also use the VS Quick Open (⌘+P) to search for the extension. When you've opened it,
|
|
||||||
enter the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ext install ng-template
|
|
||||||
```
|
|
||||||
|
|
||||||
Then click the install button to install the Angular Language Service.
|
|
||||||
|
|
||||||
|
|
||||||
### WebStorm
|
|
||||||
|
|
||||||
In webstorm, you have to install the language service as a dev dependency.
|
|
||||||
When Angular sees this dev dependency, it provides the
|
|
||||||
language service inside of WebStorm. Webstorm then gives you
|
|
||||||
colorization inside the template and autocomplete in addition to the Angular Language Service.
|
|
||||||
|
|
||||||
Here's the dev dependency
|
|
||||||
you need to have in `package.json`:
|
|
||||||
|
|
||||||
```json
|
|
||||||
|
|
||||||
devDependencies {
|
|
||||||
"@angular/language-service": "^4.0.0"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Then in the terminal window at the root of your project,
|
|
||||||
install the `devDependencies` with `npm` or `yarn`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
*OR*
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yarn
|
|
||||||
```
|
|
||||||
|
|
||||||
*OR*
|
|
||||||
|
|
||||||
```sh
|
|
||||||
yarn install
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
### Sublime Text
|
|
||||||
|
|
||||||
In [Sublime Text](https://www.sublimetext.com/), you first need an extension to allow Typescript.
|
|
||||||
Install the latest version of typescript in a local `node_modules` directory:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --save-dev typescript
|
|
||||||
```
|
|
||||||
|
|
||||||
Then install the Angular Language Service in the same location:
|
|
||||||
```sh
|
|
||||||
npm install --save-dev @angular/language-service
|
|
||||||
```
|
|
||||||
|
|
||||||
Starting with TypeScript 2.3, TypeScript has a language service plugin model that the language service can use.
|
|
||||||
|
|
||||||
Next, in your user preferences (`Cmd+,` or `Ctrl+,`), add:
|
|
||||||
|
|
||||||
```json
|
|
||||||
"typescript-tsdk": "<path to your folder>/node_modules/typescript/lib"
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Installing in your project
|
|
||||||
|
|
||||||
You can also install Angular Language Service in your project with the
|
|
||||||
following `npm` command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install --save-dev @angular/language-service
|
|
||||||
```
|
|
||||||
Additionally, add the following to the `"compilerOptions"` section of
|
|
||||||
your project's `tsconfig.json`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
"plugins": [
|
|
||||||
{"name": "@angular/language-service"}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
Note that this only provides diagnostics and completions in `.ts`
|
|
||||||
files. You need a custom sublime plugin (or modifications to the current plugin)
|
|
||||||
for completions in HTML files.
|
|
||||||
|
|
||||||
|
|
||||||
## How the Language Service works
|
|
||||||
|
|
||||||
When you use an editor with a language service, there's an
|
|
||||||
editor process which starts a separate language process/service
|
|
||||||
to which it speaks through an [RPC](https://en.wikipedia.org/wiki/Remote_procedure_call).
|
|
||||||
Any time you type inside of the editor, it sends information to the other process to
|
|
||||||
track the state of your project. When you trigger a completion list within a template, the editor process first parses the template into an HTML AST, or [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). Then the Angular compiler interprets
|
|
||||||
what module the template is part of, the scope you're in, and the component selector. Then it figures out where in the template AST your cursor is. When it determines the
|
|
||||||
context, it can then determine what the children can be.
|
|
||||||
|
|
||||||
It's a little more involved if you are in an interpolation. If you have an interpolation of `{{data.---}}` inside a `div` and need the completion list after `data.---`, the compiler can't use the HTML AST to find the answer. The HTML AST can only tell the compiler that there is some text with the characters "`{{data.---}}`". 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 and asks the TypeScript Language Service what the members of data are. TypeScript then returns the list of possibilities.
|
|
||||||
|
|
||||||
|
|
||||||
For more in-depth information, see the
|
|
||||||
[Angular Language Service API](https://github.com/angular/angular/blob/master/packages/language-service/src/types.ts)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
## More on Information
|
|
||||||
|
|
||||||
For more information, see [Chuck Jazdzewski's presentation](https://www.youtube.com/watch?v=ez3R0Gi4z5A&t=368s) on the Angular Language
|
|
||||||
Service from [ng-conf](https://www.ng-conf.org/) 2017.
|
|
||||||
|
|
||||||
|
|
@ -16,7 +16,7 @@ In the following example, the `@Component()` metadata object and the class const
|
|||||||
```typescript
|
```typescript
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-typical',
|
selector: 'app-typical',
|
||||||
template: '<div>A typical component for {{data.name}}</div>'
|
template: 'div>A typical component for {{data.name}}</div>
|
||||||
)}
|
)}
|
||||||
export class TypicalComponent {
|
export class TypicalComponent {
|
||||||
@Input() data: TypicalData;
|
@Input() data: TypicalData;
|
||||||
|
@ -46,6 +46,24 @@ Inside the interpolation expression, you flow the component's `birthday` value t
|
|||||||
function on the right. All pipes work this way.
|
function on the right. All pipes work this way.
|
||||||
|
|
||||||
|
|
||||||
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
The `Date` and `Currency` pipes need the *ECMAScript Internationalization API*.
|
||||||
|
Safari and other older browsers don't support it. You can add support with a polyfill.
|
||||||
|
|
||||||
|
|
||||||
|
<code-example language="html">
|
||||||
|
<script src="https://cdn.polyfill.io/v2/polyfill.min.js?features=Intl.~locale.en"></script>
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Built-in pipes
|
## Built-in pipes
|
||||||
|
@ -204,6 +204,149 @@ application using the `Router` service and the `routerState` property.
|
|||||||
Each `ActivatedRoute` in the `RouterState` provides methods to traverse up and down the route tree
|
Each `ActivatedRoute` in the `RouterState` provides methods to traverse up and down the route tree
|
||||||
to get information from parent, child and sibling routes.
|
to get information from parent, child and sibling routes.
|
||||||
|
|
||||||
|
{@a activated-route}
|
||||||
|
|
||||||
|
|
||||||
|
### Activated route
|
||||||
|
|
||||||
|
The route path and parameters are available through an injected router service called the
|
||||||
|
[ActivatedRoute](api/router/ActivatedRoute).
|
||||||
|
It has a great deal of useful information including:
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<tr>
|
||||||
|
<th>
|
||||||
|
Property
|
||||||
|
</th>
|
||||||
|
|
||||||
|
<th>
|
||||||
|
Description
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>url</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An `Observable` of the route path(s), represented as an array of strings for each part of the route path.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>data</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An `Observable` that contains the `data` object provided for the route. Also contains any resolved values from the [resolve guard](#resolve-guard).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>paramMap</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An `Observable` that contains a [map](api/router/ParamMap) of the required and [optional parameters](#optional-route-parameters) specific to the route. The map supports retrieving single and multiple values from the same parameter.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>queryParamMap</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An `Observable` that contains a [map](api/router/ParamMap) of the [query parameters](#query-parameters) available to all routes.
|
||||||
|
The map supports retrieving single and multiple values from the query parameter.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>fragment</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
An `Observable` of the URL [fragment](#fragment) available to all routes.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>outlet</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The name of the `RouterOutlet` used to render the route. For an unnamed outlet, the outlet name is _primary_.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>routeConfig</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The route configuration used for the route that contains the origin path.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>parent</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
The route's parent `ActivatedRoute` when this route is a [child route](#child-routing-component).
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>firstChild</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Contains the first `ActivatedRoute` in the list of this route's child routes.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<code>children</code>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Contains all the [child routes](#child-routing-component) activated under the current route.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
Two older properties are still available. They are less capable than their replacements, discouraged, and may be deprecated in a future Angular version.
|
||||||
|
|
||||||
|
**`params`** — An `Observable` that contains the required and [optional parameters](#optional-route-parameters) specific to the route. Use `paramMap` instead.
|
||||||
|
|
||||||
|
**`queryParams`** — An `Observable` that contains the [query parameters](#query-parameters) available to all routes.
|
||||||
|
Use `queryParamMap` instead.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
### Router events
|
### Router events
|
||||||
|
|
||||||
During each navigation, the `Router` emits navigation events through the `Router.events` property. These events range from when the navigation starts and ends to many points in between. The full list of navigation events is displayed in the table below.
|
During each navigation, the `Router` emits navigation events through the `Router.events` property. These events range from when the navigation starts and ends to many points in between. The full list of navigation events is displayed in the table below.
|
||||||
@ -1333,7 +1476,7 @@ Create a new `heroes-routing.module.ts` in the `heroes` folder
|
|||||||
using the same techniques you learned while creating the `AppRoutingModule`.
|
using the same techniques you learned while creating the `AppRoutingModule`.
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/heroes/heroes-routing.module.ts" title="src/app/heroes/heroes-routing.module.ts">
|
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" title="src/app/heroes/heroes-routing.module.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -1503,7 +1646,7 @@ Return to the `HeroesRoutingModule` and look at the route definitions again.
|
|||||||
The route to `HeroDetailComponent` has a twist.
|
The route to `HeroDetailComponent` has a twist.
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/heroes/heroes-routing.module.ts" linenums="false" title="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route">
|
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" title="src/app/heroes/heroes-routing.module.ts (excerpt)" region="hero-detail-route">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -1547,52 +1690,6 @@ a route for some other hero.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a navigate}
|
|
||||||
|
|
||||||
|
|
||||||
### Navigate to hero detail imperatively
|
|
||||||
|
|
||||||
Users *will not* navigate to the detail component by clicking a link
|
|
||||||
so you won't add a new `RouterLink` anchor tag to the shell.
|
|
||||||
|
|
||||||
Instead, when the user *clicks* a hero in the list, you'll ask the router
|
|
||||||
to navigate to the hero detail view for the selected hero.
|
|
||||||
|
|
||||||
Start in the `HeroListComponent`.
|
|
||||||
Revise its constructor so that it acquires the `Router` and the `HeroService` by dependency injection:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/heroes/hero-list.component.1.ts" linenums="false" title="src/app/heroes/hero-list.component.ts (constructor)" region="ctor">
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Make the following few changes to the component's template:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/heroes/hero-list.component.1.ts" linenums="false" title="src/app/heroes/hero-list.component.ts (template)" region="template">
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The template defines an `*ngFor` repeater such as [you've seen before](guide/displaying-data#ngFor).
|
|
||||||
There's a `(click)` [event binding](guide/template-syntax#event-binding) to the component's
|
|
||||||
`onSelect` method which you implement as follows:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/heroes/hero-list.component.1.ts" linenums="false" title="src/app/heroes/hero-list.component.ts (select)" region="select">
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The component's `onSelect` calls the router's **`navigate`** method with a _link parameters array_.
|
|
||||||
You can use this same syntax in a `RouterLink` if you decide later to navigate in HTML template rather than in component code.
|
|
||||||
|
|
||||||
|
|
||||||
{@a route-parameters}
|
{@a route-parameters}
|
||||||
|
|
||||||
|
|
||||||
@ -1629,152 +1726,9 @@ the `HeroDetailComponent` via the `ActivatedRoute` service.
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a activated-route}
|
{@a activated-route}
|
||||||
|
|
||||||
|
### _Activated Route_ in action
|
||||||
### ActivatedRoute: the one-stop-shop for route information
|
|
||||||
|
|
||||||
The route path and parameters are available through an injected router service called the
|
|
||||||
[ActivatedRoute](api/router/ActivatedRoute).
|
|
||||||
It has a great deal of useful information including:
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<th>
|
|
||||||
Property
|
|
||||||
</th>
|
|
||||||
|
|
||||||
<th>
|
|
||||||
Description
|
|
||||||
</th>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>url</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
An `Observable` of the route path(s), represented as an array of strings for each part of the route path.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>data</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
An `Observable` that contains the `data` object provided for the route. Also contains any resolved values from the [resolve guard](#resolve-guard).
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>paramMap</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
An `Observable` that contains a [map](api/router/ParamMap) of the required and [optional parameters](#optional-route-parameters) specific to the route. The map supports retrieving single and multiple values from the same parameter.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>queryParamMap</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
An `Observable` that contains a [map](api/router/ParamMap) of the [query parameters](#query-parameters) available to all routes.
|
|
||||||
The map supports retrieving single and multiple values from the query parameter.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>fragment</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
An `Observable` of the URL [fragment](#fragment) available to all routes.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>outlet</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The name of the `RouterOutlet` used to render the route. For an unnamed outlet, the outlet name is _primary_.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>routeConfig</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The route configuration used for the route that contains the origin path.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>parent</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
The route's parent `ActivatedRoute` when this route is a [child route](#child-routing-component).
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>firstChild</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Contains the first `ActivatedRoute` in the list of this route's child routes.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<code>children</code>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
|
|
||||||
Contains all the [child routes](#child-routing-component) activated under the current route.
|
|
||||||
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div class="l-sub-section">
|
|
||||||
|
|
||||||
Two older properties are still available. They are less capable than their replacements, discouraged, and may be deprecated in a future Angular version.
|
|
||||||
|
|
||||||
**`params`** — An `Observable` that contains the required and [optional parameters](#optional-route-parameters) specific to the route. Use `paramMap` instead.
|
|
||||||
|
|
||||||
**`queryParams`** — An `Observable` that contains the [query parameters](#query-parameters) available to all routes.
|
|
||||||
Use `queryParamMap` instead.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
#### _Activated Route_ in action
|
|
||||||
|
|
||||||
Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package.
|
Import the `Router`, `ActivatedRoute`, and `ParamMap` tokens from the router package.
|
||||||
|
|
||||||
@ -1820,13 +1774,12 @@ Then you tell the `HeroService` to fetch the hero with that `id` and return the
|
|||||||
|
|
||||||
You might think to use the RxJS `map` operator.
|
You might think to use the RxJS `map` operator.
|
||||||
But the `HeroService` returns an `Observable<Hero>`.
|
But the `HeroService` returns an `Observable<Hero>`.
|
||||||
Your subscription wants the `Hero`, not an `Observable<Hero>`.
|
|
||||||
So you flatten the `Observable` with the `switchMap` operator instead.
|
So you flatten the `Observable` with the `switchMap` operator instead.
|
||||||
|
|
||||||
The `switchMap` operator also cancels previous in-flight requests. If the user re-navigates to this route
|
The `switchMap` operator also cancels previous in-flight requests. If the user re-navigates to this route
|
||||||
with a new `id` while the `HeroService` is still retrieving the old `id`, `switchMap` discards that old request and returns the hero for the new `id`.
|
with a new `id` while the `HeroService` is still retrieving the old `id`, `switchMap` discards that old request and returns the hero for the new `id`.
|
||||||
|
|
||||||
Finally, you activate the observable with `subscribe` method and (re)set the component's `hero` property with the retrieved hero.
|
The observable `Subscription` will be handled by the `AsyncPipe` and the component's `hero` property will be (re)set with the retrieved hero.
|
||||||
|
|
||||||
#### _ParamMap_ API
|
#### _ParamMap_ API
|
||||||
|
|
||||||
@ -2054,7 +2007,7 @@ The router embedded the `id` value in the navigation URL because you had defined
|
|||||||
as a route parameter with an `:id` placeholder token in the route `path`:
|
as a route parameter with an `:id` placeholder token in the route `path`:
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/heroes/heroes-routing.module.ts" linenums="false" title="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route">
|
<code-example path="router/src/app/heroes/heroes-routing.module.1.ts" linenums="false" title="src/app/heroes/heroes-routing.module.ts (hero-detail-route)" region="hero-detail-route">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -2190,17 +2143,9 @@ Then you inject the `ActivatedRoute` in the `HeroListComponent` constructor.
|
|||||||
The `ActivatedRoute.paramMap` property is an `Observable` map of route parameters. The `paramMap` emits a new map of values that includes `id`
|
The `ActivatedRoute.paramMap` property is an `Observable` map of route parameters. The `paramMap` emits a new map of values that includes `id`
|
||||||
when the user navigates to the component. In `ngOnInit` you subscribe to those values, set the `selectedId`, and get the heroes.
|
when the user navigates to the component. In `ngOnInit` you subscribe to those values, set the `selectedId`, and get the heroes.
|
||||||
|
|
||||||
Add an `isSelected` method that returns `true` when a hero's `id` matches the selected `id`.
|
|
||||||
|
|
||||||
|
Update the template with a [class binding](guide/template-syntax#class-binding).
|
||||||
<code-example path="router/src/app/heroes/hero-list.component.ts" linenums="false" title="src/app/heroes/hero-list.component.ts (isSelected)" region="isSelected">
|
The binding adds the `selected` CSS class when the comparison returns `true` and removes it when `false`.
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Finally, update the template with a [class binding](guide/template-syntax#class-binding) to that `isSelected` method.
|
|
||||||
The binding adds the `selected` CSS class when the method returns `true` and removes it when `false`.
|
|
||||||
Look for it within the repeated `<li>` tag as shown here:
|
Look for it within the repeated `<li>` tag as shown here:
|
||||||
|
|
||||||
|
|
||||||
@ -2439,7 +2384,7 @@ Here are the relevant files for this version of the sample application.
|
|||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
<code-pane title="heroes-routing.module.ts" path="router/src/app/heroes/heroes-routing.module.ts">
|
<code-pane title="heroes-routing.module.ts" path="router/src/app/heroes/heroes-routing.module.1.ts">
|
||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
@ -2693,40 +2638,15 @@ The router then calculates the target URL based on the active route's location.
|
|||||||
{@a nav-to-crisis}
|
{@a nav-to-crisis}
|
||||||
|
|
||||||
|
|
||||||
### Navigate to crisis detail with a relative URL
|
### Navigate to crisis list with a relative URL
|
||||||
|
|
||||||
Update the *Crisis List* `onSelect` method to use relative navigation so you don't have
|
|
||||||
to start from the top of the route configuration.
|
|
||||||
|
|
||||||
You've already injected the `ActivatedRoute` that you need to compose the relative navigation path.
|
You've already injected the `ActivatedRoute` that you need to compose the relative navigation path.
|
||||||
|
|
||||||
<code-example path="router/src/app/crisis-center/crisis-list.component.ts" linenums="false" title="src/app/crisis-center/crisis-list.component.ts (constructor)" region="ctor">
|
When using a `RouterLink` to navigate instead of the `Router` service, you'd use the _same_
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
When you visit the *Crisis Center*, the ancestor path is `/crisis-center`,
|
|
||||||
so you only need to add the `id` of the *Crisis Center* to the existing path.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/crisis-center/crisis-list.component.ts" linenums="false" title="src/app/crisis-center/crisis-list.component.ts (relative navigation)" region="onSelect">
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you were using a `RouterLink` to navigate instead of the `Router` service, you'd use the _same_
|
|
||||||
link parameters array, but you wouldn't provide the object with the `relativeTo` property.
|
link parameters array, but you wouldn't provide the object with the `relativeTo` property.
|
||||||
The `ActivatedRoute` is implicit in a `RouterLink` directive.
|
The `ActivatedRoute` is implicit in a `RouterLink` directive.
|
||||||
|
|
||||||
|
|
||||||
<code-example path="router/src/app/crisis-center/crisis-list.component.1.ts" linenums="false" title="src/app/crisis-center/crisis-list.component.ts (relative routerLink)" region="relative-navigation-router-link">
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back to the *Crisis Center* list using relative path navigation.
|
Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back to the *Crisis Center* list using relative path navigation.
|
||||||
|
|
||||||
|
|
||||||
@ -2735,7 +2655,6 @@ Update the `gotoCrises` method of the `CrisisDetailComponent` to navigate back t
|
|||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Notice that the path goes up a level using the `../` syntax.
|
Notice that the path goes up a level using the `../` syntax.
|
||||||
If the current crisis `id` is `3`, the resulting path back to the crisis list is `/crisis-center/;id=3;foo=foo`.
|
If the current crisis `id` is `3`, the resulting path back to the crisis list is `/crisis-center/;id=3;foo=foo`.
|
||||||
|
|
||||||
@ -3415,8 +3334,7 @@ is like waiting for the server asynchronously.
|
|||||||
|
|
||||||
The `DialogService`, provided in the `AppModule` for app-wide use, does the asking.
|
The `DialogService`, provided in the `AppModule` for app-wide use, does the asking.
|
||||||
|
|
||||||
It returns a [promise](http://exploringjs.com/es6/ch_promises.html) that
|
It returns an `Observable` that *resolves* when the user eventually decides what to do: either
|
||||||
*resolves* when the user eventually decides what to do: either
|
|
||||||
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
|
to discard changes and navigate away (`true`) or to preserve the pending changes and stay in the crisis editor (`false`).
|
||||||
|
|
||||||
|
|
||||||
@ -3541,8 +3459,12 @@ Be explicit. Implement the `Resolve` interface with a type of `Crisis`.
|
|||||||
Inject the `CrisisService` and `Router` and implement the `resolve()` method.
|
Inject the `CrisisService` and `Router` and implement the `resolve()` method.
|
||||||
That method could return a `Promise`, an `Observable`, or a synchronous return value.
|
That method could return a `Promise`, an `Observable`, or a synchronous return value.
|
||||||
|
|
||||||
The `CrisisService.getCrisis` method returns a promise.
|
The `CrisisService.getCrisis` method returns an Observable.
|
||||||
Return that promise to prevent the route from loading until the data is fetched.
|
Return that observable to prevent the route from loading until the data is fetched.
|
||||||
|
The `Router` guards require an Observable to `complete`, meaning it has emitted all
|
||||||
|
of its values. You use the `take` operator with an argument of `1` to ensure that the
|
||||||
|
Observable completes after retrieving the first value from the Observable returned by the
|
||||||
|
`getCrisis` method.
|
||||||
If it doesn't return a valid `Crisis`, navigate the user back to the `CrisisListComponent`,
|
If it doesn't return a valid `Crisis`, navigate the user back to the `CrisisListComponent`,
|
||||||
canceling the previous in-flight navigation to the `CrisisDetailComponent`.
|
canceling the previous in-flight navigation to the `CrisisDetailComponent`.
|
||||||
|
|
||||||
@ -3580,12 +3502,15 @@ The router looks for that method and calls it if found.
|
|||||||
Don't worry about all the ways that the user could navigate away.
|
Don't worry about all the ways that the user could navigate away.
|
||||||
That's the router's job. Write this class and let the router take it from there.
|
That's the router's job. Write this class and let the router take it from there.
|
||||||
|
|
||||||
|
1. The Observable provided to the Router _must_ complete.
|
||||||
|
If the Observable does not complete, the navigation will not continue.
|
||||||
|
|
||||||
The relevant *Crisis Center* code for this milestone follows.
|
The relevant *Crisis Center* code for this milestone follows.
|
||||||
|
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
|
|
||||||
<code-pane title="app.component.ts" path="router/src/app/app.component.ts">
|
<code-pane title="app.component.ts" path="router/src/app/app.component.6.ts">
|
||||||
|
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
@ -4041,6 +3966,52 @@ Verify this by logging in to the `Admin` feature area and noting that the `crisi
|
|||||||
It's also logged to the browser's console.
|
It's also logged to the browser's console.
|
||||||
|
|
||||||
|
|
||||||
|
{@a redirect-advanced}
|
||||||
|
|
||||||
|
## Migrating URLs with Redirects
|
||||||
|
|
||||||
|
You've setup the routes for navigating around your application. You've used navigation imperatively and declaratively to many different routes. But like any application, requirements change over time. You've setup links and navigation to `/heroes` and `/hero/:id` from the `HeroListComponent` and `HeroDetailComponent` components. If there was a requirement that links to `heroes` become `superheroes`, you still want the previous URLs to navigate correctly. You also don't want to go and update every link in your application, so redirects makes refactoring routes trivial.
|
||||||
|
|
||||||
|
|
||||||
|
{@a url-refactor}
|
||||||
|
|
||||||
|
### Changing /heroes to /superheroes
|
||||||
|
|
||||||
|
Let's take the `Hero` routes and migrate them to new URLs. The `Router` checks for redirects in your configuration before navigating, so each redirect is triggered when needed. To support this change, you'll add redirects from the old routes to the new routes in the `heroes-routing.module`.
|
||||||
|
|
||||||
|
<code-example path="router/src/app/heroes/heroes-routing.module.ts" linenums="false" title="src/app/heroes/heroes-routing.module.ts (heroes redirects)">
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
You'll notice two different types of redirects. The first change is from `/heroes` to `/superheroes` without any parameters. This is a straightforward redirect, unlike the change from `/hero/:id` to `/superhero/:id`, which includes the `:id` route parameter. Router redirects also use powerful pattern matching, so the `Router` inspects the URL and replaces route parameters in the `path` with their appropriate destination. Previously, you navigated to a URL such as `/hero/15` with a route parameter `id` of `15`.
|
||||||
|
|
||||||
|
<div class="l-sub-section">
|
||||||
|
|
||||||
|
The `Router` also supports [query parameters](#query-parameters) and the [fragment](#fragment) when using redirects.
|
||||||
|
|
||||||
|
* When using absolute redirects, the `Router` will use the query parameters and the fragment from the redirectTo in the route config.
|
||||||
|
* When using relative redirects, the `Router` use the query params and the fragment from the source URL.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Before updating the `app-routing.module.ts`, you'll need to consider an important rule. Currently, our empty path route redirects to `/heroes`, which redirects to `/superheroes`. This _won't_ work and is by design as the `Router` handles redirects once at each level of routing configuration. This prevents chaining of redirects, which can lead to endless redirect loops.
|
||||||
|
|
||||||
|
So instead, you'll update the empty path route in `app-routing.module.ts` to redirect to `/superheroes`.
|
||||||
|
|
||||||
|
<code-example path="router/src/app/app-routing.module.ts" linenums="false" title="src/app/app-routing.module.ts (superheroes redirect)">
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Since `RouterLink`s aren't tied to route configuration, you'll need to update the associated router links so they remain active when the new route is active. You'll update the `app.component.ts` template for the `/heroes` routerLink.
|
||||||
|
|
||||||
|
<code-example path="router/src/app/app.component.ts" linenums="false" title="src/app/app.component.ts (superheroes active routerLink)">
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
With the redirects setup, all previous routes now point to their new destinations and both URLs still function as intended.
|
||||||
|
|
||||||
|
|
||||||
{@a inspect-config}
|
{@a inspect-config}
|
||||||
|
|
||||||
|
|
||||||
|
@ -313,7 +313,7 @@ It's intended source is implicit.
|
|||||||
Angular sets `let-hero` to the value of the context's `$implicit` property
|
Angular sets `let-hero` to the value of the context's `$implicit` property
|
||||||
which `NgFor` has initialized with the hero for the current iteration.
|
which `NgFor` has initialized with the hero for the current iteration.
|
||||||
|
|
||||||
* The [API guide](api/common/NgForOf "API: NgFor")
|
* The [API guide](api/common/NgFor "API: NgFor")
|
||||||
describes additional `NgFor` directive properties and context properties.
|
describes additional `NgFor` directive properties and context properties.
|
||||||
|
|
||||||
These microsyntax mechanisms are available to you when you write your own structural directives.
|
These microsyntax mechanisms are available to you when you write your own structural directives.
|
||||||
|
@ -1361,8 +1361,8 @@ to group elements when there is no suitable host element for the directive.
|
|||||||
_This_ section is an introduction to the common structural directives:
|
_This_ section is an introduction to the common structural directives:
|
||||||
|
|
||||||
* [`NgIf`](guide/template-syntax#ngIf) - conditionally add or remove an element from the DOM
|
* [`NgIf`](guide/template-syntax#ngIf) - conditionally add or remove an element from the DOM
|
||||||
|
* [`NgFor`](guide/template-syntax#ngFor) - repeat a template for each item in a list
|
||||||
* [`NgSwitch`](guide/template-syntax#ngSwitch) - a set of directives that switch among alternative views
|
* [`NgSwitch`](guide/template-syntax#ngSwitch) - a set of directives that switch among alternative views
|
||||||
* [NgForOf](guide/template-syntax#ngFor) - repeat a template for each item in a list
|
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
|
|
||||||
@ -1437,18 +1437,18 @@ described below.
|
|||||||
|
|
||||||
{@a ngFor}
|
{@a ngFor}
|
||||||
|
|
||||||
### NgForOf
|
### NgFor
|
||||||
|
|
||||||
`NgForOf` is a _repeater_ directive — a way to present a list of items.
|
`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.
|
You define a block of HTML that defines how a single item should be displayed.
|
||||||
You tell Angular to use that block as a template for rendering each item in the list.
|
You tell Angular to use that block as a template for rendering each item in the list.
|
||||||
|
|
||||||
Here is an example of `NgForOf` applied to a simple `<div>`:
|
Here is an example of `NgFor` applied to a simple `<div>`:
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="NgFor-1" title="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="NgFor-1" title="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
You can also apply an `NgForOf` to a component element, as in this example:
|
You can also apply an `NgFor` to a component element, as in this example:
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.html" region="NgFor-2" title="src/app/app.component.html" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.html" region="NgFor-2" title="src/app/app.component.html" linenums="false">
|
||||||
</code-example>
|
</code-example>
|
||||||
@ -1485,10 +1485,10 @@ Learn about the _microsyntax_ in the [_Structural Directives_](guide/structural-
|
|||||||
### Template input variables
|
### Template input variables
|
||||||
|
|
||||||
The `let` keyword before `hero` creates a _template input variable_ called `hero`.
|
The `let` keyword before `hero` creates a _template input variable_ called `hero`.
|
||||||
The `NgForOf` directive iterates over the `heroes` array returned by the parent component's `heroes` property
|
The `ngFor` directive iterates over the `heroes` array returned by the parent component's `heroes` property
|
||||||
and sets `hero` to the current item from the array during each iteration.
|
and sets `hero` to the current item from the array during each iteration.
|
||||||
|
|
||||||
You reference the `hero` input variable within the `NgForOf` host element
|
You reference the `hero` input variable within the `ngFor` host element
|
||||||
(and within its descendents) to access the hero's properties.
|
(and within its descendents) to access the hero's properties.
|
||||||
Here it is referenced first in an interpolation
|
Here it is referenced first in an interpolation
|
||||||
and then passed in a binding to the `hero` property of the `<hero-detail>` component.
|
and then passed in a binding to the `hero` property of the `<hero-detail>` component.
|
||||||
@ -1501,7 +1501,7 @@ Learn more about _template input variables_ in the
|
|||||||
|
|
||||||
#### *ngFor with _index_
|
#### *ngFor with _index_
|
||||||
|
|
||||||
The `index` property of the `NgForOf` directive context returns the zero-based index of the item in each iteration.
|
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 capture the `index` 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 hero name like this.
|
The next example captures the `index` in a variable named `i` and displays it with the hero name like this.
|
||||||
@ -1511,8 +1511,8 @@ The next example captures the `index` in a variable named `i` and displays it wi
|
|||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
Learn about the other `NgForOf` context values such as `last`, `even`,
|
Learn about the other `NgFor` context values such as `last`, `even`,
|
||||||
and `odd` in the [NgForOf API reference](api/common/NgForOf).
|
and `odd` in the [NgFor API reference](api/common/NgFor).
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -1520,7 +1520,7 @@ and `odd` in the [NgForOf API reference](api/common/NgForOf).
|
|||||||
|
|
||||||
#### *ngFor with _trackBy_
|
#### *ngFor with _trackBy_
|
||||||
|
|
||||||
The `NgForOf` directive may perform poorly, especially with large lists.
|
The `NgFor` directive may perform poorly, especially with large lists.
|
||||||
A small change to one item, an item removed, or an item added can trigger a cascade of DOM manipulations.
|
A small change to one item, an item removed, or an item added can trigger a cascade of DOM manipulations.
|
||||||
|
|
||||||
For example, re-querying the server could reset the list with all new hero objects.
|
For example, re-querying the server could reset the list with all new hero objects.
|
||||||
@ -1531,7 +1531,7 @@ But Angular sees only a fresh list of new object references.
|
|||||||
It has no choice but to tear down the old DOM elements and insert all new DOM elements.
|
It has no choice but to tear down the old DOM elements and insert all new DOM elements.
|
||||||
|
|
||||||
Angular can avoid this churn with `trackBy`.
|
Angular can avoid this churn with `trackBy`.
|
||||||
Add a method to the component that returns the value `NgForOf` _should_ track.
|
Add a method to the component that returns the value `NgFor` _should_ track.
|
||||||
In this case, that value is the hero's `id`.
|
In this case, that value is the hero's `id`.
|
||||||
|
|
||||||
<code-example path="template-syntax/src/app/app.component.ts" region="trackByHeroes" title="src/app/app.component.ts" linenums="false">
|
<code-example path="template-syntax/src/app/app.component.ts" region="trackByHeroes" title="src/app/app.component.ts" linenums="false">
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 10 KiB |
BIN
aio/content/images/bios/nirkaufman.jpg
Normal file
BIN
aio/content/images/bios/nirkaufman.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
Binary file not shown.
Before Width: | Height: | Size: 132 KiB |
Binary file not shown.
Before Width: | Height: | Size: 274 KiB |
Binary file not shown.
Before Width: | Height: | Size: 857 KiB |
@ -80,15 +80,6 @@
|
|||||||
"group": "Angular"
|
"group": "Angular"
|
||||||
},
|
},
|
||||||
|
|
||||||
"aaronzhang": {
|
|
||||||
"name": "Aaron Zhang (章小飞)",
|
|
||||||
"picture": "xiaofei.jpg",
|
|
||||||
"twitter": "",
|
|
||||||
"website": "http://github.com/damoqiongqiu",
|
|
||||||
"bio": "Aaron is Angular's developer PM in China. He is the lead for angular.cn and social channels in China, and helps developers in China's enterprise and open source communities to be successful with Angular. One of the earliest Angular developers in China since Angular 2012, he translated the first books on Angular into Chinese. Aaron joined the Google team in 2016.",
|
|
||||||
"group": "Angular"
|
|
||||||
},
|
|
||||||
|
|
||||||
"tobias": {
|
"tobias": {
|
||||||
"name": "Tobias Bosch",
|
"name": "Tobias Bosch",
|
||||||
"picture": "tobias.jpg",
|
"picture": "tobias.jpg",
|
||||||
@ -582,5 +573,23 @@
|
|||||||
"website": "https://medium.com/@gerard.sans",
|
"website": "https://medium.com/@gerard.sans",
|
||||||
"bio": "Gerard is very excited about the future of the Web and JavaScript. Always happy Computer Science Engineer and humble Google Developer Expert. He loves to share his learnings by giving talks, trainings and writing about cool technologies. He loves running AngularZone and GraphQL London, mentoring students and giving back to the community.",
|
"bio": "Gerard is very excited about the future of the Web and JavaScript. Always happy Computer Science Engineer and humble Google Developer Expert. He loves to share his learnings by giving talks, trainings and writing about cool technologies. He loves running AngularZone and GraphQL London, mentoring students and giving back to the community.",
|
||||||
"group": "GDE"
|
"group": "GDE"
|
||||||
|
},
|
||||||
|
|
||||||
|
"amcdnl": {
|
||||||
|
"name": "Austin McDaniel",
|
||||||
|
"picture": "amcdnl.jpg",
|
||||||
|
"twitter": "amcdnl",
|
||||||
|
"website": "https://amcdnl.com",
|
||||||
|
"bio": "Austin is an software architect with a passion for JavaScript and Angular. Austin loves to share his experiences with other like-minded developers by giving talks, blogging, podcasting and open-sourcing.",
|
||||||
|
"group": "Angular"
|
||||||
|
},
|
||||||
|
|
||||||
|
"nirkaufman": {
|
||||||
|
"name": "Nir Kaufman",
|
||||||
|
"picture": "nirkaufman.jpg",
|
||||||
|
"twitter": "nirkaufman",
|
||||||
|
"website": "http://ngnir.life/",
|
||||||
|
"bio": "Nir is a Principal Frontend Consultant & Head of the Angular department at 500Tech, Google Developer Expert and community leader. He organizes the largest Angular meetup group in Israel (Angular-IL), talks and teaches about front-end technologies around the world. He is also the author of two books about Angular and the founder of the 'Frontend Band'.",
|
||||||
|
"group": "GDE"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,19 +14,17 @@
|
|||||||
|
|
||||||
<!-- CONTAINER -->
|
<!-- CONTAINER -->
|
||||||
<div class="homepage-container">
|
<div class="homepage-container">
|
||||||
|
<!-- container content starts -->
|
||||||
|
|
||||||
<div class="hero-headline no-toc">One framework.<br>Mobile & desktop.</div>
|
<div class="hero-headline no-toc">One framework.<br>Mobile & desktop.</div>
|
||||||
<a class="button hero-cta" href="guide/quickstart">Get Started</a>
|
<a class="button hero-cta" href="guide/quickstart">Get Started</a>
|
||||||
</div>
|
</div><!-- CONTAINER END -->
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<!-- MAIN CONTENT -->
|
|
||||||
<article>
|
<article>
|
||||||
|
|
||||||
<h1 class="no-toc" style="display: none"></h1>
|
|
||||||
|
|
||||||
<div class="home-rows">
|
<div class="home-rows">
|
||||||
|
|
||||||
<!--Announcement Bar-->
|
<!--Announcement Bar-->
|
||||||
@ -55,7 +53,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
<!-- Group 2-->
|
<!-- Group 2-->
|
||||||
<div layout="row" layout-xs="column" class="home-row">
|
<div layout="row" layout-xs="column" class="home-row">
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
@ -93,6 +90,7 @@
|
|||||||
|
|
||||||
<!-- Group 4-->
|
<!-- Group 4-->
|
||||||
<div layout="row" layout-xs="column" class="home-row">
|
<div layout="row" layout-xs="column" class="home-row">
|
||||||
|
|
||||||
<div class="text-container">
|
<div class="text-container">
|
||||||
<div class="text-block l-pad-top-2">
|
<div class="text-block l-pad-top-2">
|
||||||
<div class="text-headline">Loved by Millions</div>
|
<div class="text-headline">Loved by Millions</div>
|
||||||
@ -109,6 +107,7 @@
|
|||||||
|
|
||||||
<!-- CTA CARDS -->
|
<!-- CTA CARDS -->
|
||||||
<div layout="row" layout-xs="column" class="home-row">
|
<div layout="row" layout-xs="column" class="home-row">
|
||||||
|
|
||||||
<a href="guide/quickstart">
|
<a href="guide/quickstart">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<img src="generated/images/marketing/home/code-icon.svg" height="70px">
|
<img src="generated/images/marketing/home/code-icon.svg" height="70px">
|
||||||
|
@ -130,6 +130,13 @@
|
|||||||
"rev": true,
|
"rev": true,
|
||||||
"title": "Apollo",
|
"title": "Apollo",
|
||||||
"url": "http://docs.apollostack.com/apollo-client/angular2.html"
|
"url": "http://docs.apollostack.com/apollo-client/angular2.html"
|
||||||
|
},
|
||||||
|
"ab4": {
|
||||||
|
"desc": "Angular Commerce is a solution for building modern e-commerce applications with power of Google Firebase. Set of components is design agnostic and allows to easily extend functionality.",
|
||||||
|
"logo": "",
|
||||||
|
"rev": true,
|
||||||
|
"title": "AngularCommerce",
|
||||||
|
"url": "https://github.com/NodeArt/angular-commerce"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -320,10 +327,10 @@
|
|||||||
"url": "https://www.ag-grid.com/best-angular-2-data-grid/"
|
"url": "https://www.ag-grid.com/best-angular-2-data-grid/"
|
||||||
},
|
},
|
||||||
"jqwidgets": {
|
"jqwidgets": {
|
||||||
"desc": "Angular UI Components including Grids, Charts, Scheduling and more.",
|
"desc": "Angular UI Components including data grid, tree grid, pivot grid, scheduler, charts, editors and other multi-purpose components",
|
||||||
"rev": true,
|
"rev": true,
|
||||||
"title": "jQWidgets",
|
"title": "jQWidgets",
|
||||||
"url": "http://www.jqwidgets.com/angular/"
|
"url": "https://www.jqwidgets.com/angular/"
|
||||||
},
|
},
|
||||||
"amexio": {
|
"amexio": {
|
||||||
"desc": "Amexio (Angular MetaMagic EXtensions for Inputs and Outputs) is a rich set of Angular components powered by Bootstrap for Responsive Design. UI Components include Standard Form Components, Data Grids, Tree Grids, Tabs etc. Open Source (Apache 2 License) & Free and backed by MetaMagic Global Inc",
|
"desc": "Amexio (Angular MetaMagic EXtensions for Inputs and Outputs) is a rich set of Angular components powered by Bootstrap for Responsive Design. UI Components include Standard Form Components, Data Grids, Tree Grids, Tabs etc. Open Source (Apache 2 License) & Free and backed by MetaMagic Global Inc",
|
||||||
|
@ -288,11 +288,6 @@
|
|||||||
"title": "Internationalization (i18n)",
|
"title": "Internationalization (i18n)",
|
||||||
"tooltip": "Translate the app's template text into multiple languages."
|
"tooltip": "Translate the app's template text into multiple languages."
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"url": "guide/language-service",
|
|
||||||
"title": "Language Service",
|
|
||||||
"tooltip": "Use Angular Language Service to speed up dev time."
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"url": "guide/security",
|
"url": "guide/security",
|
||||||
"title": "Security",
|
"title": "Security",
|
||||||
@ -385,6 +380,12 @@
|
|||||||
"title": "API",
|
"title": "API",
|
||||||
"tooltip": "Details of the Angular classes and values.",
|
"tooltip": "Details of the Angular classes and values.",
|
||||||
"url": "api"
|
"url": "api"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"url": "guide/change-log",
|
||||||
|
"title": "Change Log",
|
||||||
|
"tooltip": "Angular Documentation Change Log",
|
||||||
|
"hidden": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
@ -418,7 +419,7 @@
|
|||||||
"title": "Help",
|
"title": "Help",
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{
|
||||||
"url": "http://stackoverflow.com/questions/tagged/angular2",
|
"url": "https://stackoverflow.com/questions/tagged/angular",
|
||||||
"title": "Stack Overflow",
|
"title": "Stack Overflow",
|
||||||
"tooltip": "Stack Overflow: where the community answers your technical Angular questions."
|
"tooltip": "Stack Overflow: where the community answers your technical Angular questions."
|
||||||
},
|
},
|
||||||
|
@ -92,4 +92,12 @@ describe('site App', function() {
|
|||||||
// Todo: add test to confirm tracking URL when navigate.
|
// Todo: add test to confirm tracking URL when navigate.
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('search', () => {
|
||||||
|
it('should find pages when searching by a partial word in the title', () => {
|
||||||
|
page.enterSearch('ngCont');
|
||||||
|
expect(page.getSearchResults().map(link => link.getText())).toContain('NgControl');
|
||||||
|
page.enterSearch('accessor');
|
||||||
|
expect(page.getSearchResults().map(link => link.getText())).toContain('ControlValueAccessor');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { browser, element, by, promise, ElementFinder } from 'protractor';
|
import { browser, element, by, promise, ElementFinder, ExpectedConditions } from 'protractor';
|
||||||
|
|
||||||
const githubRegex = /https:\/\/github.com\/angular\/angular\//;
|
const githubRegex = /https:\/\/github.com\/angular\/angular\//;
|
||||||
|
|
||||||
@ -50,6 +50,18 @@ export class SitePage {
|
|||||||
return browser.executeScript('window.scrollTo(0, document.body.scrollHeight)');
|
return browser.executeScript('window.scrollTo(0, document.body.scrollHeight)');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enterSearch(query: string) {
|
||||||
|
const input = element(by.css('.search-container input[type=search]'));
|
||||||
|
input.clear();
|
||||||
|
input.sendKeys(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSearchResults() {
|
||||||
|
const results = element.all(by.css('.search-results li'));
|
||||||
|
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace the ambient Google Analytics tracker with homebrew spy
|
* Replace the ambient Google Analytics tracker with homebrew spy
|
||||||
* don't send commands to GA during e2e testing!
|
* don't send commands to GA during e2e testing!
|
||||||
|
@ -82,7 +82,7 @@
|
|||||||
"concurrently": "^3.4.0",
|
"concurrently": "^3.4.0",
|
||||||
"cross-spawn": "^5.1.0",
|
"cross-spawn": "^5.1.0",
|
||||||
"dgeni": "^0.4.7",
|
"dgeni": "^0.4.7",
|
||||||
"dgeni-packages": "^0.20.0",
|
"dgeni-packages": "^0.21.2",
|
||||||
"entities": "^1.1.1",
|
"entities": "^1.1.1",
|
||||||
"eslint": "^3.19.0",
|
"eslint": "^3.19.0",
|
||||||
"eslint-plugin-jasmine": "^2.2.0",
|
"eslint-plugin-jasmine": "^2.2.0",
|
||||||
|
@ -2,13 +2,9 @@
|
|||||||
|
|
||||||
set -u -e -o pipefail
|
set -u -e -o pipefail
|
||||||
|
|
||||||
declare -A payloadLimits
|
declare -A limitUncompressed
|
||||||
payloadLimits["aio", "uncompressed", "inline"]=1600
|
limitUncompressed=(["inline"]=1600 ["main"]=525000 ["polyfills"]=38000)
|
||||||
payloadLimits["aio", "uncompressed", "main"]=525000
|
declare -A limitGzip7
|
||||||
payloadLimits["aio", "uncompressed", "polyfills"]=38000
|
limitGzip7=(["inline"]=1000 ["main"]=127000 ["polyfills"]=12500)
|
||||||
payloadLimits["aio", "gzip7", "inline"]=1000
|
declare -A limitGzip9
|
||||||
payloadLimits["aio", "gzip7", "main"]=127000
|
limitGzip9=(["inline"]=1000 ["main"]=127000 ["polyfills"]=12500)
|
||||||
payloadLimits["aio", "gzip7", "polyfills"]=12500
|
|
||||||
payloadLimits["aio", "gzip9", "inline"]=1000
|
|
||||||
payloadLimits["aio", "gzip9", "main"]=127000
|
|
||||||
payloadLimits["aio", "gzip9", "polyfills"]=12500
|
|
||||||
|
@ -4,10 +4,79 @@ set -eu -o pipefail
|
|||||||
|
|
||||||
readonly thisDir=$(cd $(dirname $0); pwd)
|
readonly thisDir=$(cd $(dirname $0); pwd)
|
||||||
readonly parentDir=$(dirname $thisDir)
|
readonly parentDir=$(dirname $thisDir)
|
||||||
|
readonly PROJECT_NAME="angular-payload-size"
|
||||||
|
|
||||||
# Track payload size functions
|
|
||||||
source ../scripts/ci/payload-size.sh
|
|
||||||
source ${thisDir}/_payload-limits.sh
|
source ${thisDir}/_payload-limits.sh
|
||||||
|
|
||||||
trackPayloadSize "aio" "dist/*.bundle.js" true true
|
failed=false
|
||||||
|
payloadData=""
|
||||||
|
for filename in dist/*.bundle.js; do
|
||||||
|
size=$(stat -c%s "$filename")
|
||||||
|
label=$(echo "$filename" | sed "s/.*\///" | sed "s/\..*//")
|
||||||
|
payloadData="$payloadData\"uncompressed/$label\": $size, "
|
||||||
|
|
||||||
|
|
||||||
|
gzip -7 $filename -c >> "${filename}7.gz"
|
||||||
|
size7=$(stat -c%s "${filename}7.gz")
|
||||||
|
payloadData="$payloadData\"gzip7/$label\": $size7, "
|
||||||
|
|
||||||
|
gzip -9 $filename -c >> "${filename}9.gz"
|
||||||
|
size9=$(stat -c%s "${filename}9.gz")
|
||||||
|
payloadData="$payloadData\"gzip9/$label\": $size9, "
|
||||||
|
|
||||||
|
if [[ $size -gt ${limitUncompressed[$label]} ]]; then
|
||||||
|
failed=true
|
||||||
|
echo "Uncompressed $label size is $size which is greater than ${limitUncompressed[$label]}"
|
||||||
|
elif [[ $size7 -gt ${limitGzip7[$label]} ]]; then
|
||||||
|
failed=true
|
||||||
|
echo "Gzip7 $label size is $size7 which is greater than ${limitGzip7[$label]}"
|
||||||
|
elif [[ $size9 -gt ${limitGzip9[$label]} ]]; then
|
||||||
|
failed=true
|
||||||
|
echo "Gzip9 $label size is $size9 which is greater than ${limitGzip9[$label]}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Add Timestamp
|
||||||
|
timestamp=$(date +%s)
|
||||||
|
payloadData="$payloadData\"timestamp\": $timestamp, "
|
||||||
|
|
||||||
|
# Add change source: application, dependencies, or 'application+dependencies'
|
||||||
|
applicationChanged=false
|
||||||
|
dependenciesChanged=false
|
||||||
|
if [[ $(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir | grep -v aio/yarn.lock | grep -v content) ]]; then
|
||||||
|
applicationChanged=true
|
||||||
|
fi
|
||||||
|
if [[ $(git diff --name-only $TRAVIS_COMMIT_RANGE $parentDir/yarn.lock) ]]; then
|
||||||
|
dependenciesChanged=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if $dependenciesChanged && $applicationChanged; then
|
||||||
|
change='application+dependencies'
|
||||||
|
elif $dependenciesChanged; then
|
||||||
|
# only yarn.lock changed
|
||||||
|
change='dependencies'
|
||||||
|
elif $applicationChanged; then
|
||||||
|
change='application'
|
||||||
|
else
|
||||||
|
# Nothing changed in aio/
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
message=$(echo $TRAVIS_COMMIT_MESSAGE | sed 's/"/\\"/g' | sed 's/\\/\\\\/g')
|
||||||
|
payloadData="$payloadData\"change\": \"$change\", \"message\": \"$message\""
|
||||||
|
|
||||||
|
payloadData="{${payloadData}}"
|
||||||
|
|
||||||
|
echo $payloadData
|
||||||
|
|
||||||
|
if [[ "$TRAVIS_PULL_REQUEST" == "false" ]]; then
|
||||||
|
readonly safeBranchName=$(echo $TRAVIS_BRANCH | sed -e 's/\./_/g')
|
||||||
|
readonly dbPath=/payload/aio/$safeBranchName/$TRAVIS_COMMIT
|
||||||
|
|
||||||
|
# WARNING: FIREBASE_TOKEN should NOT be printed.
|
||||||
|
set +x
|
||||||
|
firebase database:update --data "$payloadData" --project $PROJECT_NAME --confirm --token "$ANGULAR_PAYLOAD_FIREBASE_TOKEN" $dbPath
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $failed = true ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
@ -9,7 +9,18 @@ var SEARCH_TERMS_URL = '/generated/docs/app/search-data.json';
|
|||||||
importScripts('/assets/js/lunr.min.js');
|
importScripts('/assets/js/lunr.min.js');
|
||||||
|
|
||||||
var index;
|
var index;
|
||||||
var pages = {};
|
var pages /* : SearchInfo */ = {};
|
||||||
|
|
||||||
|
// interface SearchInfo {
|
||||||
|
// [key: string]: PageInfo;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// interface PageInfo {
|
||||||
|
// path: string;
|
||||||
|
// type: string,
|
||||||
|
// titleWords: string;
|
||||||
|
// keyWords: string;
|
||||||
|
// }
|
||||||
|
|
||||||
self.onmessage = handleMessage;
|
self.onmessage = handleMessage;
|
||||||
|
|
||||||
@ -49,15 +60,7 @@ function handleMessage(message) {
|
|||||||
// Use XHR to make a request to the server
|
// Use XHR to make a request to the server
|
||||||
function makeRequest(url, callback) {
|
function makeRequest(url, callback) {
|
||||||
|
|
||||||
// The JSON file that is loaded should be an array of SearchTerms:
|
// The JSON file that is loaded should be an array of PageInfo:
|
||||||
//
|
|
||||||
// export interface SearchTerms {
|
|
||||||
// path: string;
|
|
||||||
// type: string,
|
|
||||||
// titleWords: string;
|
|
||||||
// keyWords: string;
|
|
||||||
// }
|
|
||||||
|
|
||||||
var searchDataRequest = new XMLHttpRequest();
|
var searchDataRequest = new XMLHttpRequest();
|
||||||
searchDataRequest.onload = function() {
|
searchDataRequest.onload = function() {
|
||||||
callback(JSON.parse(this.responseText));
|
callback(JSON.parse(this.responseText));
|
||||||
@ -68,11 +71,11 @@ function makeRequest(url, callback) {
|
|||||||
|
|
||||||
|
|
||||||
// Create the search index from the searchInfo which contains the information about each page to be indexed
|
// Create the search index from the searchInfo which contains the information about each page to be indexed
|
||||||
function loadIndex(searchInfo) {
|
function loadIndex(searchInfo /*: SearchInfo */) {
|
||||||
return function(index) {
|
return function(index) {
|
||||||
// Store the pages data to be used in mapping query results back to pages
|
// Store the pages data to be used in mapping query results back to pages
|
||||||
// Add search terms from each page to the search index
|
// Add search terms from each page to the search index
|
||||||
searchInfo.forEach(function(page) {
|
searchInfo.forEach(function(page /*: PageInfo */) {
|
||||||
index.add(page);
|
index.add(page);
|
||||||
pages[page.path] = page;
|
pages[page.path] = page;
|
||||||
});
|
});
|
||||||
@ -81,7 +84,19 @@ function loadIndex(searchInfo) {
|
|||||||
|
|
||||||
// Query the index and return the processed results
|
// Query the index and return the processed results
|
||||||
function queryIndex(query) {
|
function queryIndex(query) {
|
||||||
var results = index.search(query);
|
try {
|
||||||
// Only return the array of paths to pages
|
if (query.length) {
|
||||||
|
// Add a relaxed search in the title for the first word in the query
|
||||||
|
// E.g. if the search is "ngCont guide" then we search for "ngCont guide titleWords:ngCont*"
|
||||||
|
var titleQuery = 'titleWords:*' + query.split(' ', 1)[0] + '*';
|
||||||
|
var results = index.search(query + ' ' + titleQuery);
|
||||||
|
// Map the hits into info about each page to be returned as results
|
||||||
return results.map(function(hit) { return pages[hit.ref]; });
|
return results.map(function(hit) { return pages[hit.ref]; });
|
||||||
}
|
}
|
||||||
|
} catch(e) {
|
||||||
|
// If the search query cannot be parsed the index throws an error
|
||||||
|
// Log it and recover
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
@ -104,7 +104,7 @@ footer::after {
|
|||||||
right: 0;
|
right: 0;
|
||||||
background:
|
background:
|
||||||
url('../src/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 0 left 0 repeat,
|
url('../src/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 0 left 0 repeat,
|
||||||
url('../src/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 80px left 168px repeat;
|
url('../src/assets/images/logos/angular/angular_whiteTransparent_withMargin.png') top 80px left 160px repeat;
|
||||||
opacity: 0.05;
|
opacity: 0.05;
|
||||||
background-size: 320px auto;
|
background-size: 320px auto;
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,12 @@ const ANGULAR_PACKAGES = [
|
|||||||
'platform-browser-dynamic',
|
'platform-browser-dynamic',
|
||||||
'platform-server',
|
'platform-server',
|
||||||
'router',
|
'router',
|
||||||
'tsc-wrapped',
|
|
||||||
'upgrade',
|
'upgrade',
|
||||||
];
|
];
|
||||||
|
const ANGULAR_TOOLS_PACKAGES_PATH = path.resolve(ANGULAR_DIST_PATH, 'tools', '@angular');
|
||||||
|
const ANGULAR_TOOLS_PACKAGES = [
|
||||||
|
'tsc-wrapped'
|
||||||
|
];
|
||||||
|
|
||||||
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
const EXAMPLE_CONFIG_FILENAME = 'example-config.json';
|
||||||
|
|
||||||
@ -60,6 +63,7 @@ class ExampleBoilerPlate {
|
|||||||
// Replace the Angular packages with those from the dist folder, if necessary
|
// Replace the Angular packages with those from the dist folder, if necessary
|
||||||
if (useLocal) {
|
if (useLocal) {
|
||||||
ANGULAR_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_PACKAGES_PATH, packageName));
|
ANGULAR_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_PACKAGES_PATH, packageName));
|
||||||
|
ANGULAR_TOOLS_PACKAGES.forEach(packageName => this.overridePackage(ANGULAR_TOOLS_PACKAGES_PATH, packageName));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get all the examples folders, indicated by those that contain a `example-config.json` file
|
// Get all the examples folders, indicated by those that contain a `example-config.json` file
|
||||||
|
@ -32,7 +32,7 @@ describe('example-boilerplate tool', () => {
|
|||||||
// for example
|
// for example
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
|
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
|
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'tsc-wrapped');
|
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/tools/@angular'), 'tsc-wrapped');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process all the example folders', () => {
|
it('should process all the example folders', () => {
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
|
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
|
||||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||||
|
'@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
|
||||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||||
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
||||||
@ -31,6 +32,7 @@
|
|||||||
|
|
||||||
// other libraries
|
// other libraries
|
||||||
'rxjs': 'npm:rxjs',
|
'rxjs': 'npm:rxjs',
|
||||||
|
'tslib': 'npm:tslib/tslib.js',
|
||||||
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
|
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js'
|
||||||
},
|
},
|
||||||
// packages tells the System loader how to load when no filename and/or no extension
|
// packages tells the System loader how to load when no filename and/or no extension
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
'@angular/animations/browser': 'ng:animations-builds/master/bundles/animations-browser.umd.js',
|
'@angular/animations/browser': 'ng:animations-builds/master/bundles/animations-browser.umd.js',
|
||||||
'@angular/core': 'ng:core-builds/master/bundles/core.umd.js',
|
'@angular/core': 'ng:core-builds/master/bundles/core.umd.js',
|
||||||
'@angular/common': 'ng:common-builds/master/bundles/common.umd.js',
|
'@angular/common': 'ng:common-builds/master/bundles/common.umd.js',
|
||||||
|
'@angular/common/http': 'ng:common-builds/master/bundles/common-http.umd.js',
|
||||||
'@angular/compiler': 'ng:compiler-builds/master/bundles/compiler.umd.js',
|
'@angular/compiler': 'ng:compiler-builds/master/bundles/compiler.umd.js',
|
||||||
'@angular/platform-browser': 'ng:platform-browser-builds/master/bundles/platform-browser.umd.js',
|
'@angular/platform-browser': 'ng:platform-browser-builds/master/bundles/platform-browser.umd.js',
|
||||||
'@angular/platform-browser/animations': 'ng:animations-builds/master/bundles/platform-browser-animations.umd.js',
|
'@angular/platform-browser/animations': 'ng:animations-builds/master/bundles/platform-browser-animations.umd.js',
|
||||||
@ -56,6 +57,7 @@
|
|||||||
// angular testing umd bundles (overwrite the shim mappings)
|
// angular testing umd bundles (overwrite the shim mappings)
|
||||||
'@angular/core/testing': 'ng:core-builds/master/bundles/core-testing.umd.js',
|
'@angular/core/testing': 'ng:core-builds/master/bundles/core-testing.umd.js',
|
||||||
'@angular/common/testing': 'ng:common-builds/master/bundles/common-testing.umd.js',
|
'@angular/common/testing': 'ng:common-builds/master/bundles/common-testing.umd.js',
|
||||||
|
'@angular/common/http/testing': 'ng:common-builds/master/bundles/common-http-testing.umd.js',
|
||||||
'@angular/compiler/testing': 'ng:compiler-builds/master/bundles/compiler-testing.umd.js',
|
'@angular/compiler/testing': 'ng:compiler-builds/master/bundles/compiler-testing.umd.js',
|
||||||
'@angular/platform-browser/testing': 'ng:platform-browser-builds/master/bundles/platform-browser-testing.umd.js',
|
'@angular/platform-browser/testing': 'ng:platform-browser-builds/master/bundles/platform-browser-testing.umd.js',
|
||||||
'@angular/platform-browser-dynamic/testing': 'ng:platform-browser-dynamic-builds/master/bundles/platform-browser-dynamic-testing.umd.js',
|
'@angular/platform-browser-dynamic/testing': 'ng:platform-browser-dynamic-builds/master/bundles/platform-browser-dynamic-testing.umd.js',
|
||||||
@ -65,6 +67,7 @@
|
|||||||
|
|
||||||
// other libraries
|
// other libraries
|
||||||
'rxjs': 'npm:rxjs@5.0.1',
|
'rxjs': 'npm:rxjs@5.0.1',
|
||||||
|
'tslib': 'npm:tslib/tslib.js',
|
||||||
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
|
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
|
||||||
'ts': 'npm:plugin-typescript@5.2.7/lib/plugin.js',
|
'ts': 'npm:plugin-typescript@5.2.7/lib/plugin.js',
|
||||||
'typescript': 'npm:typescript@2.3.2/lib/typescript.js',
|
'typescript': 'npm:typescript@2.3.2/lib/typescript.js',
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
|
'@angular/animations/browser': 'npm:@angular/animations/bundles/animations-browser.umd.js',
|
||||||
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
'@angular/core': 'npm:@angular/core/bundles/core.umd.js',
|
||||||
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
'@angular/common': 'npm:@angular/common/bundles/common.umd.js',
|
||||||
|
'@angular/common/http': 'npm:@angular/common/bundles/common-http.umd.js',
|
||||||
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
'@angular/compiler': 'npm:@angular/compiler/bundles/compiler.umd.js',
|
||||||
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
'@angular/platform-browser': 'npm:@angular/platform-browser/bundles/platform-browser.umd.js',
|
||||||
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
'@angular/platform-browser/animations': 'npm:@angular/platform-browser/bundles/platform-browser-animations.umd.js',
|
||||||
@ -52,6 +53,7 @@
|
|||||||
|
|
||||||
// other libraries
|
// other libraries
|
||||||
'rxjs': 'npm:rxjs@5.0.1',
|
'rxjs': 'npm:rxjs@5.0.1',
|
||||||
|
'tslib': 'npm:tslib/tslib.js',
|
||||||
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
|
'angular-in-memory-web-api': 'npm:angular-in-memory-web-api/bundles/in-memory-web-api.umd.js',
|
||||||
'ts': 'npm:plugin-typescript@5.2.7/lib/plugin.js',
|
'ts': 'npm:plugin-typescript@5.2.7/lib/plugin.js',
|
||||||
'typescript': 'npm:typescript@2.3.2/lib/typescript.js',
|
'typescript': 'npm:typescript@2.3.2/lib/typescript.js',
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"@angular/router": "~4.3.1",
|
"@angular/router": "~4.3.1",
|
||||||
"@angular/tsc-wrapped": "~4.3.1",
|
"@angular/tsc-wrapped": "~4.3.1",
|
||||||
"@angular/upgrade": "~4.3.1",
|
"@angular/upgrade": "~4.3.1",
|
||||||
"angular-in-memory-web-api": "~0.3.2",
|
"angular-in-memory-web-api": "~0.4.0",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
"rxjs": "^5.1.0",
|
"rxjs": "^5.1.0",
|
||||||
"systemjs": "0.19.39",
|
"systemjs": "0.19.39",
|
||||||
|
@ -325,9 +325,9 @@ amdefine@>=0.0.4:
|
|||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
|
||||||
|
|
||||||
angular-in-memory-web-api@~0.3.2:
|
angular-in-memory-web-api@~0.4.0:
|
||||||
version "0.3.2"
|
version "0.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/angular-in-memory-web-api/-/angular-in-memory-web-api-0.3.2.tgz#8836d9e2534d37b728f3cb5a1caf6fe1e7fbbecd"
|
resolved "https://registry.yarnpkg.com/angular-in-memory-web-api/-/angular-in-memory-web-api-0.4.0.tgz#996715f37d8a4e659e154fedf76c4726470cb8d8"
|
||||||
|
|
||||||
angular2-template-loader@^0.6.0:
|
angular2-template-loader@^0.6.0:
|
||||||
version "0.6.2"
|
version "0.6.2"
|
||||||
@ -5163,18 +5163,18 @@ request-progress@~2.0.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
throttleit "^1.0.0"
|
throttleit "^1.0.0"
|
||||||
|
|
||||||
request@2, request@^2.72.0, request@^2.79.0, request@^2.81.0:
|
request@2, request@^2.72.0, request@^2.78.0, request@^2.79.0, request@~2.79.0:
|
||||||
version "2.81.0"
|
version "2.79.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
|
||||||
dependencies:
|
dependencies:
|
||||||
aws-sign2 "~0.6.0"
|
aws-sign2 "~0.6.0"
|
||||||
aws4 "^1.2.1"
|
aws4 "^1.2.1"
|
||||||
caseless "~0.12.0"
|
caseless "~0.11.0"
|
||||||
combined-stream "~1.0.5"
|
combined-stream "~1.0.5"
|
||||||
extend "~3.0.0"
|
extend "~3.0.0"
|
||||||
forever-agent "~0.6.1"
|
forever-agent "~0.6.1"
|
||||||
form-data "~2.1.1"
|
form-data "~2.1.1"
|
||||||
har-validator "~4.2.1"
|
har-validator "~2.0.6"
|
||||||
hawk "~3.1.3"
|
hawk "~3.1.3"
|
||||||
http-signature "~1.1.0"
|
http-signature "~1.1.0"
|
||||||
is-typedarray "~1.0.0"
|
is-typedarray "~1.0.0"
|
||||||
@ -5182,12 +5182,10 @@ request@2, request@^2.72.0, request@^2.79.0, request@^2.81.0:
|
|||||||
json-stringify-safe "~5.0.1"
|
json-stringify-safe "~5.0.1"
|
||||||
mime-types "~2.1.7"
|
mime-types "~2.1.7"
|
||||||
oauth-sign "~0.8.1"
|
oauth-sign "~0.8.1"
|
||||||
performance-now "^0.2.0"
|
qs "~6.3.0"
|
||||||
qs "~6.4.0"
|
|
||||||
safe-buffer "^5.0.1"
|
|
||||||
stringstream "~0.0.4"
|
stringstream "~0.0.4"
|
||||||
tough-cookie "~2.3.0"
|
tough-cookie "~2.3.0"
|
||||||
tunnel-agent "^0.6.0"
|
tunnel-agent "~0.4.1"
|
||||||
uuid "^3.0.0"
|
uuid "^3.0.0"
|
||||||
|
|
||||||
request@2.78.0:
|
request@2.78.0:
|
||||||
@ -5215,18 +5213,18 @@ request@2.78.0:
|
|||||||
tough-cookie "~2.3.0"
|
tough-cookie "~2.3.0"
|
||||||
tunnel-agent "~0.4.1"
|
tunnel-agent "~0.4.1"
|
||||||
|
|
||||||
request@^2.78.0, request@~2.79.0:
|
request@^2.81.0:
|
||||||
version "2.79.0"
|
version "2.81.0"
|
||||||
resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de"
|
resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0"
|
||||||
dependencies:
|
dependencies:
|
||||||
aws-sign2 "~0.6.0"
|
aws-sign2 "~0.6.0"
|
||||||
aws4 "^1.2.1"
|
aws4 "^1.2.1"
|
||||||
caseless "~0.11.0"
|
caseless "~0.12.0"
|
||||||
combined-stream "~1.0.5"
|
combined-stream "~1.0.5"
|
||||||
extend "~3.0.0"
|
extend "~3.0.0"
|
||||||
forever-agent "~0.6.1"
|
forever-agent "~0.6.1"
|
||||||
form-data "~2.1.1"
|
form-data "~2.1.1"
|
||||||
har-validator "~2.0.6"
|
har-validator "~4.2.1"
|
||||||
hawk "~3.1.3"
|
hawk "~3.1.3"
|
||||||
http-signature "~1.1.0"
|
http-signature "~1.1.0"
|
||||||
is-typedarray "~1.0.0"
|
is-typedarray "~1.0.0"
|
||||||
@ -5234,10 +5232,12 @@ request@^2.78.0, request@~2.79.0:
|
|||||||
json-stringify-safe "~5.0.1"
|
json-stringify-safe "~5.0.1"
|
||||||
mime-types "~2.1.7"
|
mime-types "~2.1.7"
|
||||||
oauth-sign "~0.8.1"
|
oauth-sign "~0.8.1"
|
||||||
qs "~6.3.0"
|
performance-now "^0.2.0"
|
||||||
|
qs "~6.4.0"
|
||||||
|
safe-buffer "^5.0.1"
|
||||||
stringstream "~0.0.4"
|
stringstream "~0.0.4"
|
||||||
tough-cookie "~2.3.0"
|
tough-cookie "~2.3.0"
|
||||||
tunnel-agent "~0.4.1"
|
tunnel-agent "^0.6.0"
|
||||||
uuid "^3.0.0"
|
uuid "^3.0.0"
|
||||||
|
|
||||||
require-directory@^2.1.1:
|
require-directory@^2.1.1:
|
||||||
|
@ -27,7 +27,11 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
|
|||||||
.processor(require('./processors/simplifyMemberAnchors'))
|
.processor(require('./processors/simplifyMemberAnchors'))
|
||||||
|
|
||||||
// Where do we get the source files?
|
// Where do we get the source files?
|
||||||
.config(function(readTypeScriptModules, readFilesProcessor, collectExamples) {
|
.config(function(readTypeScriptModules, readFilesProcessor, collectExamples, tsParser) {
|
||||||
|
|
||||||
|
// Tell TypeScript how to load modules that start with with `@angular`
|
||||||
|
tsParser.options.paths = { '@angular/*': [API_SOURCE_PATH + '/*'] };
|
||||||
|
tsParser.options.baseUrl = '.';
|
||||||
|
|
||||||
// API files are typescript
|
// API files are typescript
|
||||||
readTypeScriptModules.basePath = API_SOURCE_PATH;
|
readTypeScriptModules.basePath = API_SOURCE_PATH;
|
||||||
@ -124,4 +128,5 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage])
|
|||||||
convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
convertToJsonProcessor.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
||||||
postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
postProcessHtml.docTypes = convertToJsonProcessor.docTypes.concat(DOCS_TO_CONVERT);
|
||||||
autoLinkCode.docTypes = DOCS_TO_CONVERT;
|
autoLinkCode.docTypes = DOCS_TO_CONVERT;
|
||||||
|
autoLinkCode.codeElements = ['code', 'code-example', 'code-pane'];
|
||||||
});
|
});
|
||||||
|
@ -3,34 +3,69 @@ const is = require('hast-util-is-element');
|
|||||||
const textContent = require('hast-util-to-string');
|
const textContent = require('hast-util-to-string');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Automatically add in a link to the relevant document for simple
|
* Automatically add in a link to the relevant document for code blocks.
|
||||||
* code blocks, e.g. `<code>MyClass</code>` becomes
|
* E.g. `<code>MyClass</code>` becomes `<code><a href="path/to/myclass">MyClass</a></code>`
|
||||||
* `<code><a href="path/to/myclass">MyClass</a></code>`
|
|
||||||
*
|
*
|
||||||
* @property docTypes an array of strings. Only docs that have one of these docTypes
|
* @property docTypes an array of strings.
|
||||||
* will be linked to.
|
* Only docs that have one of these docTypes will be linked to.
|
||||||
* Usually set to the API exported docTypes, e.g. "class", "function", "directive", etc.
|
* Usually set to the API exported docTypes, e.g. "class", "function", "directive", etc.
|
||||||
|
*
|
||||||
|
* @property codeElements an array of strings.
|
||||||
|
* Only text contained in these elements will be linked to.
|
||||||
|
* Usually set to "code" but also "code-example" for angular.io.
|
||||||
*/
|
*/
|
||||||
module.exports = function autoLinkCode(getDocFromAlias) {
|
module.exports = function autoLinkCode(getDocFromAlias) {
|
||||||
autoLinkCodeImpl.docTypes = [];
|
autoLinkCodeImpl.docTypes = [];
|
||||||
|
autoLinkCodeImpl.codeElements = ['code'];
|
||||||
return autoLinkCodeImpl;
|
return autoLinkCodeImpl;
|
||||||
|
|
||||||
function autoLinkCodeImpl() {
|
function autoLinkCodeImpl() {
|
||||||
return (ast) => {
|
return (ast) => {
|
||||||
visit(ast, (node, ancestors) => {
|
visit(ast, 'element', (node, ancestors) => {
|
||||||
if (is(node, 'code') && ancestors.every(ancestor => !is(ancestor, 'a'))) {
|
// Only interested in code elements that are not inside links
|
||||||
const docs = getDocFromAlias(textContent(node));
|
if (autoLinkCodeImpl.codeElements.some(elementType => is(node, elementType)) &&
|
||||||
if (docs.length === 1 && autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1) {
|
ancestors.every(ancestor => !is(ancestor, 'a'))) {
|
||||||
const link = {
|
visit(node, 'text', (node, ancestors) => {
|
||||||
type: 'element',
|
// Only interested in text nodes that are not inside links
|
||||||
tagName: 'a',
|
if (ancestors.every(ancestor => !is(ancestor, 'a'))) {
|
||||||
properties: { href: docs[0].path },
|
|
||||||
children: node.children
|
const parent = ancestors[ancestors.length-1];
|
||||||
};
|
const index = parent.children.indexOf(node);
|
||||||
node.children = [link];
|
|
||||||
|
// Can we convert the whole text node into a doc link?
|
||||||
|
const docs = getDocFromAlias(node.value);
|
||||||
|
if (foundValidDoc(docs)) {
|
||||||
|
parent.children.splice(index, 1, createLinkNode(docs[0], node.value));
|
||||||
|
} else {
|
||||||
|
// Parse the text for words that we can convert to links
|
||||||
|
const nodes = textContent(node).split(/([A-Za-z0-9_]+)/)
|
||||||
|
.filter(word => word.length)
|
||||||
|
.map(word => {
|
||||||
|
const docs = getDocFromAlias(word);
|
||||||
|
return foundValidDoc(docs) ?
|
||||||
|
createLinkNode(docs[0], word) : // Create a link wrapping the text node.
|
||||||
|
{ type: 'text', value: word }; // this is just text so push a new text node
|
||||||
|
});
|
||||||
|
|
||||||
|
// Replace the text node with the links and leftover text nodes
|
||||||
|
Array.prototype.splice.apply(parent.children, [index, 1].concat(nodes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function foundValidDoc(docs) {
|
||||||
|
return docs.length === 1 && autoLinkCodeImpl.docTypes.indexOf(docs[0].docType) !== -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLinkNode(doc, text) {
|
||||||
|
return {
|
||||||
|
type: 'element',
|
||||||
|
tagName: 'a',
|
||||||
|
properties: { href: doc.path, class: 'code-anchor' },
|
||||||
|
children: [{ type: 'text', value: text }]
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@ describe('autoLinkCode post-processor', () => {
|
|||||||
const dgeni = new Dgeni([testPackage]);
|
const dgeni = new Dgeni([testPackage]);
|
||||||
const injector = dgeni.configureInjector();
|
const injector = dgeni.configureInjector();
|
||||||
autoLinkCode = injector.get('autoLinkCode');
|
autoLinkCode = injector.get('autoLinkCode');
|
||||||
autoLinkCode.docTypes = ['class', 'pipe'];
|
autoLinkCode.docTypes = ['class', 'pipe', 'function', 'const'];
|
||||||
aliasMap = injector.get('aliasMap');
|
aliasMap = injector.get('aliasMap');
|
||||||
processor = injector.get('postProcessHtml');
|
processor = injector.get('postProcessHtml');
|
||||||
processor.docTypes = ['test-doc'];
|
processor.docTypes = ['test-doc'];
|
||||||
@ -20,14 +20,14 @@ describe('autoLinkCode post-processor', () => {
|
|||||||
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
const doc = { docType: 'test-doc', renderedContent: '<code>MyClass</code>' };
|
const doc = { docType: 'test-doc', renderedContent: '<code>MyClass</code>' };
|
||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass">MyClass</a></code>');
|
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass" class="code-anchor">MyClass</a></code>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should insert an anchor into every code item that matches an alias of an API doc', () => {
|
it('should insert an anchor into every code item that matches an alias of an API doc', () => {
|
||||||
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass', 'foo.MyClass'], path: 'a/b/myclass' });
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass', 'foo.MyClass'], path: 'a/b/myclass' });
|
||||||
const doc = { docType: 'test-doc', renderedContent: '<code>foo.MyClass</code>' };
|
const doc = { docType: 'test-doc', renderedContent: '<code>foo.MyClass</code>' };
|
||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass">foo.MyClass</a></code>');
|
expect(doc.renderedContent).toEqual('<code><a href="a/b/myclass" class="code-anchor">foo.MyClass</a></code>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should ignore code items that do not match a link to an API doc', () => {
|
it('should ignore code items that do not match a link to an API doc', () => {
|
||||||
@ -43,4 +43,35 @@ describe('autoLinkCode post-processor', () => {
|
|||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.renderedContent).toEqual('<a href="..."><div><code>MyClass</code></div></a>');
|
expect(doc.renderedContent).toEqual('<a href="..."><div><code>MyClass</code></div></a>');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should ignore code items match an API doc but are not in the list of acceptable docTypes', () => {
|
||||||
|
aliasMap.addDoc({ docType: 'directive', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code>MyClass</code>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code>MyClass</code>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert anchors for individual text nodes within a code block', () => {
|
||||||
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code><span>MyClass</span><span>MyClass</span></code>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code><span><a href="a/b/myclass" class="code-anchor">MyClass</a></span><span><a href="a/b/myclass" class="code-anchor">MyClass</a></span></code>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should insert anchors for words that match within text nodes in a code block', () => {
|
||||||
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
aliasMap.addDoc({ docType: 'function', id: 'myFunc', aliases: ['myFunc'], path: 'ng/myfunc' });
|
||||||
|
aliasMap.addDoc({ docType: 'const', id: 'MY_CONST', aliases: ['MY_CONST'], path: 'ng/my_const' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code>myFunc() {\n return new MyClass(MY_CONST);\n}</code>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code><a href="ng/myfunc" class="code-anchor">myFunc</a>() {\n return new <a href="a/b/myclass" class="code-anchor">MyClass</a>(<a href="ng/my_const" class="code-anchor">MY_CONST</a>);\n}</code>');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should work with custom elements', () => {
|
||||||
|
autoLinkCode.codeElements = ['code-example'];
|
||||||
|
aliasMap.addDoc({ docType: 'class', id: 'MyClass', aliases: ['MyClass'], path: 'a/b/myclass' });
|
||||||
|
const doc = { docType: 'test-doc', renderedContent: '<code-example>MyClass</code-example>' };
|
||||||
|
processor.$process([doc]);
|
||||||
|
expect(doc.renderedContent).toEqual('<code-example><a href="a/b/myclass" class="code-anchor">MyClass</a></code-example>');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -34,5 +34,5 @@ function getText(h1) {
|
|||||||
(node.properties.ariaHidden === 'true' || node.properties['aria-hidden'] === 'true')
|
(node.properties.ariaHidden === 'true' || node.properties['aria-hidden'] === 'true')
|
||||||
));
|
));
|
||||||
|
|
||||||
return cleaned ? toString(cleaned) : '';
|
return toString(cleaned);
|
||||||
}
|
}
|
@ -69,14 +69,4 @@ describe('h1Checker postprocessor', () => {
|
|||||||
processor.$process([doc]);
|
processor.$process([doc]);
|
||||||
expect(doc.vFile.title).toEqual('What is Angular?');
|
expect(doc.vFile.title).toEqual('What is Angular?');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not break if the h1 is empty (except for an aria-hidden anchor)', () => {
|
|
||||||
const doc = {
|
|
||||||
docType: 'a',
|
|
||||||
renderedContent: `
|
|
||||||
<h1><a aria-hidden="true"></a></h1>
|
|
||||||
`
|
|
||||||
};
|
|
||||||
expect(() => processor.$process([doc])).not.toThrow();
|
|
||||||
});
|
|
||||||
});
|
});
|
@ -7,8 +7,8 @@
|
|||||||
{% block additional %}{% endblock %}
|
{% block additional %}{% endblock %}
|
||||||
{% include "includes/description.html" %}
|
{% include "includes/description.html" %}
|
||||||
{$ memberHelpers.renderMemberDetails(doc.statics, 'static-members', 'static-member', 'Static Members') $}
|
{$ memberHelpers.renderMemberDetails(doc.statics, 'static-members', 'static-member', 'Static Members') $}
|
||||||
{% include "includes/constructor.html" %}
|
{% if doc.constructorDoc %}{$ memberHelpers.renderMemberDetails([doc.constructorDoc], 'constructors', 'constructor', 'Constructor') $}{% endif %}
|
||||||
{$ memberHelpers.renderMemberDetails(doc.members, 'instance-members', 'instance-member', 'Members') $}
|
{$ memberHelpers.renderMemberDetails(doc.members, 'instance-members', 'instance-member', 'Members') $}
|
||||||
{% include "includes/annotations.html" %}
|
{% block annotations %}{% include "includes/annotations.html" %}{% endblock %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -9,3 +9,5 @@
|
|||||||
{$ directiveHelper.renderBindings(doc.outputs, 'outputs', 'output', 'Outputs') $}
|
{$ directiveHelper.renderBindings(doc.outputs, 'outputs', 'output', 'Outputs') $}
|
||||||
{% include "includes/export-as.html" %}
|
{% include "includes/export-as.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block annotations %}{% endblock %}
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
<section class="annotations">
|
<section class="annotations">
|
||||||
<h2>Annotations</h2>
|
<h2>Annotations</h2>
|
||||||
{%- for decorator in doc.decorators %}
|
{%- for decorator in doc.decorators %}
|
||||||
<code-example hideCopy="true" class="no-box api-heading">@{$ decorator.name $}{$ params.paramList(decorator.arguments) $}</code-example>
|
<code-example hideCopy="true" class="no-box api-heading">@{$ decorator.name $}({$ decorator.arguments $})</code-example>
|
||||||
{% if not decorator.notYetDocumented %}{$ decorator.description | marked $}{% endif %}
|
{% if not decorator.notYetDocumented %}{$ decorator.description | marked $}{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
@ -4,10 +4,11 @@
|
|||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<code-example language="ts" hideCopy="true">
|
<code-example language="ts" hideCopy="true">
|
||||||
{$ doc.docType $} {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {
|
{$ doc.docType $} {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {
|
||||||
|
{%- if doc.constructorDoc %}{% if not doc.constructorDoc.internal %}
|
||||||
|
<a class="code-anchor" href="#{$ doc.constructorDoc.anchor $}">{$ memberHelper.renderMember(doc.constructorDoc, 1) $}</a>{% endif %}{% endif -%}
|
||||||
{%- if doc.statics.length %}{% for member in doc.statics %}{% if not member.internal %}
|
{%- if doc.statics.length %}{% for member in doc.statics %}{% if not member.internal %}
|
||||||
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif %}
|
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif -%}
|
||||||
{%- if doc.members.length %}{% for member in doc.members %}{% if not member.internal %}
|
{$ memberHelper.renderMembers(doc) $}
|
||||||
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif %}
|
|
||||||
}
|
}
|
||||||
</code-example>
|
</code-example>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
{%- if doc.constructorDoc and not doc.constructorDoc.internal %}
|
|
||||||
<section class="constructor">
|
|
||||||
<a id="{$ doc.constructorDoc.name $}"></a>
|
|
||||||
<h2>Constructor</h2>
|
|
||||||
<code-example hideCopy="true" class="no-box api-heading">{$ doc.constructorDoc.name $}{$ params.paramList(doc.constructorDoc.parameters) $}</code-example>
|
|
||||||
{% if not doc.constructorDoc.notYetDocumented %}{$ doc.constructorDoc.description | marked $}{% endif %}
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
@ -3,8 +3,7 @@
|
|||||||
<section class="decorator-overview">
|
<section class="decorator-overview">
|
||||||
<h2>Metadata Overview</h2>
|
<h2>Metadata Overview</h2>
|
||||||
<code-example language="ts" hideCopy="true">
|
<code-example language="ts" hideCopy="true">
|
||||||
@{$ doc.name $}{$ doc.typeParams | escape $}({ {% if doc.members.length %}{% for member in doc.members %}{% if not member.internal %}
|
@{$ doc.name $}{$ doc.typeParams | escape $}({ {$ memberHelper.renderMembers(doc) $}
|
||||||
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif %}
|
|
||||||
})
|
})
|
||||||
</code-example>
|
</code-example>
|
||||||
</section>
|
</section>
|
@ -3,12 +3,11 @@
|
|||||||
<section class="{$ doc.docType $}-overview">
|
<section class="{$ doc.docType $}-overview">
|
||||||
<h2>Overview</h2>
|
<h2>Overview</h2>
|
||||||
<code-example language="ts" hideCopy="true">{% for decorator in doc.decorators %}
|
<code-example language="ts" hideCopy="true">{% for decorator in doc.decorators %}
|
||||||
<a href="#annotations">@{$ decorator.name $}{$ params.paramList(decorator.arguments) $}</a>{% endfor %}
|
@{$ decorator.name $}({$ decorator.arguments $}){% endfor %}
|
||||||
class {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {
|
class {$ doc.name $}{$ doc.typeParams | escape $}{$ memberHelper.renderHeritage(doc) $} {
|
||||||
{%- if doc.statics.length %}{% for member in doc.statics %}{% if not member.internal %}
|
{%- if doc.statics.length %}{% for member in doc.statics %}{% if not member.internal %}
|
||||||
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif %}
|
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif -%}
|
||||||
{%- if doc.members.length %}{% for member in doc.members %}{% if not member.internal %}
|
{$ memberHelper.renderMembers(doc) $}
|
||||||
<a class="code-anchor" href="#{$ member.anchor $}">{$ memberHelper.renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif %}
|
|
||||||
}
|
}
|
||||||
</code-example>
|
</code-example>
|
||||||
</section>
|
</section>
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
{%- if doc.selector %}
|
{%- if doc.selector %}
|
||||||
<section class="selectors">
|
<section class="selectors">
|
||||||
<h2>Selectors</h2>
|
<h2>Selectors</h2>
|
||||||
{% for selector in doc.selector.split(',') %}
|
<code-example hideCopy="true" class="no-box api-heading selector">
|
||||||
<div class="selector">
|
{%- for selector in doc.selector.split(',') %}
|
||||||
<code>{$ selector $}</code>
|
{$ selector $}{% endfor %}
|
||||||
</div>
|
</code-example>
|
||||||
{% endfor %}
|
|
||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -2,13 +2,20 @@
|
|||||||
|
|
||||||
{%- macro renderHeritage(exportDoc) -%}
|
{%- macro renderHeritage(exportDoc) -%}
|
||||||
{%- if exportDoc.extendsClauses.length %} extends {% for clause in exportDoc.extendsClauses -%}
|
{%- if exportDoc.extendsClauses.length %} extends {% for clause in exportDoc.extendsClauses -%}
|
||||||
{$ clause $}{% if not loop.last %}, {% endif -%}
|
<a class="code-anchor" href="{$ clause.doc.path $}">{$ clause.text $}</a>{% if not loop.last %}, {% endif -%}
|
||||||
{% endfor %}{% endif %}
|
{% endfor %}{% endif %}
|
||||||
{%- if exportDoc.implementsClauses.length %} implements {% for clause in exportDoc.implementsClauses -%}
|
{%- if exportDoc.implementsClauses.length %} implements {% for clause in exportDoc.implementsClauses -%}
|
||||||
{$ clause $}{% if not loop.last %}, {% endif -%}
|
<a class="code-anchor" href="{$ clause.doc.path $}">{$ clause.text $}</a>{% if not loop.last %}, {% endif -%}
|
||||||
{% endfor %}{% endif %}
|
{% endfor %}{% endif %}
|
||||||
{%- endmacro -%}
|
{%- endmacro -%}
|
||||||
|
|
||||||
|
{%- macro renderMembers(doc) -%}
|
||||||
|
{%- if doc.members.length %}{% for member in doc.members %}{% if not member.internal %}
|
||||||
|
<a class="code-anchor" href="{$ doc.path $}#{$ member.anchor $}">{$ renderMember(member, 1) $}</a>{% endif %}{% endfor %}{% endif %}
|
||||||
|
{%- for ancestor in doc.extendsClauses %}{% if ancestor.doc %}
|
||||||
|
// inherited from <a class="code-anchor" href="{$ ancestor.doc.path $}">{$ ancestor.doc.id $}</a>{$ renderMembers(ancestor.doc) $}{% endif %}{% endfor %}
|
||||||
|
{%- endmacro -%}
|
||||||
|
|
||||||
{%- macro renderMember(member, truncateLines) -%}
|
{%- macro renderMember(member, truncateLines) -%}
|
||||||
{%- if member.accessibility !== 'public' %}{$ member.accessibility $} {% endif -%}
|
{%- if member.accessibility !== 'public' %}{$ member.accessibility $} {% endif -%}
|
||||||
{%- if member.isGetAccessor %}get {% endif -%}
|
{%- if member.isGetAccessor %}get {% endif -%}
|
||||||
|
@ -2002,9 +2002,9 @@ devtools-timeline-model@1.1.6:
|
|||||||
chrome-devtools-frontend "1.0.401423"
|
chrome-devtools-frontend "1.0.401423"
|
||||||
resolve "1.1.7"
|
resolve "1.1.7"
|
||||||
|
|
||||||
dgeni-packages@^0.20.0:
|
dgeni-packages@^0.21.2:
|
||||||
version "0.20.0"
|
version "0.21.2"
|
||||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.20.0.tgz#e7da99b0a119ee2eb584202d054a5aa01f23e208"
|
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.21.2.tgz#b031194176507b7c7d1c9735ea14664970763866"
|
||||||
dependencies:
|
dependencies:
|
||||||
canonical-path "0.0.2"
|
canonical-path "0.0.2"
|
||||||
catharsis "^0.8.1"
|
catharsis "^0.8.1"
|
||||||
@ -2026,7 +2026,7 @@ dgeni-packages@^0.20.0:
|
|||||||
source-map-support "^0.4.15"
|
source-map-support "^0.4.15"
|
||||||
spdx-license-list "^2.1.0"
|
spdx-license-list "^2.1.0"
|
||||||
stringmap "^0.2.2"
|
stringmap "^0.2.2"
|
||||||
typescript "^2.3.4"
|
typescript "2.4"
|
||||||
|
|
||||||
dgeni@^0.4.7:
|
dgeni@^0.4.7:
|
||||||
version "0.4.7"
|
version "0.4.7"
|
||||||
@ -7619,7 +7619,7 @@ typescript@2.3.2, "typescript@>=2.0.0 <2.5.0":
|
|||||||
version "2.3.2"
|
version "2.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.2.tgz#f0f045e196f69a72f06b25fd3bd39d01c3ce9984"
|
||||||
|
|
||||||
typescript@^2.3.3, typescript@^2.3.4:
|
typescript@2.4, typescript@^2.3.3:
|
||||||
version "2.4.1"
|
version "2.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.4.1.tgz#c3ccb16ddaa0b2314de031e7e6fee89e5ba346bc"
|
||||||
|
|
||||||
|
48
build.sh
48
build.sh
@ -86,7 +86,7 @@ done
|
|||||||
#######################################
|
#######################################
|
||||||
isIgnoredDirectory() {
|
isIgnoredDirectory() {
|
||||||
name=$(basename ${1})
|
name=$(basename ${1})
|
||||||
if [[ -f "${1}" || "${name}" == "src" || "${name}" == "test" || "${name}" == "integrationtest" || "${name}" == "i18n_data" ]]; then
|
if [[ -f "${1}" || "${name}" == "src" || "${name}" == "test" || "${name}" == "integrationtest" ]]; then
|
||||||
return 0
|
return 0
|
||||||
else
|
else
|
||||||
return 1
|
return 1
|
||||||
@ -327,23 +327,13 @@ mapSources() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
updateVersionReferences() {
|
|
||||||
NPM_DIR="$1"
|
|
||||||
(
|
|
||||||
echo "====== VERSION: Updating version references in ${NPM_DIR}"
|
|
||||||
cd ${NPM_DIR}
|
|
||||||
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
|
|
||||||
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
VERSION="${VERSION_PREFIX}${VERSION_SUFFIX}"
|
VERSION="${VERSION_PREFIX}${VERSION_SUFFIX}"
|
||||||
echo "====== BUILDING: Version ${VERSION}"
|
echo "====== BUILDING: Version ${VERSION}"
|
||||||
|
|
||||||
N="
|
N="
|
||||||
"
|
"
|
||||||
TSC=`pwd`/node_modules/.bin/tsc
|
TSC=`pwd`/node_modules/.bin/tsc
|
||||||
NGC="node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main"
|
NGC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||||
MAP_SOURCES="node `pwd`/scripts/build/map_sources.js "
|
MAP_SOURCES="node `pwd`/scripts/build/map_sources.js "
|
||||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||||
TSCONFIG=./tools/tsconfig.json
|
TSCONFIG=./tools/tsconfig.json
|
||||||
@ -355,6 +345,8 @@ if [[ ${BUILD_TOOLS} == true ]]; then
|
|||||||
rm -rf ./dist/tools/
|
rm -rf ./dist/tools/
|
||||||
mkdir -p ./dist/tools/
|
mkdir -p ./dist/tools/
|
||||||
$(npm bin)/tsc -p ${TSCONFIG}
|
$(npm bin)/tsc -p ${TSCONFIG}
|
||||||
|
|
||||||
|
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||||
travisFoldEnd "build tools"
|
travisFoldEnd "build tools"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@ -406,11 +398,11 @@ if [[ ${BUILD_ALL} == true && ${TYPECHECK_ALL} == true ]]; then
|
|||||||
|
|
||||||
TSCONFIG="packages/tsconfig.json"
|
TSCONFIG="packages/tsconfig.json"
|
||||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||||
$TSC -p ${TSCONFIG}
|
$NGC -p ${TSCONFIG}
|
||||||
travisFoldEnd "tsc -p ${TSCONFIG}"
|
travisFoldEnd "tsc -p ${TSCONFIG}"
|
||||||
TSCONFIG="modules/tsconfig.json"
|
TSCONFIG="modules/tsconfig.json"
|
||||||
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
travisFoldStart "tsc -p ${TSCONFIG}" "no-xtrace"
|
||||||
$TSC -p ${TSCONFIG}
|
$NGC -p ${TSCONFIG}
|
||||||
travisFoldEnd "tsc -p ${TSCONFIG}"
|
travisFoldEnd "tsc -p ${TSCONFIG}"
|
||||||
|
|
||||||
fi
|
fi
|
||||||
@ -422,22 +414,6 @@ if [[ ${BUILD_ALL} == true ]]; then
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then
|
|
||||||
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json ====="
|
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json
|
|
||||||
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json ====="
|
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json
|
|
||||||
cp ./packages/tsc-wrapped/package.json ./dist/packages-dist/tsc-wrapped
|
|
||||||
cp ./packages/tsc-wrapped/README.md ./dist/packages-dist/tsc-wrapped
|
|
||||||
updateVersionReferences dist/packages-dist/tsc-wrapped
|
|
||||||
|
|
||||||
rsync -a packages/bazel/ ./dist/packages-dist/bazel
|
|
||||||
# Remove BEGIN-INTERNAL...END-INTERAL blocks
|
|
||||||
# https://stackoverflow.com/questions/24175271/how-can-i-match-multi-line-patterns-in-the-command-line-with-perl-style-regex
|
|
||||||
perl -0777 -n -i -e "s/(?m)^.*BEGIN-INTERNAL[\w\W]*END-INTERNAL.*\n//g; print" $(grep -ril BEGIN-INTERNAL dist/packages-dist/bazel) < /dev/null 2> /dev/null
|
|
||||||
updateVersionReferences dist/packages-dist/bazel
|
|
||||||
fi
|
|
||||||
|
|
||||||
for PACKAGE in ${PACKAGES[@]}
|
for PACKAGE in ${PACKAGES[@]}
|
||||||
do
|
do
|
||||||
travisFoldStart "build package: ${PACKAGE}" "no-xtrace"
|
travisFoldStart "build package: ${PACKAGE}" "no-xtrace"
|
||||||
@ -482,11 +458,6 @@ do
|
|||||||
minify ${BUNDLES_DIR}
|
minify ${BUNDLES_DIR}
|
||||||
|
|
||||||
) 2>&1 | grep -v "as external dependency"
|
) 2>&1 | grep -v "as external dependency"
|
||||||
|
|
||||||
if [[ ${PACKAGE} == "common" ]]; then
|
|
||||||
echo "====== Copy i18n locale data"
|
|
||||||
rsync -a --exclude=*.d.ts --exclude=*.metadata.json ${OUT_DIR}/i18n_data/ ${NPM_DIR}/i18n_data
|
|
||||||
fi
|
|
||||||
else
|
else
|
||||||
echo "====== Copy ${PACKAGE} node tool"
|
echo "====== Copy ${PACKAGE} node tool"
|
||||||
rsync -a ${OUT_DIR}/ ${NPM_DIR}
|
rsync -a ${OUT_DIR}/ ${NPM_DIR}
|
||||||
@ -501,7 +472,12 @@ do
|
|||||||
|
|
||||||
|
|
||||||
if [[ -d ${NPM_DIR} ]]; then
|
if [[ -d ${NPM_DIR} ]]; then
|
||||||
updateVersionReferences ${NPM_DIR}
|
(
|
||||||
|
echo "====== VERSION: Updating version references"
|
||||||
|
cd ${NPM_DIR}
|
||||||
|
echo "====== EXECUTE: perl -p -i -e \"s/0\.0\.0\-PLACEHOLDER/${VERSION}/g\" $""(grep -ril 0\.0\.0\-PLACEHOLDER .)"
|
||||||
|
perl -p -i -e "s/0\.0\.0\-PLACEHOLDER/${VERSION}/g" $(grep -ril 0\.0\.0\-PLACEHOLDER .) < /dev/null 2> /dev/null
|
||||||
|
)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
travisFoldEnd "build package: ${PACKAGE}"
|
travisFoldEnd "build package: ${PACKAGE}"
|
||||||
|
92
docs/CARETAKER.md
Normal file
92
docs/CARETAKER.md
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
# Caretaker
|
||||||
|
|
||||||
|
Caretaker is responsible for merging PRs into the individual branches and internally at Google.
|
||||||
|
|
||||||
|
## Responsibilities
|
||||||
|
|
||||||
|
- Draining the queue of PRs ready to be merged. (PRs with [`PR action: merge`](https://github.com/angular/angular/pulls?q=is%3Aopen+is%3Apr+label%3A%22PR+action%3A+merge%22) label)
|
||||||
|
- Assigining [new issues](https://github.com/angular/angular/issues?q=is%3Aopen+is%3Aissue+no%3Alabel) to individual component authors.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Set `upstream` to fetch PRs into your local repo
|
||||||
|
|
||||||
|
Use this conmmands to configure your `git` to fetch PRs into your local repo.
|
||||||
|
|
||||||
|
```
|
||||||
|
git remote add upstream git@github.com:angular/angular.git
|
||||||
|
git config --add remote.upstream.fetch +refs/pull/*/head:refs/remotes/upstream/pr/*
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Merging the PR
|
||||||
|
|
||||||
|
A PR needs to have `PR action: merge` and `PR target: *` labels to be considered
|
||||||
|
ready to merge. Merging is performed by running `merge-pr` with a PR number to merge.
|
||||||
|
|
||||||
|
NOTE: before running `merge-pr` ensure that you have synced all of the PRs
|
||||||
|
locally by running:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ git fetch upstream
|
||||||
|
```
|
||||||
|
|
||||||
|
To merge a PR run:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./scripts/github/merge-pr 1234
|
||||||
|
```
|
||||||
|
|
||||||
|
The `merge-pr` script will:
|
||||||
|
- Ensure that all approriate labels are on the PR.
|
||||||
|
- That the current branch (`master` or `?.?.x` patch) mathches the `PR target: *` label.
|
||||||
|
- It will `cherry-pick` all of the SHAs from the PR into the current branch.
|
||||||
|
- It will rewrite commit history by automatically adding `Close #1234` and `(#1234)` into the commit message.
|
||||||
|
|
||||||
|
|
||||||
|
### Recovering from failed `merge-pr` due to conflicts
|
||||||
|
|
||||||
|
When running `merge-pr` the script will output the commands which it is about to run.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./scripts/github/merge-pr 1234
|
||||||
|
======================
|
||||||
|
GitHub Merge PR Steps
|
||||||
|
======================
|
||||||
|
git cherry-pick upstream/pr/1234~1..upstream/pr/1234
|
||||||
|
git filter-branch -f --msg-filter "/usr/local/google/home/misko/angular-pr/scripts/github/utils/github.closes 1234" HEAD~1..HEAD
|
||||||
|
```
|
||||||
|
|
||||||
|
If the `cherry-pick` command fails than resolve conflicts and use `git cherry-pick --continue` once ready. After the `cherry-pick` is done cut&paste and run the `filter-branch` command to properly rewrite the messages
|
||||||
|
|
||||||
|
## Cherry-picking PRs into patch branch
|
||||||
|
|
||||||
|
In addition to merging PRs into the master branch, many PRs need to be also merged into a patch branch.
|
||||||
|
Follow these steps to get path brach up to date.
|
||||||
|
|
||||||
|
1. Check out the most recent patch branch: `git checkout 4.3.x`
|
||||||
|
2. Get a list of PRs merged into master: `git log master --oneline -n10`
|
||||||
|
3. For each PR number in the commit message run: `././scripts/github/merge-pr 1234`
|
||||||
|
- The PR will only merge if the `PR target:` matches the branch.
|
||||||
|
|
||||||
|
Once all of the PRs are in patch branch, push the all branches and tags to github using `push-upstream` script.
|
||||||
|
|
||||||
|
|
||||||
|
## Pushing merged PRs into github
|
||||||
|
|
||||||
|
Use `push-upstream` script to push all of the branch and tags to github.
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ./scripts/github/push-upstream
|
||||||
|
git push git@github.com:angular/angular.git master:master 4.3.x:4.3.x
|
||||||
|
Counting objects: 25, done.
|
||||||
|
Delta compression using up to 6 threads.
|
||||||
|
Compressing objects: 100% (17/17), done.
|
||||||
|
Writing objects: 100% (25/25), 2.22 KiB | 284.00 KiB/s, done.
|
||||||
|
Total 25 (delta 22), reused 8 (delta 7)
|
||||||
|
remote: Resolving deltas: 100% (22/22), completed with 18 local objects.
|
||||||
|
To github.com:angular/angular.git
|
||||||
|
079d884b6..d1c4a94bb master -> master
|
||||||
|
git push --tags -f git@github.com:angular/angular.git patch_sync:patch_sync
|
||||||
|
Everything up-to-date
|
||||||
|
```
|
@ -13,8 +13,8 @@ Change approvals in our monorepo are managed via [pullapprove.com](https://about
|
|||||||
|
|
||||||
# Merging
|
# Merging
|
||||||
|
|
||||||
Once a change has all the approvals either the last approver or the PR author (if PR author has the project collaborator status) should mark the PR with "PR: merge" label.
|
Once a change has all the approvals either the last approver or the PR author (if PR author has the project collaborator status) should mark the PR with `PR: merge` as well as `PR target: *` labels.
|
||||||
This signals to the caretaker that the PR should be merged.
|
This signals to the caretaker that the PR should be merged. See [merge instructions](../CARETAKER.md).
|
||||||
|
|
||||||
# Who is the Caretaker?
|
# Who is the Caretaker?
|
||||||
|
|
||||||
|
@ -42,5 +42,3 @@ gulp.task('serve', loadTask('serve', 'default'));
|
|||||||
gulp.task('serve-examples', loadTask('serve', 'examples'));
|
gulp.task('serve-examples', loadTask('serve', 'examples'));
|
||||||
gulp.task('changelog', loadTask('changelog'));
|
gulp.task('changelog', loadTask('changelog'));
|
||||||
gulp.task('check-env', () => {/* this is a noop because the env test ran already above */});
|
gulp.task('check-env', () => {/* this is a noop because the env test ran already above */});
|
||||||
gulp.task('cldr:extract', loadTask('cldr', 'extract'));
|
|
||||||
gulp.task('cldr:download', loadTask('cldr', 'download'));
|
|
||||||
|
5
integration/.gitignore
vendored
5
integration/.gitignore
vendored
@ -1,13 +1,10 @@
|
|||||||
built/
|
built/
|
||||||
dist/
|
dist/
|
||||||
vendor/
|
vendor/
|
||||||
yarn.lock
|
|
||||||
.ng-cli/
|
|
||||||
cli-*/**
|
|
||||||
*/src/*.d.ts
|
*/src/*.d.ts
|
||||||
*/src/*.js
|
*/src/*.js
|
||||||
**/*.ngfactory.ts
|
**/*.ngfactory.ts
|
||||||
**/*.ngsummary.json
|
**/*.ngsummary.json
|
||||||
**/*.ngsummary.ts
|
**/*.ngsummary.ts
|
||||||
*/yarn*
|
*/yarn*
|
||||||
**/.yarn_local_cache*
|
*/.yarn_local_cache*
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -u -e -o pipefail
|
|
||||||
|
|
||||||
declare -A payloadLimits
|
|
||||||
payloadLimits["hello_world__closure", "uncompressed", "bundle"]=106000
|
|
||||||
payloadLimits["hello_world__closure", "gzip7", "bundle"]=35000
|
|
||||||
payloadLimits["hello_world__closure", "gzip9", "bundle"]=35000
|
|
||||||
|
|
||||||
payloadLimits["cli-hello-world", "uncompressed", "inline"]=1500
|
|
||||||
payloadLimits["cli-hello-world", "uncompressed", "main"]=183000
|
|
||||||
payloadLimits["cli-hello-world", "uncompressed", "polyfills"]=63000
|
|
||||||
payloadLimits["cli-hello-world", "gzip7", "inline"]=900
|
|
||||||
payloadLimits["cli-hello-world", "gzip7", "main"]=48000
|
|
||||||
payloadLimits["cli-hello-world", "gzip7", "polyfills"]=21000
|
|
||||||
payloadLimits["cli-hello-world", "gzip9", "inline"]=900
|
|
||||||
payloadLimits["cli-hello-world", "gzip9", "main"]=48000
|
|
||||||
payloadLimits["cli-hello-world", "gzip9", "polyfills"]=21000
|
|
||||||
|
|
@ -1,10 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
filegroup(
|
|
||||||
name = "node_modules",
|
|
||||||
srcs = glob([
|
|
||||||
"node_modules/**/*.js",
|
|
||||||
"node_modules/**/*.d.ts",
|
|
||||||
"node_modules/**/*.json",
|
|
||||||
])
|
|
||||||
)
|
|
@ -1,30 +0,0 @@
|
|||||||
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
|
||||||
|
|
||||||
git_repository(
|
|
||||||
name = "build_bazel_rules_nodejs",
|
|
||||||
remote = "https://github.com/bazelbuild/rules_nodejs.git",
|
|
||||||
tag = "0.0.2",
|
|
||||||
)
|
|
||||||
|
|
||||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
|
||||||
node_repositories(package_json = ["//:package.json"])
|
|
||||||
|
|
||||||
local_repository(
|
|
||||||
name = "build_bazel_rules_typescript",
|
|
||||||
path = "node_modules/@bazel/typescript",
|
|
||||||
)
|
|
||||||
|
|
||||||
local_repository(
|
|
||||||
name = "angular",
|
|
||||||
path = "node_modules/@angular/bazel",
|
|
||||||
)
|
|
||||||
|
|
||||||
git_repository(
|
|
||||||
name = "io_bazel_rules_sass",
|
|
||||||
remote = "https://github.com/bazelbuild/rules_sass.git",
|
|
||||||
tag = "0.0.2",
|
|
||||||
)
|
|
||||||
|
|
||||||
load("@io_bazel_rules_sass//sass:sass.bzl", "sass_repositories")
|
|
||||||
|
|
||||||
sass_repositories()
|
|
@ -1,23 +0,0 @@
|
|||||||
// WORKAROUND https://github.com/angular/angular/issues/18810
|
|
||||||
// This file is required to run ngc on angular libraries, to write files like
|
|
||||||
// node_modules/@angular/core/core.ngsummary.json
|
|
||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es2015"
|
|
||||||
],
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": []
|
|
||||||
},
|
|
||||||
"include": [
|
|
||||||
"node_modules/@angular/**/*"
|
|
||||||
],
|
|
||||||
"exclude": [
|
|
||||||
"node_modules/@angular/bazel/**",
|
|
||||||
"node_modules/@angular/compiler-cli/**",
|
|
||||||
// Workaround bug introduced by 079d884
|
|
||||||
"node_modules/@angular/common/i18n_data*",
|
|
||||||
"node_modules/@angular/tsc-wrapped/**"
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "angular-bazel",
|
|
||||||
"description": "example and integration test for building Angular apps with Bazel",
|
|
||||||
"version": "0.0.0",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
|
||||||
"@angular/common": "file:../../dist/packages-dist/common",
|
|
||||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
|
||||||
"@angular/core": "file:../../dist/packages-dist/core",
|
|
||||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
|
||||||
"rxjs": "5.3.1",
|
|
||||||
"zone.js": "0.8.6"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@angular/bazel": "file:../../dist/packages-dist/bazel",
|
|
||||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
|
||||||
"@bazel/typescript": "0.0.7",
|
|
||||||
"typescript": "~2.3.1"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"postinstall": "ngc -p angular.tsconfig.json",
|
|
||||||
"test": "bazel build ..."
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
load("@angular//:index.bzl", "ng_module")
|
|
||||||
|
|
||||||
# Allow targets under sub-packages to reference the tsconfig.json file
|
|
||||||
exports_files(["tsconfig.json"])
|
|
||||||
|
|
||||||
ng_module(
|
|
||||||
name = "app",
|
|
||||||
srcs = ["app.module.ts"],
|
|
||||||
deps = ["//src/hello-world"],
|
|
||||||
tsconfig = ":tsconfig.json",
|
|
||||||
)
|
|
@ -1,9 +0,0 @@
|
|||||||
import {HelloWorldModule} from './hello-world/hello-world.module';
|
|
||||||
|
|
||||||
import {NgModule} from '@angular/core';
|
|
||||||
import {BrowserModule} from '@angular/platform-browser';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [BrowserModule, HelloWorldModule]
|
|
||||||
})
|
|
||||||
export class AppModule {}
|
|
@ -1,19 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
load("@angular//:index.bzl", "ng_module")
|
|
||||||
load("@io_bazel_rules_sass//sass:sass.bzl", "sass_binary")
|
|
||||||
|
|
||||||
sass_binary(
|
|
||||||
name = "styles",
|
|
||||||
src = "hello-world.component.scss",
|
|
||||||
deps = [
|
|
||||||
"//src/shared:colors",
|
|
||||||
"//src/shared:fonts",
|
|
||||||
],
|
|
||||||
)
|
|
||||||
|
|
||||||
ng_module(
|
|
||||||
name = "hello-world",
|
|
||||||
srcs = glob(["*.ts"]),
|
|
||||||
tsconfig = "//src:tsconfig.json",
|
|
||||||
assets = [":styles"],
|
|
||||||
)
|
|
@ -1,12 +0,0 @@
|
|||||||
@import "src/shared/fonts";
|
|
||||||
@import "src/shared/colors";
|
|
||||||
|
|
||||||
html {
|
|
||||||
body {
|
|
||||||
font-family: $default-font-stack;
|
|
||||||
h1 {
|
|
||||||
font-family: $modern-font-stack;
|
|
||||||
color: $example-red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
|
|
||||||
import {Component, NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hello-world-app',
|
|
||||||
template: `
|
|
||||||
<div>Hello {{ name }}!</div>
|
|
||||||
<input type="text" [value]="name" (input)="name = $event.target.value"/>
|
|
||||||
`,
|
|
||||||
// TODO: might be better to point to .scss so this looks valid at design-time
|
|
||||||
styleUrls: ['./styles.css']
|
|
||||||
})
|
|
||||||
export class HelloWorldComponent {
|
|
||||||
name: string = 'world';
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
import {HelloWorldComponent} from './hello-world.component';
|
|
||||||
import {NgModule} from '@angular/core';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
declarations: [HelloWorldComponent],
|
|
||||||
bootstrap: [HelloWorldComponent],
|
|
||||||
})
|
|
||||||
export class HelloWorldModule {}
|
|
@ -1,13 +0,0 @@
|
|||||||
package(default_visibility = ["//visibility:public"])
|
|
||||||
|
|
||||||
load("@io_bazel_rules_sass//sass:sass.bzl", "sass_library")
|
|
||||||
|
|
||||||
sass_library(
|
|
||||||
name = "colors",
|
|
||||||
srcs = ["_colors.scss"],
|
|
||||||
)
|
|
||||||
|
|
||||||
sass_library(
|
|
||||||
name = "fonts",
|
|
||||||
srcs = ["_fonts.scss"],
|
|
||||||
)
|
|
@ -1,2 +0,0 @@
|
|||||||
$example-blue: #0000ff;
|
|
||||||
$example-red: #ff0000;
|
|
@ -1,2 +0,0 @@
|
|||||||
$default-font-stack: Cambria, "Hoefler Text", Utopia, "Liberation Serif", "Nimbus Roman No9 L Regular", Times, "Times New Roman", serif;
|
|
||||||
$modern-font-stack: Constantia, "Lucida Bright", Lucidabright, "Lucida Serif", Lucida, "DejaVu Serif", "Bitstream Vera Serif", "Liberation Serif", Georgia, serif;
|
|
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"lib": [
|
|
||||||
"dom",
|
|
||||||
"es5",
|
|
||||||
"es2015.collection",
|
|
||||||
"es2015.iterable",
|
|
||||||
"es2015.promise"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -10,10 +10,10 @@
|
|||||||
"@angular/core": "file:../../dist/packages-dist/core",
|
"@angular/core": "file:../../dist/packages-dist/core",
|
||||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||||
"google-closure-compiler": "20170409.0.0",
|
"google-closure-compiler": "20170409.0.0",
|
||||||
"rxjs": "5.3.1",
|
"rxjs": "5.3.1",
|
||||||
"typescript": "~2.3.1",
|
"typescript": "2.1.6",
|
||||||
"zone.js": "0.8.6"
|
"zone.js": "0.8.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user