Compare commits
296 Commits
4.0.0-rc.3
...
4.0.3
Author | SHA1 | Date | |
---|---|---|---|
6ccb93728e | |||
b54a957ef4 | |||
21c14a79fa | |||
eed2d456d7 | |||
719c232321 | |||
bd033b69c1 | |||
5b92f7ee6d | |||
20ffb4196d | |||
11e6f45387 | |||
a30a73c2aa | |||
fb94c9937c | |||
b3259e26fe | |||
be406f8464 | |||
5cab3b146e | |||
eff6216319 | |||
88b2ab09ea | |||
5ad6d55a45 | |||
c81c7c0920 | |||
6f8e23b061 | |||
dc9711107d | |||
4c0c1e5fa0 | |||
06ef0908c7 | |||
a893d5e0a9 | |||
44afa5a03a | |||
0733f8d3e1 | |||
6604a4dc40 | |||
96c63a92e2 | |||
c444b1575f | |||
2b676192f6 | |||
14a2d1a6f7 | |||
61e089931f | |||
cbf30cb101 | |||
2f41b52e78 | |||
dcf60da16d | |||
9c9f409364 | |||
b44b983c1f | |||
978f80985c | |||
c10e50cf38 | |||
168a2eb5bf | |||
c9c7acd484 | |||
5b99533315 | |||
037805b741 | |||
0861fda69c | |||
feae7b6059 | |||
e4277a0869 | |||
1864ccb3dd | |||
6b79ab5abe | |||
53c12a84dc | |||
ca665303f4 | |||
0fe4985756 | |||
74087cb39d | |||
902bb2f026 | |||
23bf34853c | |||
8c4b963927 | |||
bfa4f70204 | |||
d481f6d150 | |||
fd6114561b | |||
c82851172e | |||
75478b2078 | |||
cdbb3dbd2a | |||
e72124c888 | |||
b8c0a97e35 | |||
5597fd3180 | |||
a88413f871 | |||
aa116524e6 | |||
4a5ad7ba30 | |||
d74e4d0633 | |||
a2c2b87aa3 | |||
8f4ea3e4b8 | |||
bf25e94f19 | |||
7983414e6a | |||
8248eba3e2 | |||
a65487528f | |||
426b3a19b7 | |||
2360676a7b | |||
ce3e03ff1a | |||
858c11cf7b | |||
95afaf495b | |||
d92930e975 | |||
c2892dada3 | |||
b2b1195534 | |||
e1b09e3bcc | |||
c65b75443e | |||
db0dca3fc1 | |||
7a715b2403 | |||
1ba296644d | |||
b800a0c824 | |||
0dda01e37c | |||
c8ab5cb0c5 | |||
92084f2b6a | |||
08f2f08d74 | |||
376088da70 | |||
73808dd38b | |||
ee03418b10 | |||
da700d1842 | |||
de87c47dd9 | |||
1060805a1f | |||
08d86751b9 | |||
9319b5f329 | |||
26f6bd4d3b | |||
edb2571a59 | |||
98cb974796 | |||
8b414222aa | |||
ea49a95bd9 | |||
a50d79df47 | |||
64285a2171 | |||
941f194a83 | |||
8b4edcc7ad | |||
c58499786c | |||
1bcbcfd56f | |||
90d2518d9a | |||
7354949763 | |||
5efc86069f | |||
97149f9424 | |||
bac265fdc2 | |||
e59e5e24b9 | |||
9e5d4781cb | |||
fc1f6efe0d | |||
c9710d4fb5 | |||
cf16f3b0dd | |||
a9e91115bf | |||
90f699fdcf | |||
fd7b855cfc | |||
20aab64c65 | |||
2eb027a793 | |||
b0a7bc77ee | |||
4e10faf1eb | |||
a0c6d44e18 | |||
9bc998c7a1 | |||
1a0c6d89b1 | |||
8c12374c4c | |||
45e2126273 | |||
41497b052d | |||
068cad1c1b | |||
31ef92fc36 | |||
b4081e3713 | |||
c8b4a33a7f | |||
a13ddf2e8a | |||
64beae9527 | |||
15a082c74e | |||
fbccd5cd38 | |||
1e8b132ade | |||
431eb309f3 | |||
8e6995c91e | |||
0759911431 | |||
1d7693c1e1 | |||
16e0423085 | |||
c2ffb6bfcd | |||
2489e4ba1b | |||
94da80148e | |||
764e90f9bb | |||
9bf2fb4a74 | |||
de3d2eeeba | |||
a805d00256 | |||
61135bc842 | |||
fa36ffda14 | |||
d3eda7a5b5 | |||
a755b715ed | |||
a9d5de0e56 | |||
f634c62cb3 | |||
f8c075ae27 | |||
aeb99645bb | |||
0d3e314df0 | |||
28ce68a13d | |||
80075afe8a | |||
bcc29ffdd1 | |||
0c43535ccc | |||
49829b4a4d | |||
5c5c2ae405 | |||
6e9264a79c | |||
a6fb78ee3c | |||
d0bc83ca27 | |||
b5b2fed54d | |||
4cef5dddc6 | |||
f92591054b | |||
2a0e55ffb5 | |||
9429032da1 | |||
791534f2f4 | |||
604546c287 | |||
c66437fc13 | |||
8415910375 | |||
5486e5417b | |||
2d78c8cc05 | |||
52bed7f9b3 | |||
7fb45283df | |||
b7212f5afe | |||
4e25601c4d | |||
add7829cb7 | |||
a52184bdda | |||
410aa33005 | |||
0ab49d4cec | |||
994089d36b | |||
d2fbbb44ae | |||
77fd91d615 | |||
492153a986 | |||
c0e05e6f03 | |||
73a46205a0 | |||
992aa17361 | |||
a4076c70cc | |||
52bbc9baf4 | |||
26d4ce29e8 | |||
41f61b0b5b | |||
f92d644c95 | |||
923d0c56e7 | |||
dd36d413ba | |||
013d806b79 | |||
6e98757665 | |||
313158132d | |||
6772c913c7 | |||
b11d0119ac | |||
637a489996 | |||
c2bd357825 | |||
4870f910d6 | |||
c95a3048ce | |||
0e6eb6d719 | |||
09574fc285 | |||
4347cb2119 | |||
f600d4e9e4 | |||
eaa04354d5 | |||
3f7cfde476 | |||
62d5543b01 | |||
f1b33ab7b1 | |||
029d0f25e5 | |||
3f38c6fdcd | |||
80112a9ea1 | |||
795638e18b | |||
322bf7a0d5 | |||
cd981499f9 | |||
c439742a54 | |||
a3e32fb7e1 | |||
1171f91a80 | |||
8e2c8b3e4d | |||
b00fe20afd | |||
36ce0afff6 | |||
5c0ea20bd0 | |||
49764a5bff | |||
4f7d62adac | |||
c10c060d20 | |||
5fe2d8fd80 | |||
5c34066058 | |||
50ab06e29d | |||
06fc42bc44 | |||
959a03a61f | |||
3b1956bbf2 | |||
3c15916e17 | |||
ff60c041f6 | |||
13686bb518 | |||
f093501501 | |||
80649ea03c | |||
778f7d6f33 | |||
2c5a671341 | |||
0aad270267 | |||
221899a930 | |||
060a2d11e5 | |||
abbbb4d52c | |||
ccd38dd54d | |||
cdc882bd36 | |||
1c1085b140 | |||
914797a8ff | |||
ab40fcb068 | |||
1847550ad1 | |||
6497633529 | |||
8850098ea4 | |||
eedca09d73 | |||
7b6dbf0952 | |||
e6c81d2a42 | |||
498a95148b | |||
21a18d6ceb | |||
195b863ea4 | |||
75147ff008 | |||
018e5c979b | |||
e7dab7e6c1 | |||
26efa3a25c | |||
893652a813 | |||
6559425b07 | |||
df914ef4bf | |||
4e1cf5b41a | |||
0c5f893f6e | |||
17f5f3b32c | |||
3bb59902f7 | |||
b804a488c5 | |||
cbde75e77b | |||
413e11fac2 | |||
0e2dd76c3b | |||
ff71eff157 | |||
fa1920a02b | |||
71cd2957f7 | |||
6c8638cf01 | |||
8b5c6b2732 | |||
601494734c | |||
1aebea52e1 | |||
920b3d259d | |||
fce55d87d2 | |||
53d62fa7d0 | |||
a69afeb614 | |||
5f9fb911f7 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -26,7 +26,3 @@ yarn-error.log
|
||||
|
||||
# rollup-test output
|
||||
/modules/rollup-test/dist/
|
||||
|
||||
# angular.io
|
||||
/aio/src/content/docs
|
||||
/aio/dist
|
||||
|
@ -20,6 +20,8 @@
|
||||
# tbosch - Tobias Bosch
|
||||
# vicb - Victor Berchet
|
||||
# vikerman - Vikram Subramanian
|
||||
# wardbell - Ward Bell
|
||||
# tinayuangao - Tina Gao
|
||||
|
||||
version: 2
|
||||
|
||||
@ -40,6 +42,7 @@ groups:
|
||||
- "aio/*"
|
||||
- "integration/*"
|
||||
- "modules/*"
|
||||
- "packages/*"
|
||||
- "tools/*"
|
||||
users:
|
||||
- IgorMinar
|
||||
@ -153,7 +156,7 @@ groups:
|
||||
- "packages/forms/*"
|
||||
users:
|
||||
- kara #primary
|
||||
# needs secondary
|
||||
- tinayuangao #secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -173,7 +176,8 @@ groups:
|
||||
- "packages/language-service/*"
|
||||
users:
|
||||
- chuckjaz #primary
|
||||
# needs secondary
|
||||
- tbosch #secondary
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -182,8 +186,8 @@ groups:
|
||||
files:
|
||||
- "packages/router/*"
|
||||
users:
|
||||
- vicb #primary
|
||||
# needs secondary
|
||||
- jasonaden
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
@ -246,7 +250,8 @@ groups:
|
||||
files:
|
||||
- "aio/*"
|
||||
users:
|
||||
- IgorMinar
|
||||
- robwormald
|
||||
- petebacondarwin
|
||||
- IgorMinar #primary
|
||||
- petebacondarwin #secondary
|
||||
- gkalpak
|
||||
- wardbell
|
||||
- mhevery #fallback
|
||||
|
@ -19,7 +19,7 @@ addons:
|
||||
- secure: "L7nrZwkAtFtYrP2DykPXgZvEKjkv0J/TwQ/r2QGxFTaBq4VZn+2Dw0YS7uCxoMqYzDwH0aAOqxoutibVpk8Z/16nE3tNmU5RzltMd6Xmt3qU2f/JDQLMo6PSlBodnjOUsDHJgmtrcbjhqrx/znA237BkNUu6UZRT7mxhXIZpn0U="
|
||||
branches:
|
||||
except:
|
||||
- g3_v2_0
|
||||
- g3
|
||||
|
||||
cache:
|
||||
yarn: true
|
||||
@ -30,9 +30,9 @@ cache:
|
||||
|
||||
env:
|
||||
global:
|
||||
# GITHUB_TOKEN_ANGULAR
|
||||
# GITHUB_TOKEN_ANGULAR=<github token, a personal access token of the angular-builds account, account access in valentine>
|
||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||
- secure: "rNqXoy2gqjbF5tBXlRBy+oiYntO3BtzcxZuEtlLMzNaTNzC4dyMOFub0GkzIPWwOzkARoEU9Kv+bC97fDVbCBUKeyzzEqxqddUKhzRxeaYjsefJ6XeTvBvDxwo7wDwyxZSuWdBeGAe4eARVHm7ypsd+AlvqxtzjyS27TK2BzdL4="
|
||||
# FIREBASE_TOKEN
|
||||
# This is needed for publishing builds to the "aio-staging" firebase site.
|
||||
# TODO(i): the token was generated using the iminar@google account, we should switch to a shared/role-base account.
|
||||
@ -40,6 +40,7 @@ env:
|
||||
matrix:
|
||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
- CI_MODE=e2e
|
||||
- CI_MODE=e2e_2
|
||||
- CI_MODE=js
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
|
267
CHANGELOG.md
267
CHANGELOG.md
@ -1,3 +1,269 @@
|
||||
<a name="4.0.3"></a>
|
||||
## [4.0.3](https://github.com/angular/angular/compare/4.0.2...4.0.3) (2017-04-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **benchpress:** chrome - prevent trace buffer overflow ([d216f94](https://github.com/angular/angular/commit/d216f94))
|
||||
* **compiler:** fix build error in xliff2 ([1870347](https://github.com/angular/angular/commit/1870347))
|
||||
* **compiler:** ignore calls to unresolved symbols in metadata ([d4038ab](https://github.com/angular/angular/commit/d4038ab)), closes [#15969](https://github.com/angular/angular/issues/15969)
|
||||
* **compiler:** ignore calls to unresolved symbols in metadata ([#15970](https://github.com/angular/angular/issues/15970)) ([db25f08](https://github.com/angular/angular/commit/db25f08)), closes [#15969](https://github.com/angular/angular/issues/15969)
|
||||
* **compiler:** Inform user where Quoted error was thrown ([3184cc5](https://github.com/angular/angular/commit/3184cc5))
|
||||
* **compiler:** suppress another closure warning ([#16137](https://github.com/angular/angular/issues/16137)) ([72e240a](https://github.com/angular/angular/commit/72e240a))
|
||||
* **core:** benchmarks - enable ng1 benchmark again ([ccac4c6](https://github.com/angular/angular/commit/ccac4c6))
|
||||
* **core:** distribute externs for testability API ([#16179](https://github.com/angular/angular/issues/16179)) ([e377d9d](https://github.com/angular/angular/commit/e377d9d))
|
||||
* **core:** key-value differ changes iteration ([#15968](https://github.com/angular/angular/issues/15968)) ([a8600dc](https://github.com/angular/angular/commit/a8600dc)), closes [#14997](https://github.com/angular/angular/issues/14997)
|
||||
* **language-service:** only use canonical symbols ([786093a](https://github.com/angular/angular/commit/786093a))
|
||||
* **packaging:** increased buffer size ([#15840](https://github.com/angular/angular/issues/15840)) ([88ad490](https://github.com/angular/angular/commit/88ad490))
|
||||
* **platform-server:** handle innerText ([#15818](https://github.com/angular/angular/issues/15818)) ([7de340d](https://github.com/angular/angular/commit/7de340d))
|
||||
* **router:** prevent `RouterLinkActive` from causing an infinite CD loop ([4479c42](https://github.com/angular/angular/commit/4479c42)), closes [#15825](https://github.com/angular/angular/issues/15825)
|
||||
* **tsc-wrapped:** collect new expressions with no arguments ([#15908](https://github.com/angular/angular/issues/15908)) ([41cac9e](https://github.com/angular/angular/commit/41cac9e)), closes [#15906](https://github.com/angular/angular/issues/15906)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** Implement i18n XLIFF 2.0 serializer ([#14185](https://github.com/angular/angular/issues/14185)) ([a7d8edd](https://github.com/angular/angular/commit/a7d8edd)), closes [#11735](https://github.com/angular/angular/issues/11735)
|
||||
* **upgrade:** allow setting the angularjs lib at runtime ([#15168](https://github.com/angular/angular/issues/15168)) ([a75d056](https://github.com/angular/angular/commit/a75d056))
|
||||
* **upgrade:** allow setting the angularjs lib at runtime ([#15168](https://github.com/angular/angular/issues/15168)) ([4f172b0](https://github.com/angular/angular/commit/4f172b0))
|
||||
* **upgrade:** fixes for allow setting the angularjs lib at runtime ([bb6932d](https://github.com/angular/angular/commit/bb6932d))
|
||||
* add support for TS 2.3 ([5cf101f](https://github.com/angular/angular/commit/5cf101f))
|
||||
|
||||
|
||||
|
||||
<a name="4.1.0-beta.1"></a>
|
||||
# [4.1.0-beta.1](https://github.com/angular/angular/compare/4.1.0-beta.0...4.1.0-beta.1) (2017-04-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix inheritance for AOT with summaries ([#15583](https://github.com/angular/angular/issues/15583)) ([8ef621a](https://github.com/angular/angular/commit/8ef621a))
|
||||
* **language-service:** avoid throwing exceptions when reporting metadata errors ([7764c5c](https://github.com/angular/angular/commit/7764c5c))
|
||||
* **language-service:** detect when there isn't a tsconfig.json ([258d539](https://github.com/angular/angular/commit/258d539)), closes [#15874](https://github.com/angular/angular/issues/15874)
|
||||
* **language-service:** improve resilience to incomplete information ([71a8627](https://github.com/angular/angular/commit/71a8627))
|
||||
* **language-service:** initialize static reflector correctly ([fe0d02f](https://github.com/angular/angular/commit/fe0d02f)), closes [#15768](https://github.com/angular/angular/issues/15768)
|
||||
* **language-service:** parse extended i18n forms ([bde9771](https://github.com/angular/angular/commit/bde9771))
|
||||
* **language-service:** resolve any parameter types to any result ([5fbb0d0](https://github.com/angular/angular/commit/5fbb0d0))
|
||||
* **router:** fix query param parsing ([a487563](https://github.com/angular/angular/commit/a487563))
|
||||
* **router:** the preloader use the module from the loaded config ([6d12aa9](https://github.com/angular/angular/commit/6d12aa9))
|
||||
* **tsc-wrapped:** ensure valid path separators in metadata ([96aa236](https://github.com/angular/angular/commit/96aa236))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** Update types for TypeScript nullability support ([38d75d4](https://github.com/angular/angular/commit/38d75d4)), closes [#15870](https://github.com/angular/angular/issues/15870)
|
||||
* **benchpress:** Update types for TypeScript nullability support ([14669f2](https://github.com/angular/angular/commit/14669f2))
|
||||
* **common:** Update types for TypeScript nullability support ([d8b73e4](https://github.com/angular/angular/commit/d8b73e4))
|
||||
* **compiler:** Update types for TypeScript nullability support ([09d9f5f](https://github.com/angular/angular/commit/09d9f5f))
|
||||
* **language-service:** Update types for TypeScript nullability support ([540581d](https://github.com/angular/angular/commit/540581d))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.2"></a>
|
||||
## [4.0.2](https://github.com/angular/angular/compare/4.0.1...4.0.2) (2017-04-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix inheritance for AOT with summaries ([#15583](https://github.com/angular/angular/issues/15583)) ([1864ccb](https://github.com/angular/angular/commit/1864ccb))
|
||||
* **language-service:** avoid throwing exceptions when reporting metadata errors ([0861fda](https://github.com/angular/angular/commit/0861fda))
|
||||
* **language-service:** detect when there isn't a tsconfig.json ([168a2eb](https://github.com/angular/angular/commit/168a2eb)), closes [#15874](https://github.com/angular/angular/issues/15874)
|
||||
* **language-service:** improve resilience to incomplete information ([e4277a0](https://github.com/angular/angular/commit/e4277a0))
|
||||
* **language-service:** initialize static reflector correctly ([5b99533](https://github.com/angular/angular/commit/5b99533)), closes [#15768](https://github.com/angular/angular/issues/15768)
|
||||
* **language-service:** parse extended i18n forms ([c9c7acd](https://github.com/angular/angular/commit/c9c7acd))
|
||||
* **language-service:** resolve any parameter types to any result ([feae7b6](https://github.com/angular/angular/commit/feae7b6))
|
||||
* **router:** fix query param parsing ([2f41b52](https://github.com/angular/angular/commit/2f41b52))
|
||||
* **router:** the preloader use the module from the loaded config ([978f809](https://github.com/angular/angular/commit/978f809))
|
||||
* **tsc-wrapped:** ensure valid path separators in metadata ([c10e50c](https://github.com/angular/angular/commit/c10e50c))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.1"></a>
|
||||
## [4.0.1](https://github.com/angular/angular/compare/4.0.0...4.0.1) (2017-03-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** make sure style calculations are not computed too early ([#15540](https://github.com/angular/angular/issues/15540)) ([c828511](https://github.com/angular/angular/commit/c828511)), closes [#15507](https://github.com/angular/angular/issues/15507)
|
||||
* **compiler:** allow single quotes into named interpolations ([#15461](https://github.com/angular/angular/issues/15461)) ([a654875](https://github.com/angular/angular/commit/a654875)), closes [#15318](https://github.com/angular/angular/issues/15318)
|
||||
* **compiler:** ignore errors when evaluating base classes ([#15560](https://github.com/angular/angular/issues/15560)) ([a88413f](https://github.com/angular/angular/commit/a88413f)), closes [#15536](https://github.com/angular/angular/issues/15536)
|
||||
* **compiler:** throw when a component defines both template and templateUrl ([#15572](https://github.com/angular/angular/issues/15572)) ([902bb2f](https://github.com/angular/angular/commit/902bb2f)), closes [#15566](https://github.com/angular/angular/issues/15566)
|
||||
* **core:** check for undefined on normalizeDebugBindingValue ([#15503](https://github.com/angular/angular/issues/15503)) ([b8c0a97](https://github.com/angular/angular/commit/b8c0a97)), closes [#15494](https://github.com/angular/angular/issues/15494)
|
||||
* **core:** fix inheritance in JIT mode for TS 2.1 ([#15599](https://github.com/angular/angular/issues/15599)) ([ca66530](https://github.com/angular/angular/commit/ca66530)), closes [#15502](https://github.com/angular/angular/issues/15502)
|
||||
* **core:** fix the key/value differ ([#15539](https://github.com/angular/angular/issues/15539)) ([e72124c](https://github.com/angular/angular/commit/e72124c)), closes [#15457](https://github.com/angular/angular/issues/15457)
|
||||
* **core:** improve error msg for invalid KeyValueDiffer.diff arg ([#15489](https://github.com/angular/angular/issues/15489)) ([d74e4d0](https://github.com/angular/angular/commit/d74e4d0)), closes [#15402](https://github.com/angular/angular/issues/15402)
|
||||
* **core:** Update types for TypeScript nullability support ([#15472](https://github.com/angular/angular/issues/15472)) ([8c4b963](https://github.com/angular/angular/commit/8c4b963))
|
||||
* **language-service:** be resilient to invalidate ordering ([#15470](https://github.com/angular/angular/issues/15470)) ([a2c2b87](https://github.com/angular/angular/commit/a2c2b87)), closes [#15466](https://github.com/angular/angular/issues/15466)
|
||||
* **language-service:** correctly determine base members of types ([#15600](https://github.com/angular/angular/issues/15600)) ([0fe4985](https://github.com/angular/angular/commit/0fe4985)), closes [#15460](https://github.com/angular/angular/issues/15460)
|
||||
* **language-service:** don't require `reflect-metadata` module to be provided ([#15569](https://github.com/angular/angular/issues/15569)) ([bfa4f70](https://github.com/angular/angular/commit/bfa4f70)), closes [#15568](https://github.com/angular/angular/issues/15568)
|
||||
* **language-service:** guard access to `Symbol.members` ([#15529](https://github.com/angular/angular/issues/15529)) ([bf25e94](https://github.com/angular/angular/commit/bf25e94)), closes [#15528](https://github.com/angular/angular/issues/15528)
|
||||
* **language-service:** improve performance of `updateModuleAnalysis()` ([#15543](https://github.com/angular/angular/issues/15543)) ([5597fd3](https://github.com/angular/angular/commit/5597fd3))
|
||||
* **router:** should run CanActivate after CanDeactivate guards ([75478b2](https://github.com/angular/angular/commit/75478b2)), closes [#14059](https://github.com/angular/angular/issues/14059) [#15467](https://github.com/angular/angular/issues/15467)
|
||||
* **router:** shouldn't execute CanLoad when a route has been loaded ([2360676](https://github.com/angular/angular/commit/2360676)), closes [#14475](https://github.com/angular/angular/issues/14475) [#15438](https://github.com/angular/angular/issues/15438)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **router:** don't create new serializer every time UrlTree.toString is called ([#15565](https://github.com/angular/angular/issues/15565)) ([fd61145](https://github.com/angular/angular/commit/fd61145))
|
||||
|
||||
<a name="4.0.0"></a>
|
||||
# [4.0.0](https://github.com/angular/angular/compare/4.0.0-rc.6...4.0.0) invisible-makeover (2017-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** assume queries with no matches as static ([#15429](https://github.com/angular/angular/issues/15429)) ([c8ab5cb](https://github.com/angular/angular/commit/c8ab5cb)), closes [#15417](https://github.com/angular/angular/issues/15417)
|
||||
* **compiler:** correctly handle when `toString` is exported ([#15430](https://github.com/angular/angular/issues/15430)) ([0dda01e](https://github.com/angular/angular/commit/0dda01e)), closes [#15420](https://github.com/angular/angular/issues/15420)
|
||||
* **platform-browser:** setAttribute should work with xmlns namespace ([#14874](https://github.com/angular/angular/issues/14874)) ([92084f2](https://github.com/angular/angular/commit/92084f2)), closes [#14865](https://github.com/angular/angular/issues/14865)
|
||||
* **router:** should pass new data to Observable when query params change ([#15387](https://github.com/angular/angular/issues/15387)) ([08f2f08](https://github.com/angular/angular/commit/08f2f08)), closes [#15290](https://github.com/angular/angular/issues/15290)
|
||||
* prevent strictNullChecks support until [#15432](https://github.com/angular/angular/issues/15432) is fixed ([#15434](https://github.com/angular/angular/issues/15434)) ([b800a0c](https://github.com/angular/angular/commit/b800a0c))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-rc.6"></a>
|
||||
# [4.0.0-rc.6](https://github.com/angular/angular/compare/4.0.0-rc.5...4.0.0-rc.6) (2017-03-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** correct the main entry path in package.json ([#15300](https://github.com/angular/angular/issues/15300)) ([2489e4b](https://github.com/angular/angular/commit/2489e4b))
|
||||
* **animations:** ensure empty animate() steps work at the end of a sequence ([#15328](https://github.com/angular/angular/issues/15328)) ([fbccd5c](https://github.com/angular/angular/commit/fbccd5c)), closes [#15310](https://github.com/angular/angular/issues/15310)
|
||||
* **animations:** ensure enter/leave cancellations work ([#15323](https://github.com/angular/angular/issues/15323)) ([9bf2fb4](https://github.com/angular/angular/commit/9bf2fb4)), closes [#15315](https://github.com/angular/angular/issues/15315)
|
||||
* **animations:** make sure easing values work with web-animations ([#15195](https://github.com/angular/angular/issues/15195)) ([f925910](https://github.com/angular/angular/commit/f925910)), closes [#15115](https://github.com/angular/angular/issues/15115)
|
||||
* **animations:** make sure non-transitioned leave operations cancel existing animations ([#15254](https://github.com/angular/angular/issues/15254)) ([a6fb78e](https://github.com/angular/angular/commit/a6fb78e)), closes [#15213](https://github.com/angular/angular/issues/15213)
|
||||
* **animations:** only process element nodes through the animation engine ([#15268](https://github.com/angular/angular/issues/15268)) ([80075af](https://github.com/angular/angular/commit/80075af)), closes [#15267](https://github.com/angular/angular/issues/15267)
|
||||
* **animations:** only treat view removals as `void` state transitions ([#15245](https://github.com/angular/angular/issues/15245)) ([c66437f](https://github.com/angular/angular/commit/c66437f)), closes [#15223](https://github.com/angular/angular/issues/15223)
|
||||
* **animations:** stringify boolean values as `1` and `0` ([#15311](https://github.com/angular/angular/issues/15311)) ([94da801](https://github.com/angular/angular/commit/94da801)), closes [#15247](https://github.com/angular/angular/issues/15247)
|
||||
* **compiler:** add an empty content for source file of non mapped code. ([#15246](https://github.com/angular/angular/issues/15246)) ([8415910](https://github.com/angular/angular/commit/8415910))
|
||||
* **compiler:** don’t call `check` if we don’t need to ([#15322](https://github.com/angular/angular/issues/15322)) ([764e90f](https://github.com/angular/angular/commit/764e90f))
|
||||
* **compiler:** look for flat module resources using declaration module path ([#15367](https://github.com/angular/angular/issues/15367)) ([90d2518](https://github.com/angular/angular/commit/90d2518)), closes [#15221](https://github.com/angular/angular/issues/15221)
|
||||
* **compiler:** only log template deprecation warning once ([#15364](https://github.com/angular/angular/issues/15364)) ([08d8675](https://github.com/angular/angular/commit/08d8675))
|
||||
* **compiler:** use attribute id to merge translations ([#15302](https://github.com/angular/angular/issues/15302)) ([1d7693c](https://github.com/angular/angular/commit/1d7693c)), closes [#15234](https://github.com/angular/angular/issues/15234)
|
||||
* **compiler-cli:** adding missing format xliff for the extractor ([#15386](https://github.com/angular/angular/issues/15386)) ([a50d79d](https://github.com/angular/angular/commit/a50d79d))
|
||||
* **core:** allow tree shaking of component factories and styles ([#15214](https://github.com/angular/angular/issues/15214)) ([2a0e55f](https://github.com/angular/angular/commit/2a0e55f)), closes [#15181](https://github.com/angular/angular/issues/15181)
|
||||
* **core:** don’t create a comment for components with empty template. ([#15260](https://github.com/angular/angular/issues/15260)) ([f8c075a](https://github.com/angular/angular/commit/f8c075a)), closes [#15143](https://github.com/angular/angular/issues/15143)
|
||||
* **core:** mark components for check when host events trigger. ([#15359](https://github.com/angular/angular/issues/15359)) ([64beae9](https://github.com/angular/angular/commit/64beae9)), closes [#15352](https://github.com/angular/angular/issues/15352)
|
||||
* **core:** only apply `WrappedValue` to the binding of the pipe ([#15257](https://github.com/angular/angular/issues/15257)) ([0c43535](https://github.com/angular/angular/commit/0c43535)), closes [#15116](https://github.com/angular/angular/issues/15116)
|
||||
* **core:** provide `NgModuleRef` in `ViewContainerRef.createComponent`. ([#15350](https://github.com/angular/angular/issues/15350)) ([431eb30](https://github.com/angular/angular/commit/431eb30)), closes [#15241](https://github.com/angular/angular/issues/15241)
|
||||
* **core:** stringify shouldn't throw when toString returns null/undefined ([#14975](https://github.com/angular/angular/issues/14975)) ([8e6995c](https://github.com/angular/angular/commit/8e6995c)), closes [#14948](https://github.com/angular/angular/issues/14948)
|
||||
* **core:** trigger host animations for elements that are removed. ([#15251](https://github.com/angular/angular/issues/15251)) ([0d3e314](https://github.com/angular/angular/commit/0d3e314)), closes [#14813](https://github.com/angular/angular/issues/14813) [#15193](https://github.com/angular/angular/issues/15193)
|
||||
* **core:** update peer dep on zone.js to ^0.8.5 ([#15365](https://github.com/angular/angular/issues/15365)) ([97149f9](https://github.com/angular/angular/commit/97149f9)), closes [#15185](https://github.com/angular/angular/issues/15185)
|
||||
* **forms:** make composition event buffering configurable ([#15256](https://github.com/angular/angular/issues/15256)) ([5efc860](https://github.com/angular/angular/commit/5efc860)), closes [#15079](https://github.com/angular/angular/issues/15079)
|
||||
* **platform-server:** interpret Native view encapsulation as Emulated on the server ([#15155](https://github.com/angular/angular/issues/15155)) ([de3d2ee](https://github.com/angular/angular/commit/de3d2ee))
|
||||
* **platform-server:** setup NoopAnimationsModule in ServerModule by default ([#15131](https://github.com/angular/angular/issues/15131)) ([5c5c2ae](https://github.com/angular/angular/commit/5c5c2ae)), closes [#15098](https://github.com/angular/angular/issues/15098) [#14784](https://github.com/angular/angular/issues/14784)
|
||||
* **platform-server:** throw a better error message for relative URLs ([#15357](https://github.com/angular/angular/issues/15357)) ([15a082c](https://github.com/angular/angular/commit/15a082c)), closes [#15349](https://github.com/angular/angular/issues/15349)
|
||||
* **tsc-wrapped:** emit flat module format correctly on Windows ([#15215](https://github.com/angular/angular/issues/15215)) ([6e9264a](https://github.com/angular/angular/commit/6e9264a)), closes [#15192](https://github.com/angular/angular/issues/15192)
|
||||
* **tsc-wrapped:** use windows friendly path normalization in bundler ([#15374](https://github.com/angular/angular/issues/15374)) ([c584997](https://github.com/angular/angular/commit/c584997)), closes [#15289](https://github.com/angular/angular/issues/15289)
|
||||
* **upgrade:** component injectors should not link the module injector tree ([#15385](https://github.com/angular/angular/issues/15385)) ([ea49a95](https://github.com/angular/angular/commit/ea49a95))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** expose `inputs`, `outputs` and `ngContentSelectors` on `ComponentFactory`. ([#15214](https://github.com/angular/angular/issues/15214)) ([791534f](https://github.com/angular/angular/commit/791534f))
|
||||
* **router:** add `ParamMap.keys` to get a list of parameters ([d3eda7a](https://github.com/angular/angular/commit/d3eda7a))
|
||||
* **router:** introduce `ParamMap` to access parameters ([a755b71](https://github.com/angular/angular/commit/a755b71))
|
||||
* **tsc-wrapped:** record original location of flattened symbols ([#15367](https://github.com/angular/angular/issues/15367)) ([7354949](https://github.com/angular/angular/commit/7354949))
|
||||
* **upgrade:** use `ComponentFactory.inputs/outputs/ngContentSelectors` ([#15214](https://github.com/angular/angular/issues/15214)) ([9429032](https://github.com/angular/angular/commit/9429032))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-rc.5"></a>
|
||||
# [4.0.0-rc.5](https://github.com/angular/angular/compare/4.0.0-rc.4...4.0.0-rc.5) (2017-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler-cli:** update the tsc-wrapped dependency version ([#15226](https://github.com/angular/angular/issues/15226)) ([7fb4528](https://github.com/angular/angular/commit/7fb4528))
|
||||
|
||||
|
||||
|
||||
<a name="4.0.0-rc.4"></a>
|
||||
# [4.0.0-rc.4](https://github.com/angular/angular/compare/4.0.0-rc.3...4.0.0-rc.4) (2017-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always fire callbacks even for noop animations ([#15170](https://github.com/angular/angular/issues/15170)) ([3f38c6f](https://github.com/angular/angular/commit/3f38c6f))
|
||||
* **animations:** make sure easing values are applied to an empty animate() step ([#15174](https://github.com/angular/angular/issues/15174)) ([62d5543](https://github.com/angular/angular/commit/62d5543)), closes [#15115](https://github.com/angular/angular/issues/15115)
|
||||
* **animations:** support multiple state names per state() call ([#15147](https://github.com/angular/angular/issues/15147)) ([36ce0af](https://github.com/angular/angular/commit/36ce0af)), closes [#14732](https://github.com/angular/angular/issues/14732)
|
||||
* **compiler:** always use `ng://` prefix for sourcemap urls ([#15218](https://github.com/angular/angular/issues/15218)) ([994089d](https://github.com/angular/angular/commit/994089d))
|
||||
* **compiler:** fix utf8encode, move to sharted utils, add tests ([#15076](https://github.com/angular/angular/issues/15076)) ([959a03a](https://github.com/angular/angular/commit/959a03a))
|
||||
* **compiler:** generated code should pass `noUnusedLocals` check ([50ab06e](https://github.com/angular/angular/commit/50ab06e)), closes [#14797](https://github.com/angular/angular/issues/14797)
|
||||
* **compiler:** Improve error message for missing annotations ([#14724](https://github.com/angular/angular/issues/14724)) ([3c15916](https://github.com/angular/angular/commit/3c15916))
|
||||
* **compiler:** improve error msg for unexpected closing tags ([#14747](https://github.com/angular/angular/issues/14747)) ([5f9fb91](https://github.com/angular/angular/commit/5f9fb91)), closes [#6652](https://github.com/angular/angular/issues/6652)
|
||||
* **compiler:** make sourcemaps work in AOT mode ([492153a](https://github.com/angular/angular/commit/492153a))
|
||||
* **compiler:** only warn for `[@Injectable](https://github.com/Injectable)` classes with invalid args. ([5c34066](https://github.com/angular/angular/commit/5c34066)), closes [#15003](https://github.com/angular/angular/issues/15003)
|
||||
* **compiler:** shouldn't throw when Symbol is used as DI token ([#13701](https://github.com/angular/angular/issues/13701)) ([8b5c6b2](https://github.com/angular/angular/commit/8b5c6b2)), closes [#13314](https://github.com/angular/angular/issues/13314)
|
||||
* **compiler:** support interface types in injectable constuctors ([#14894](https://github.com/angular/angular/issues/14894)) ([b00fe20](https://github.com/angular/angular/commit/b00fe20)), closes [#12631](https://github.com/angular/angular/issues/12631)
|
||||
* **compiler:** warning prints "WARNING" instead of "ERROR" ([#15125](https://github.com/angular/angular/issues/15125)) ([3b1956b](https://github.com/angular/angular/commit/3b1956b))
|
||||
* **core:** don’t recreate `TemplateRef` when used as a reference. ([#15066](https://github.com/angular/angular/issues/15066)) ([df914ef](https://github.com/angular/angular/commit/df914ef)), closes [#14873](https://github.com/angular/angular/issues/14873)
|
||||
* **core:** don’t throw if queries change during change detection. ([06fc42b](https://github.com/angular/angular/commit/06fc42b)), closes [#14925](https://github.com/angular/angular/issues/14925)
|
||||
* **core:** ErrorHandler should not rethrow an error by default ([#15077](https://github.com/angular/angular/issues/15077)) ([#15208](https://github.com/angular/angular/issues/15208)) ([77fd91d](https://github.com/angular/angular/commit/77fd91d)), closes [#14949](https://github.com/angular/angular/issues/14949) [#15182](https://github.com/angular/angular/issues/15182) [#14316](https://github.com/angular/angular/issues/14316)
|
||||
* **core:** update peer dep on zone.js to ^0.8.4 ([#15209](https://github.com/angular/angular/issues/15209)) ([d2fbbb4](https://github.com/angular/angular/commit/d2fbbb4)), closes [#15180](https://github.com/angular/angular/issues/15180) [#15185](https://github.com/angular/angular/issues/15185)
|
||||
* **core:** use presence of .subscribe to detect observables rather then Symbol.observable ([#15171](https://github.com/angular/angular/issues/15171)) ([6e98757](https://github.com/angular/angular/commit/6e98757)), closes [#14298](https://github.com/angular/angular/issues/14298) [#14473](https://github.com/angular/angular/issues/14473) [#14926](https://github.com/angular/angular/issues/14926)
|
||||
* **forms:** ensure observable validators are properly canceled ([#15132](https://github.com/angular/angular/issues/15132)) ([26d4ce2](https://github.com/angular/angular/commit/26d4ce2))
|
||||
* **forms:** remove equalsTo validator ([#15050](https://github.com/angular/angular/issues/15050)) ([778f7d6](https://github.com/angular/angular/commit/778f7d6))
|
||||
* element injector vs module injector ([#15044](https://github.com/angular/angular/issues/15044)) ([13686bb](https://github.com/angular/angular/commit/13686bb)), closes [#12869](https://github.com/angular/angular/issues/12869) [#12889](https://github.com/angular/angular/issues/12889) [#13885](https://github.com/angular/angular/issues/13885) [#13870](https://github.com/angular/angular/issues/13870)
|
||||
* **http:** Make ResponseOptionsArgs an interface ([#14607](https://github.com/angular/angular/issues/14607)) ([#14623](https://github.com/angular/angular/issues/14623)) ([f1b33ab](https://github.com/angular/angular/commit/f1b33ab)), closes [#13708](https://github.com/angular/angular/issues/13708)
|
||||
* **platform-browser:** prevent clobbered elements from freezing the browser ([a4076c7](https://github.com/angular/angular/commit/a4076c7))
|
||||
* **platform-server:** correctly implement get href in parse5 adapter ([#15022](https://github.com/angular/angular/issues/15022)) ([80649ea](https://github.com/angular/angular/commit/80649ea))
|
||||
* **platform-server:** fix an exception when HostListener('window:scroll') is used on the server ([#15019](https://github.com/angular/angular/issues/15019)) ([4f7d62a](https://github.com/angular/angular/commit/4f7d62a))
|
||||
* correct UMD resolutions for platform-browser_animations ([#15190](https://github.com/angular/angular/issues/15190)) ([923d0c5](https://github.com/angular/angular/commit/923d0c5)), closes [#15114](https://github.com/angular/angular/issues/15114)
|
||||
* don't instantiate providers with ngOnDestroy eagerly. ([#15070](https://github.com/angular/angular/issues/15070)) ([2c5a671](https://github.com/angular/angular/commit/2c5a671)), closes [#14552](https://github.com/angular/angular/issues/14552)
|
||||
* fix path locally to empty.js ([#15073](https://github.com/angular/angular/issues/15073)) ([80112a9](https://github.com/angular/angular/commit/80112a9))
|
||||
* **platform-server:** fix get/set title in parse5 adapter ([#14965](https://github.com/angular/angular/issues/14965)) ([018e5c9](https://github.com/angular/angular/commit/018e5c9))
|
||||
* **platform-server:** handle styles with extra ':'s correctly ([#15189](https://github.com/angular/angular/issues/15189)) ([013d806](https://github.com/angular/angular/commit/013d806))
|
||||
* **platform-server:** support svg elements with namespaced attributes ([#15101](https://github.com/angular/angular/issues/15101)) ([f093501](https://github.com/angular/angular/commit/f093501))
|
||||
* **router:** fix query parameters with multiple values ([#15129](https://github.com/angular/angular/issues/15129)) ([029d0f2](https://github.com/angular/angular/commit/029d0f2)), closes [#14796](https://github.com/angular/angular/issues/14796)
|
||||
* **tsc-wrapped:** emit js files in all cases ([c0e05e6](https://github.com/angular/angular/commit/c0e05e6))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **core:** use flags in `Renderer2.setStyle` instead of booleans ([#15045](https://github.com/angular/angular/issues/15045)) ([ff71eff](https://github.com/angular/angular/commit/ff71eff))
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** support `as` syntax in template/* bindings ([#15025](https://github.com/angular/angular/issues/15025)) ([c10c060](https://github.com/angular/angular/commit/c10c060)), closes [#15020](https://github.com/angular/angular/issues/15020)
|
||||
* **compiler-cli:** support metadata file aliases ([0ab49d4](https://github.com/angular/angular/commit/0ab49d4))
|
||||
* **core:** allow to provide multiple default testing modules ([#15054](https://github.com/angular/angular/issues/15054)) ([6c8638c](https://github.com/angular/angular/commit/6c8638c))
|
||||
* **core:** expose `inputs`, `outputs` and `ngContentSelectors` on `ComponentFactory`. ([1171f91](https://github.com/angular/angular/commit/1171f91))
|
||||
* **upgrade:** support multi-slot projection in upgrade/static ([#14282](https://github.com/angular/angular/issues/14282)) ([914797a](https://github.com/angular/angular/commit/914797a)), closes [#14261](https://github.com/angular/angular/issues/14261)
|
||||
* **upgrade:** use `ComponentFactory.inputs/outputs/ngContentSelectors` ([a3e32fb](https://github.com/angular/angular/commit/a3e32fb))
|
||||
* introduce source maps for templates ([#15011](https://github.com/angular/angular/issues/15011)) ([cdc882b](https://github.com/angular/angular/commit/cdc882b))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Perviously, any provider that had an ngOnDestroy lifecycle hook would be created eagerly.
|
||||
|
||||
Now, only classes that are annotated with @Component, @Directive, @Pipe, @NgModule are eager. Providers only become eager if they are either directly or transitively injected into one of the above.
|
||||
|
||||
This also makes all `useValue` providers eager, which
|
||||
should have no observable impact other than code size.
|
||||
|
||||
**EXPECTED IMPACT**:
|
||||
Making providers eager was an incorrect behavior and never documented.
|
||||
Also, providers that are used by a directive / pipe / ngModule stay eager.
|
||||
So the impact should be rather small.
|
||||
|
||||
* DebugNode.source no longer returns the source location of a node.
|
||||
|
||||
Closes 14013
|
||||
|
||||
* core: (since v4 rc.1)
|
||||
- `Renderer2.setStyle` no longer takes booleans but rather a
|
||||
bit mask of flags.
|
||||
|
||||
|
||||
|
||||
<a name="2.4.10"></a>
|
||||
## [2.4.10](https://github.com/angular/angular/compare/2.4.9...2.4.10) (2017-03-17)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** fix decoding surrogate pairs ([#15154](https://github.com/angular/angular/issues/15154)) ([e5c9bbc](https://github.com/angular/angular/commit/e5c9bbc))
|
||||
* **router:** do not finish bootstrap until all the routes are resolved ([#15121](https://github.com/angular/angular/issues/15121)) ([34403cd](https://github.com/angular/angular/commit/34403cd))
|
||||
|
||||
|
||||
<a name="4.0.0-rc.3"></a>
|
||||
# [4.0.0-rc.3](https://github.com/angular/angular/compare/4.0.0-rc.2...4.0.0-rc.3) (2017-03-10)
|
||||
|
||||
@ -19,7 +285,6 @@
|
||||
- rename `RendererTypeV2` to `RendererType2`
|
||||
- rename `RendererFactoryV2` to `RendererFactory2`
|
||||
|
||||
|
||||
<a name="2.4.9"></a>
|
||||
## [2.4.9](https://github.com/angular/angular/compare/2.4.8...2.4.9) (2017-03-02)
|
||||
|
||||
|
@ -147,7 +147,7 @@ To ensure consistency throughout the source code, keep these rules in mind as yo
|
||||
* All public API methods **must be documented**. (Details TBC).
|
||||
* We follow [Google's JavaScript Style Guide][js-style-guide], but wrap all code at
|
||||
**100 characters**. An automated formatter is available, see
|
||||
[DEVELOPER.md](DEVELOPER.md#clang-format).
|
||||
[DEVELOPER.md](docs/DEVELOPER.md#clang-format).
|
||||
|
||||
## <a name="commit"></a> Commit Message Guidelines
|
||||
|
||||
@ -263,7 +263,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
[coc]: https://github.com/angular/code-of-conduct/blob/master/CODE_OF_CONDUCT.md
|
||||
[commit-message-format]: https://docs.google.com/document/d/1QrDFcIiPjSLDn3EL15IJygNPiHORgU1_OOAqWjiDU5Y/edit#
|
||||
[corporate-cla]: http://code.google.com/legal/corporate-cla-v1.0.html
|
||||
[dev-doc]: https://github.com/angular/angular/blob/master/DEVELOPER.md
|
||||
[dev-doc]: https://github.com/angular/angular/blob/master/docs/DEVELOPER.md
|
||||
[github]: https://github.com/angular/angular
|
||||
[gitter]: https://gitter.im/angular/angular
|
||||
[individual-cla]: http://code.google.com/legal/individual-cla-v1.0.html
|
||||
|
@ -19,6 +19,7 @@
|
||||
"test": "test.ts",
|
||||
"tsconfig": "tsconfig.json",
|
||||
"prefix": "aio",
|
||||
"serviceWorker": true,
|
||||
"styles": [
|
||||
"styles.scss"
|
||||
],
|
||||
|
11
aio/.gitignore
vendored
Normal file
11
aio/.gitignore
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
# Ignore node_modules
|
||||
node_modules
|
||||
|
||||
# Ignore npm/yarn debug log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# Ignore generated content
|
||||
/dist
|
||||
/src/content
|
||||
/.sass-cache
|
@ -19,8 +19,8 @@ ARG AIO_DOMAIN_NAME=ngbuilds.io
|
||||
ARG TEST_AIO_DOMAIN_NAME=$AIO_DOMAIN_NAME.localhost
|
||||
ARG AIO_GITHUB_ORGANIZATION=angular
|
||||
ARG TEST_AIO_GITHUB_ORGANIZATION=angular
|
||||
ARG AIO_GITHUB_TEAM_SLUGS=angular-core
|
||||
ARG TEST_AIO_GITHUB_TEAM_SLUGS=angular-core
|
||||
ARG AIO_GITHUB_TEAM_SLUGS=angular-core,aio-contributors
|
||||
ARG TEST_AIO_GITHUB_TEAM_SLUGS=angular-core,aio-contributors
|
||||
ARG AIO_NGINX_HOSTNAME=$AIO_DOMAIN_NAME
|
||||
ARG TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_DOMAIN_NAME
|
||||
ARG AIO_NGINX_PORT_HTTP=80
|
||||
@ -42,6 +42,7 @@ ENV AIO_BUILDS_DIR=$AIO_BUILDS_DIR TEST_AIO_BUILDS_DIR=$TEST
|
||||
AIO_GITHUB_TEAM_SLUGS=$AIO_GITHUB_TEAM_SLUGS TEST_AIO_GITHUB_TEAM_SLUGS=$TEST_AIO_GITHUB_TEAM_SLUGS \
|
||||
AIO_LOCALCERTS_DIR=/etc/ssl/localcerts TEST_AIO_LOCALCERTS_DIR=/etc/ssl/localcerts-test \
|
||||
AIO_NGINX_HOSTNAME=$AIO_NGINX_HOSTNAME TEST_AIO_NGINX_HOSTNAME=$TEST_AIO_NGINX_HOSTNAME \
|
||||
AIO_NGINX_LOGS_DIR=/var/log/aio/nginx TEST_AIO_NGINX_LOGS_DIR=/var/log/aio/nginx-test \
|
||||
AIO_NGINX_PORT_HTTP=$AIO_NGINX_PORT_HTTP TEST_AIO_NGINX_PORT_HTTP=$TEST_AIO_NGINX_PORT_HTTP \
|
||||
AIO_NGINX_PORT_HTTPS=$AIO_NGINX_PORT_HTTPS TEST_AIO_NGINX_PORT_HTTPS=$TEST_AIO_NGINX_PORT_HTTPS \
|
||||
AIO_REPO_SLUG=$AIO_REPO_SLUG TEST_AIO_REPO_SLUG=$TEST_AIO_REPO_SLUG \
|
||||
@ -82,14 +83,15 @@ RUN yarn global add pm2@2
|
||||
COPY cronjobs/aio-builds-cleanup /etc/cron.d/
|
||||
RUN chmod 0744 /etc/cron.d/aio-builds-cleanup
|
||||
RUN crontab /etc/cron.d/aio-builds-cleanup
|
||||
RUN printenv | grep AIO_ >> /etc/environment
|
||||
|
||||
|
||||
# Set up dnsmasq
|
||||
COPY dnsmasq/dnsmasq.conf /etc/
|
||||
RUN sed -i "s|{{\$AIO_NGINX_HOSTNAME}}|$AIO_NGINX_HOSTNAME|" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$AIO_UPLOAD_HOSTNAME|" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$TEST_AIO_NGINX_HOSTNAME}}|$TEST_AIO_NGINX_HOSTNAME|" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$TEST_AIO_UPLOAD_HOSTNAME}}|$TEST_AIO_UPLOAD_HOSTNAME|" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_HOSTNAME}}|$AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$AIO_UPLOAD_HOSTNAME|g" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$TEST_AIO_NGINX_HOSTNAME}}|$TEST_AIO_NGINX_HOSTNAME|g" /etc/dnsmasq.conf
|
||||
RUN sed -i "s|{{\$TEST_AIO_UPLOAD_HOSTNAME}}|$TEST_AIO_UPLOAD_HOSTNAME|g" /etc/dnsmasq.conf
|
||||
|
||||
|
||||
# Set up SSL/TLS certificates
|
||||
@ -105,25 +107,27 @@ RUN update-ca-certificates
|
||||
RUN rm /etc/nginx/sites-enabled/*
|
||||
|
||||
COPY nginx/aio-builds.conf /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$AIO_BUILDS_DIR|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_DOMAIN_NAME}}|$AIO_DOMAIN_NAME|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$AIO_LOCALCERTS_DIR|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$AIO_NGINX_PORT_HTTP|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$AIO_NGINX_PORT_HTTPS|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$AIO_UPLOAD_HOSTNAME|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_MAX_SIZE}}|$AIO_UPLOAD_MAX_SIZE|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_PORT}}|$AIO_UPLOAD_PORT|" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$AIO_BUILDS_DIR|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_DOMAIN_NAME}}|$AIO_DOMAIN_NAME|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$AIO_LOCALCERTS_DIR|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$AIO_NGINX_LOGS_DIR|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$AIO_NGINX_PORT_HTTP|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$AIO_NGINX_PORT_HTTPS|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$AIO_UPLOAD_HOSTNAME|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_MAX_SIZE}}|$AIO_UPLOAD_MAX_SIZE|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_PORT}}|$AIO_UPLOAD_PORT|g" /etc/nginx/sites-available/aio-builds-prod.conf
|
||||
RUN ln -s /etc/nginx/sites-available/aio-builds-prod.conf /etc/nginx/sites-enabled/aio-builds-prod.conf
|
||||
|
||||
COPY nginx/aio-builds.conf /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$TEST_AIO_BUILDS_DIR|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_DOMAIN_NAME}}|$TEST_AIO_DOMAIN_NAME|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$TEST_AIO_LOCALCERTS_DIR|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$TEST_AIO_NGINX_PORT_HTTP|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$TEST_AIO_NGINX_PORT_HTTPS|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$TEST_AIO_UPLOAD_HOSTNAME|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_MAX_SIZE}}|$TEST_AIO_UPLOAD_MAX_SIZE|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_PORT}}|$TEST_AIO_UPLOAD_PORT|" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_BUILDS_DIR}}|$TEST_AIO_BUILDS_DIR|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_DOMAIN_NAME}}|$TEST_AIO_DOMAIN_NAME|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_LOCALCERTS_DIR}}|$TEST_AIO_LOCALCERTS_DIR|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_LOGS_DIR}}|$TEST_AIO_NGINX_LOGS_DIR|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTP}}|$TEST_AIO_NGINX_PORT_HTTP|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_NGINX_PORT_HTTPS}}|$TEST_AIO_NGINX_PORT_HTTPS|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_HOSTNAME}}|$TEST_AIO_UPLOAD_HOSTNAME|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_MAX_SIZE}}|$TEST_AIO_UPLOAD_MAX_SIZE|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN sed -i "s|{{\$AIO_UPLOAD_PORT}}|$TEST_AIO_UPLOAD_PORT|g" /etc/nginx/sites-available/aio-builds-test.conf
|
||||
RUN ln -s /etc/nginx/sites-available/aio-builds-test.conf /etc/nginx/sites-enabled/aio-builds-test.conf
|
||||
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
# Periodically clean up builds that do not correspond to currently open PRs
|
||||
0 4 * * * root /usr/local/bin/aio-clean-up >> /var/log/cron.log 2>&1
|
||||
0 12 * * * root /usr/local/bin/aio-clean-up >> /var/log/cron.log 2>&1
|
||||
|
@ -1,9 +1,22 @@
|
||||
# Redirect all HTTP traffic to HTTPS
|
||||
server {
|
||||
server_name _;
|
||||
|
||||
listen {{$AIO_NGINX_PORT_HTTP}} default_server;
|
||||
listen [::]:{{$AIO_NGINX_PORT_HTTP}};
|
||||
|
||||
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
|
||||
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
|
||||
|
||||
# Ideally we want 308 (permanent + keep original method),
|
||||
# but it is relatively new and not supported by some clients (e.g. cURL).
|
||||
return 307 https://$host:{{$AIO_NGINX_PORT_HTTPS}}$request_uri;
|
||||
}
|
||||
|
||||
# Serve PR-preview requests
|
||||
server {
|
||||
server_name "~^pr(?<pr>[1-9][0-9]*)-(?<sha>[0-9a-f]{40})\.";
|
||||
|
||||
listen {{$AIO_NGINX_PORT_HTTP}};
|
||||
listen [::]:{{$AIO_NGINX_PORT_HTTP}};
|
||||
listen {{$AIO_NGINX_PORT_HTTPS}} ssl;
|
||||
listen [::]:{{$AIO_NGINX_PORT_HTTPS}} ssl;
|
||||
|
||||
@ -14,31 +27,39 @@ server {
|
||||
disable_symlinks on from=$document_root;
|
||||
index index.html;
|
||||
|
||||
location / {
|
||||
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
|
||||
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
|
||||
|
||||
location "~/[^/]+\.[^/]+$" {
|
||||
try_files $uri $uri/ =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html =404;
|
||||
}
|
||||
}
|
||||
|
||||
# Handle all other requests
|
||||
server {
|
||||
server_name _;
|
||||
|
||||
listen {{$AIO_NGINX_PORT_HTTP}} default_server;
|
||||
listen [::]:{{$AIO_NGINX_PORT_HTTP}};
|
||||
listen {{$AIO_NGINX_PORT_HTTPS}} ssl default_server;
|
||||
listen [::]:{{$AIO_NGINX_PORT_HTTPS}} ssl;
|
||||
|
||||
ssl_certificate {{$AIO_LOCALCERTS_DIR}}/{{$AIO_DOMAIN_NAME}}.crt;
|
||||
ssl_certificate_key {{$AIO_LOCALCERTS_DIR}}/{{$AIO_DOMAIN_NAME}}.key;
|
||||
|
||||
access_log {{$AIO_NGINX_LOGS_DIR}}/access.log;
|
||||
error_log {{$AIO_NGINX_LOGS_DIR}}/error.log;
|
||||
|
||||
# Health check
|
||||
location "~^\/health-check\/?$" {
|
||||
location "~^/health-check/?$" {
|
||||
add_header Content-Type text/plain;
|
||||
return 200 '';
|
||||
}
|
||||
|
||||
# Upload builds
|
||||
location "~^\/create-build\/(?<pr>[1-9][0-9]*)\/(?<sha>[0-9a-f]{40})\/?$" {
|
||||
location "~^/create-build/(?<pr>[1-9][0-9]*)/(?<sha>[0-9a-f]{40})/?$" {
|
||||
if ($request_method != "POST") {
|
||||
add_header Allow "POST";
|
||||
return 405;
|
||||
|
@ -12,6 +12,8 @@ _main();
|
||||
|
||||
// Functions
|
||||
function _main() {
|
||||
console.log(`[${new Date()}] - Cleaning up builds...`);
|
||||
|
||||
const buildCleaner = new BuildCleaner(AIO_BUILDS_DIR, AIO_REPO_SLUG, AIO_GITHUB_TOKEN);
|
||||
|
||||
buildCleaner.cleanUp().catch(err => {
|
||||
|
@ -3,16 +3,41 @@ import * as path from 'path';
|
||||
import {helper as h} from './helper';
|
||||
|
||||
// Tests
|
||||
h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpperCase()})`, () => {
|
||||
describe(`nginx`, () => {
|
||||
|
||||
beforeEach(() => jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000);
|
||||
afterEach(() => h.cleanUp());
|
||||
|
||||
|
||||
it('should redirect HTTP to HTTPS', done => {
|
||||
const httpHost = `${h.nginxHostname}:${h.nginxPortHttp}`;
|
||||
const httpsHost = `${h.nginxHostname}:${h.nginxPortHttps}`;
|
||||
const urlMap = {
|
||||
[`http://${httpHost}/`]: `https://${httpsHost}/`,
|
||||
[`http://${httpHost}/foo`]: `https://${httpsHost}/foo`,
|
||||
[`http://foo.${httpHost}/`]: `https://foo.${httpsHost}/`,
|
||||
};
|
||||
|
||||
const verifyRedirection = (httpUrl: string) => h.runCmd(`curl -i ${httpUrl}`).then(result => {
|
||||
h.verifyResponse(307)(result);
|
||||
|
||||
const headers = result.stdout.split(/(?:\r?\n){2,}/)[0];
|
||||
expect(headers).toContain(`Location: ${urlMap[httpUrl]}`);
|
||||
});
|
||||
|
||||
Promise.
|
||||
all(Object.keys(urlMap).map(verifyRedirection)).
|
||||
then(done);
|
||||
});
|
||||
|
||||
|
||||
h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpperCase()})`, () => {
|
||||
const hostname = h.nginxHostname;
|
||||
const host = `${hostname}:${port}`;
|
||||
const pr = '9';
|
||||
const sha9 = '9'.repeat(40);
|
||||
const sha0 = '0'.repeat(40);
|
||||
|
||||
beforeEach(() => jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000);
|
||||
afterEach(() => h.cleanUp());
|
||||
|
||||
|
||||
describe(`pr<pr>-<sha>.${host}/*`, () => {
|
||||
|
||||
@ -24,21 +49,21 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpp
|
||||
|
||||
it('should return /index.html', done => {
|
||||
const origin = `${scheme}://pr${pr}-${sha9}.${host}`;
|
||||
const bodyegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
|
||||
Promise.all([
|
||||
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyegex)),
|
||||
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyegex)),
|
||||
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyegex)),
|
||||
h.runCmd(`curl -iL ${origin}/index.html`).then(h.verifyResponse(200, bodyRegex)),
|
||||
h.runCmd(`curl -iL ${origin}/`).then(h.verifyResponse(200, bodyRegex)),
|
||||
h.runCmd(`curl -iL ${origin}`).then(h.verifyResponse(200, bodyRegex)),
|
||||
]).then(done);
|
||||
});
|
||||
|
||||
|
||||
it('should return /foo/bar.js', done => {
|
||||
const bodyegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /foo/bar\\.js$`);
|
||||
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha9}.${host}/foo/bar.js`).
|
||||
then(h.verifyResponse(200, bodyegex)).
|
||||
then(h.verifyResponse(200, bodyRegex)).
|
||||
then(done);
|
||||
});
|
||||
|
||||
@ -51,13 +76,23 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpp
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown paths', done => {
|
||||
it('should respond with 404 for unknown paths to files', done => {
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha9}.${host}/foo/baz.css`).
|
||||
then(h.verifyResponse(404)).
|
||||
then(done);
|
||||
});
|
||||
|
||||
|
||||
it('should rewrite to \'index.html\' for unknown paths that don\'t look like files', done => {
|
||||
const bodyRegex = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
|
||||
Promise.all([
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha9}.${host}/foo/baz`).then(h.verifyResponse(200, bodyRegex)),
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha9}.${host}/foo/baz/`).then(h.verifyResponse(200, bodyRegex)),
|
||||
]).then(done);
|
||||
});
|
||||
|
||||
|
||||
it('should respond with 404 for unknown PRs/SHAs', done => {
|
||||
const otherPr = 54321;
|
||||
const otherSha = '8'.repeat(40);
|
||||
@ -92,12 +127,14 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpp
|
||||
});
|
||||
|
||||
|
||||
it('should accept SHAs with leading zeros (but not ignore them)', done => {
|
||||
const bodyegex = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
||||
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||
const bodyRegex9 = new RegExp(`^PR: ${pr} | SHA: ${sha9} | File: /index\\.html$`);
|
||||
const bodyRegex0 = new RegExp(`^PR: ${pr} | SHA: ${sha0} | File: /index\\.html$`);
|
||||
|
||||
Promise.all([
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-0${sha9}.${host}`).then(h.verifyResponse(404)),
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha0}.${host}`).then(h.verifyResponse(200, bodyegex)),
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha9}.${host}`).then(h.verifyResponse(200, bodyRegex9)),
|
||||
h.runCmd(`curl -iL ${scheme}://pr${pr}-${sha0}.${host}`).then(h.verifyResponse(200, bodyRegex0)),
|
||||
]).then(done);
|
||||
});
|
||||
|
||||
@ -194,7 +231,7 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpp
|
||||
});
|
||||
|
||||
|
||||
it('should accept SHAs with leading zeros (but not ignore them)', done => {
|
||||
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||
const cmdPrefix = `curl -iLX POST ${scheme}://${host}/create-build/${pr}`;
|
||||
const bodyRegex = /Missing or empty 'AUTHORIZATION' header/;
|
||||
|
||||
@ -229,4 +266,6 @@ h.runForAllSupportedSchemes((scheme, port) => describe(`nginx (on ${scheme.toUpp
|
||||
|
||||
});
|
||||
|
||||
}));
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -84,9 +84,10 @@ describe('upload-server (on HTTP)', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should accept SHAs with leading zeros (but not ignore them)', done => {
|
||||
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||
Promise.all([
|
||||
h.runCmd(`${curl} http://${host}/create-build/${pr}/0${sha9}`).then(h.verifyResponse(404)),
|
||||
h.runCmd(`${curl} http://${host}/create-build/${pr}/${sha9}`).then(h.verifyResponse(500)),
|
||||
h.runCmd(`${curl} http://${host}/create-build/${pr}/${sha0}`).then(h.verifyResponse(500)),
|
||||
]).then(done);
|
||||
});
|
||||
|
@ -65,8 +65,12 @@ describe('BuildVerifier', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(bv.verify(pr, createAuthHeader())).toEqual(jasmine.any(Promise));
|
||||
it('should return a promise', done => {
|
||||
const promise = bv.verify(pr, createAuthHeader());
|
||||
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||
// to avoid running the actual `bvGetPrAuthorTeamMembership()`.
|
||||
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
});
|
||||
|
||||
|
||||
@ -194,8 +198,12 @@ describe('BuildVerifier', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should return a promise', () => {
|
||||
expect(bv.getPrAuthorTeamMembership(pr)).toEqual(jasmine.any(Promise));
|
||||
it('should return a promise', done => {
|
||||
const promise = bv.getPrAuthorTeamMembership(pr);
|
||||
promise.then(done); // Do not complete the test (and release the spies) synchronously
|
||||
// to avoid running the actual `GithubTeams#isMemberBySlug()`.
|
||||
|
||||
expect(promise).toEqual(jasmine.any(Promise));
|
||||
});
|
||||
|
||||
|
||||
|
@ -323,18 +323,16 @@ describe('uploadServerFactory', () => {
|
||||
});
|
||||
|
||||
|
||||
it('should accept SHAs with leading zeros (but not ignore them)', done => {
|
||||
const sha41 = '0'.repeat(41);
|
||||
it('should accept SHAs with leading zeros (but not trim the zeros)', done => {
|
||||
const sha40 = '0'.repeat(40);
|
||||
const sha41 = `0${sha40}`;
|
||||
|
||||
const request41 = agent.get(`/create-build/${pr}/${sha41}`);
|
||||
const request40 = agent.get(`/create-build/${pr}/${sha40}`).
|
||||
set('AUTHORIZATION', 'foo').
|
||||
set('X-FILE', 'bar');
|
||||
const request40 = agent.get(`/create-build/${pr}/${sha40}`).set('AUTHORIZATION', 'foo').set('X-FILE', 'bar');
|
||||
const request41 = agent.get(`/create-build/${pr}/${sha41}`).set('AUTHORIZATION', 'baz').set('X-FILE', 'qux');
|
||||
|
||||
Promise.all([
|
||||
promisifyRequest(request41.expect(404)),
|
||||
promisifyRequest(request40.expect(201)),
|
||||
promisifyRequest(request41.expect(404)),
|
||||
]).then(done, done.fail);
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
#!/bin/bash
|
||||
set -e -o pipefail
|
||||
|
||||
# Set up env variables
|
||||
export AIO_GITHUB_TOKEN=$(head -c -1 /aio-secrets/GITHUB_TOKEN 2>/dev/null)
|
||||
|
||||
# Run the clean-up
|
||||
node $AIO_SCRIPTS_JS_DIR/dist/lib/clean-up >> /var/log/aio/clean-up.log 2>&1
|
||||
|
@ -6,6 +6,9 @@ exec 2>&1
|
||||
|
||||
# Start the services
|
||||
echo [`date`] - Starting services...
|
||||
mkdir -p $AIO_NGINX_LOGS_DIR
|
||||
mkdir -p $TEST_AIO_NGINX_LOGS_DIR
|
||||
|
||||
service rsyslog start
|
||||
service cron start
|
||||
service dnsmasq start
|
||||
|
@ -1,31 +0,0 @@
|
||||
# VM Setup Instructions
|
||||
|
||||
- Set up secrets (access tokens, passwords, etc)
|
||||
- Set up docker
|
||||
- Attach persistent disk
|
||||
- Build docker image (+ checkout repo)
|
||||
- Run image (+ setup for run on boot)
|
||||
|
||||
|
||||
## Build image
|
||||
- `<aio-builds-setup-dir>/build.sh [<name>[:<tag>] [--build-arg <NAME>=<value> ...]]`
|
||||
|
||||
|
||||
## Run image
|
||||
- `sudo docker run \
|
||||
-d \
|
||||
--dns 127.0.0.1 \
|
||||
--name <instance-name> \
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
--restart unless-stopped \
|
||||
[-v <host-cert-dir>:/etc/ssl/localcerts:ro] \
|
||||
-v <host-secrets-dir>:/aio-secrets:ro \
|
||||
-v <host-builds-dir>:/var/www/aio-builds \
|
||||
<name>[:<tag>]
|
||||
`
|
||||
|
||||
|
||||
## Questions
|
||||
- Do we care to keep logs (e.g. cron, nginx, aio-upload-server, aio-clean-up, pm2) outside of the container?
|
||||
- Instead of creating new comments for each commit, update the original comment?
|
28
aio/aio-builds-setup/docs/_TOC.md
Normal file
28
aio/aio-builds-setup/docs/_TOC.md
Normal file
@ -0,0 +1,28 @@
|
||||
# VM Setup Instructions
|
||||
|
||||
|
||||
## Overview
|
||||
- [General overview](overview--general.md)
|
||||
- [Security model](overview--security-model.md)
|
||||
- [Available Commands](overview--scripts-and-commands.md)
|
||||
|
||||
|
||||
## Setting up the VM
|
||||
- [Set up secrets](vm-setup--set-up-secrets.md)
|
||||
- [Set up docker](vm-setup--set-up-docker.md)
|
||||
- [Attach persistent disk](vm-setup--attach-persistent-disk.md)
|
||||
- [Create host directories and files](vm-setup--create-host-dirs-and-files.md)
|
||||
- [Create docker image](vm-setup--create-docker-image.md)
|
||||
|
||||
|
||||
## Configuring the docker image
|
||||
- [Available environment variables](image-config--environment-variables.md)
|
||||
|
||||
|
||||
## Starting the docker container
|
||||
- [Create docker image](vm-setup--start-docker-container.md)
|
||||
|
||||
|
||||
## Miscellaneous
|
||||
- [Debug docker container](misc--debug-docker-container.md)
|
||||
- [Integrate with CI](misc--integrate-with-ci.md)
|
@ -0,0 +1,52 @@
|
||||
# Image config - Environment variables
|
||||
|
||||
|
||||
Below is a list of environment variables that can be configured when creating the docker image (as
|
||||
described [here](vm-setup--create-docker-image.md)). An up-to-date list of the configurable
|
||||
environment variables and their default values can be found in the
|
||||
[Dockerfile](../dockerbuild/Dockerfile).
|
||||
|
||||
**Note:**
|
||||
Each variable has a `TEST_` prefixed counterpart, which is used for testing purposes. In most cases
|
||||
you don't need to specify values for those.
|
||||
|
||||
- `AIO_BUILDS_DIR`:
|
||||
The directory (inside the container) where the uploaded build artifacts are kept.
|
||||
|
||||
- `AIO_DOMAIN_NAME`:
|
||||
The domain name of the server.
|
||||
|
||||
- `AIO_GITHUB_ORGANIZATION`:
|
||||
The GitHub organization whose teams arew whitelisted for accepting uploads.
|
||||
See also `AIO_GITHUB_TEAM_SLUGS`.
|
||||
|
||||
- `AIO_GITHUB_TEAM_SLUGS`:
|
||||
A comma-separated list of teams, whose authors are allowed to upload PRs.
|
||||
See also `AIO_GITHUB_ORGANIZATION`.
|
||||
|
||||
- `AIO_NGINX_HOSTNAME`:
|
||||
The internal hostname for accessing the nginx server. This is mostly used for performing a
|
||||
periodic health-check.
|
||||
|
||||
- `AIO_NGINX_PORT_HTTP`:
|
||||
The port number on which nginx listens for HTTP connections. This should be mapped to the
|
||||
corresponding port on the host VM (as described [here](vm-setup--start-docker-container.md)).
|
||||
|
||||
- `AIO_NGINX_PORT_HTTPS`:
|
||||
The port number on which nginx listens for HTTPS connections. This should be mapped to the
|
||||
corresponding port on the host VM (as described [here](vm-setup--start-docker-container.md)).
|
||||
|
||||
- `AIO_REPO_SLUG`:
|
||||
The repository slug (in the form `<user>/<repo>`) for which PRs will be uploaded.
|
||||
|
||||
- `AIO_UPLOAD_HOSTNAME`:
|
||||
The internal hostname for accessing the Node.js upload-server. This is used by nginx for
|
||||
delegating upload requests and also for performing a periodic health-check.
|
||||
|
||||
- `AIO_UPLOAD_MAX_SIZE`:
|
||||
The maximum allowed size for the uploaded gzip archive containing the build artifacts. Files
|
||||
larger than this will be rejected.
|
||||
|
||||
- `AIO_UPLOAD_PORT`:
|
||||
The port number on which the Node.js upload-server listens for HTTP connections. This is used by
|
||||
nginx for delegating upload requests and also for performing a periodic health-check.
|
12
aio/aio-builds-setup/docs/misc--debug-docker-container.md
Normal file
12
aio/aio-builds-setup/docs/misc--debug-docker-container.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Miscellaneous - Debug docker container
|
||||
|
||||
|
||||
TODO (gkalpak): Add docs. Mention:
|
||||
- `aio-health-check`
|
||||
- `aio-verify-setup`
|
||||
- Test nginx accessible at:
|
||||
- `http://$TEST_AIO_NGINX_HOTNAME:$TEST_AIO_NGINX_PORT_HTTP`
|
||||
- `https://$TEST_AIO_NGINX_HOTNAME:$TEST_AIO_NGINX_PORT_HTTPS`
|
||||
- Test upload-server accessible at:
|
||||
- `http://$TEST_AIO_UPLOAD_HOTNAME:$TEST_AIO_UPLOAD_PORT`
|
||||
- Local DNS (via dnsmasq) maps the above hostnames to 127.0.0.1
|
12
aio/aio-builds-setup/docs/misc--integrate-with-ci.md
Normal file
12
aio/aio-builds-setup/docs/misc--integrate-with-ci.md
Normal file
@ -0,0 +1,12 @@
|
||||
# Miscellaneous - Integrate with CI
|
||||
|
||||
|
||||
TODO (gkalpak): Add docs. Mention:
|
||||
- Travis' JWT addon (+ limitations).
|
||||
Relevant files: `.travis.yml`
|
||||
- Testing on CI.
|
||||
Relevant files: `ci/test-aio.sh`, `aio/aio-builds-setup/scripts/test.sh`
|
||||
- Preverifying on CI.
|
||||
Relevant files: `ci/deploy.sh`, `aio/aio-builds-setup/scripts/travis-preverify-pr.sh`
|
||||
- Deploying from CI.
|
||||
Relevant files: `ci/deploy.sh`, `aio/scripts/deploy-preview.sh`
|
84
aio/aio-builds-setup/docs/overview--general.md
Normal file
84
aio/aio-builds-setup/docs/overview--general.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Overview - General
|
||||
|
||||
|
||||
## Objective
|
||||
Whenever a PR job is run on Travis, we want to build `angular.io` and upload the build artifacts to
|
||||
a publicly accessible server so that collaborators (developers, designers, authors, etc) can preview
|
||||
the changes without having to checkout and build the app locally.
|
||||
|
||||
|
||||
## Source code
|
||||
In order to make it easier to administer the server and version-control the setup, we are using
|
||||
[docker](https://www.docker.com) to run a container on a VM. The Dockerfile and all other files
|
||||
necessary for creating the docker container are stored (and versioned) along with the angular.io
|
||||
project's source code (currently part of the angular/angular repo) in the `aio-builds-setup/`
|
||||
directory.
|
||||
|
||||
|
||||
## Setup
|
||||
The VM is hosted on [Google Compute Engine](https://cloud.google.com/compute/). The host OS is
|
||||
debian:jessie. For more info how to set up the host VM take a look at the "Setting up the VM"
|
||||
section in [TOC](_TOC.md).
|
||||
|
||||
|
||||
## Security model
|
||||
Since we are managing a public server, it is important to take appropriate measures in order to
|
||||
prevent abuse. For more details on the challenges and the chosen approach take a look at the
|
||||
[security model](overview--security-model.md).
|
||||
|
||||
|
||||
## The 10000 feet view
|
||||
This section gives a brief summary of the several operations performed on CI and by the docker
|
||||
container:
|
||||
|
||||
|
||||
### On CI (Travis)
|
||||
- Build job completes successfully (i.e. build succeeds and tests pass).
|
||||
- The CI script checks whether the build job was initiated by a PR against the angular/angular
|
||||
master branch.
|
||||
- The CI script checks whether the PR has touched any files inside the angular.io project directory
|
||||
(currently `aio/`).
|
||||
- The CI script checks whether the author of the PR is a member of one of the whitelisted GitHub
|
||||
teams (and therefore allowed to upload).
|
||||
**Note:**
|
||||
For security reasons, the same checks will be performed on the server as well. This is an optional
|
||||
step with the purpose of:
|
||||
1. Avoiding the wasted overhead associated with uploads that are going to be rejected (e.g.
|
||||
building the artifacts, sending them to the server, running checks on the server, etc).
|
||||
2. Avoiding failing the build (due to an error response from the server) or requiring additional
|
||||
logic for detecting the reasons of the failure.
|
||||
- The CI script gzip and upload the build artifacts to the server.
|
||||
|
||||
More info on how to set things up on CI can be found [here](misc--integrate-with-ci.md).
|
||||
|
||||
|
||||
### Uploading build artifacts
|
||||
- nginx receives upload request.
|
||||
- nginx checks that the uploaded gzip archive does not exceed the specified max file size, stores it
|
||||
in a temporary location and passes the filepath to the Node.js upload-server.
|
||||
- The upload-server verifies that the uploaded file is not trying to overwrite an existing build,
|
||||
and runs several checks to determine whether the request should be accepted (more details can be
|
||||
found [here](overview--security-model.md)).
|
||||
- The upload-server deploys the artifacts to a sub-directory named after the PR number and SHA:
|
||||
`<PR>/<SHA>/`
|
||||
- The upload-server posts a comment on the corresponding PR on GitHub mentioning the SHA and the
|
||||
the link where the preview can be found.
|
||||
|
||||
|
||||
### Serving build artifacts
|
||||
- nginx receives a request for an uploaded resource on a subdomain corresponding to the PR and SHA.
|
||||
E.g.: `pr<PR>-<SHA>.ngbuilds.io/path/to/resurce`
|
||||
- nginx maps the subdomain to the correct sub-direcory and serves the resource.
|
||||
E.g.: `/<PR>/<SHA>/path/to/resource`
|
||||
|
||||
|
||||
### Removing obsolete artifacts
|
||||
In order to avoid flooding the disk with unnecessary build artifacts, there is a cronjob that runs a
|
||||
clean-up tasks once a day. The task retrieves all open PRs from GitHub and removes all directories
|
||||
that do not correspond with an open PR.
|
||||
|
||||
|
||||
### Health-check
|
||||
The docker service runs a periodic health-check that verifies the running conditions of the
|
||||
container. This includes verifying the status of specific system services, the responsiveness of
|
||||
nginx and the upload-server and internet connectivity.
|
55
aio/aio-builds-setup/docs/overview--scripts-and-commands.md
Normal file
55
aio/aio-builds-setup/docs/overview--scripts-and-commands.md
Normal file
@ -0,0 +1,55 @@
|
||||
# Overview - Scripts and Commands
|
||||
|
||||
|
||||
This is an overview of the available scripts and commands.
|
||||
|
||||
|
||||
## Scripts
|
||||
The scripts are located inside `<aio-builds-setup-dir>/scripts/`. The following scripts are
|
||||
available:
|
||||
|
||||
- `build.sh`:
|
||||
Can be used for creating a preconfigured docker image.
|
||||
See [here](vm-setup--create-docker-image.md) for more info.
|
||||
|
||||
- `test.sh`
|
||||
Can be used for running the tests for `<aio-builds-setup-dir>/dockerbuild/scripts-js/`. This is
|
||||
useful for CI integration. See [here](misc--integrate-with-ci.md) for more info.
|
||||
|
||||
- `travis-preverify-pr.sh`
|
||||
Can be used for "preverifying" a PR before uploading the artifacts to the server. It checks that
|
||||
the author of the PR a member of one of the specified GitHub teams and therefore allowed to upload
|
||||
build artifacts. This is useful for CI integration. See [here](misc--integrate-with-ci.md) for
|
||||
more info.
|
||||
|
||||
|
||||
## Commands
|
||||
The following commands are available globally from inside the docker container. They are either used
|
||||
by the container to perform its various operations or can be used ad-hoc, mainly for testing
|
||||
purposes. Each command is backed by a corresponding script inside
|
||||
`<aio-builds-setup-dir>/dockerbuild/scripts-sh/`.
|
||||
|
||||
- `aio-clean-up`:
|
||||
Cleans up the builds directory by removing the artifacts that do not correspond to an open PR.
|
||||
_It is run as a daily cronjob._
|
||||
|
||||
- `aio-health-check`:
|
||||
Runs a basic health-check, verifying that the necessary services are running, the servers are
|
||||
responding and there is a working internet connection.
|
||||
_It is used periodically by docker for determining the container's health status._
|
||||
|
||||
- `aio-init`:
|
||||
Initializes the container (mainly by starting the necessary services).
|
||||
_It is run (by default) when starting the container._
|
||||
|
||||
- `aio-upload-server-prod`:
|
||||
Spins up a Node.js upload-server instance.
|
||||
_It is used in `aio-init` (see above) during initialization._
|
||||
|
||||
- `aio-upload-server-test`:
|
||||
Spins up a Node.js upload-server instance for tests.
|
||||
_It is used in `aio-verify-setup` (see below) for running tests._
|
||||
|
||||
- `aio-verify-setup`:
|
||||
Runs a suite of e2e-like tests, mainly verifying the correct (inter)operation of nginx and the
|
||||
Node.js upload-server.
|
116
aio/aio-builds-setup/docs/overview--security-model.md
Normal file
116
aio/aio-builds-setup/docs/overview--security-model.md
Normal file
@ -0,0 +1,116 @@
|
||||
# Overview - Security model
|
||||
|
||||
|
||||
Whenever a PR job is run on Travis, we want to build `angular.io` and upload the build artifacts to
|
||||
a publicly accessible server so that collaborators (developers, designers, authors, etc) can preview
|
||||
the changes without having to checkout and build the app locally.
|
||||
|
||||
This document discusses the security considerations associated with uploading build artifacts as
|
||||
part of the CI setup and serving them publicly.
|
||||
|
||||
|
||||
## Security objectives
|
||||
|
||||
- **Prevent uploading arbitrary content to our servers.**
|
||||
Since there is no restriction on who can submit a PR, we cannot allow any PR's build artifacts to
|
||||
be uploaded.
|
||||
|
||||
- **Prevent overwriting other peoples uploaded content.**
|
||||
There needs to be a mechanism in place to ensure that the uploaded content does indeed correspond
|
||||
to the PR indicated by its URL.
|
||||
|
||||
- **Prevent arbitrary access on the server.**
|
||||
Since the PR author has full access over the build artifacts that would be uploaded, we must
|
||||
ensure that the uploaded files will not enable arbitrary access to the server or expose sensitive
|
||||
info.
|
||||
|
||||
|
||||
## Issues / Caveats
|
||||
|
||||
- Because the PR author can change the scripts run on CI, any security mechanisms must be immune to
|
||||
such changes.
|
||||
|
||||
- For security reasons, encrypted Travis variables are not available to PRs, so we can't rely on
|
||||
them to implement security.
|
||||
|
||||
|
||||
## Implemented approach
|
||||
|
||||
|
||||
### In a nutshell
|
||||
The implemented approach can be broken up to the following sub-tasks:
|
||||
|
||||
1. Verify which PR the uploaded artifacts correspond to.
|
||||
2. Determine the author of the PR.
|
||||
3. Check whether the PR author is a member of some whitelisted GitHub team.
|
||||
4. Deploy the artifacts to the corresponding PR's directory.
|
||||
5. Prevent overwriting previously deployed artifacts (which ensures that the guarantees established
|
||||
during deployment will remain valid until the artifacts are removed).
|
||||
6. Prevent uploaded files from accessing anything outside their directory.
|
||||
|
||||
|
||||
### Implementation details
|
||||
This section describes how each of the aforementioned sub-tasks is accomplished:
|
||||
|
||||
1. **Verify which PR the uploaded artifacts correspond to.**
|
||||
|
||||
We are taking advantage of Travis' [JWT addon](https://docs.travis-ci.com/user/jwt). By sharing
|
||||
a secret between Travis (which keeps it private but uses it to sign a JWT) and the server (which
|
||||
uses it to verify the authenticity of the JWT), we can accomplish the following:
|
||||
a. Verify that the upload request comes from Travis.
|
||||
b. Determine the PR that these artifacts correspond to (since Travis puts that information into
|
||||
the JWT, without the PR author being able to modify it).
|
||||
|
||||
_Note:_
|
||||
_There are currently certain limitation in the implementation of the JWT addon._
|
||||
_See the next section for more details._
|
||||
|
||||
2. **Determine the author of the PR.**
|
||||
|
||||
Once we have securely associated the uploaded artifaacts to a PR, we retrieve the PR's metadata -
|
||||
including the author's username - using the [GitHub API](https://developer.github.com/v3/).
|
||||
To avoid rate-limit restrictions, we use a Personal Access Token (issued by
|
||||
[@mary-poppins](https://github.com/mary-poppins)).
|
||||
|
||||
3. **Check whether the PR author is a member of some whitelisted GitHub team.**
|
||||
|
||||
Again using the GitHub API, we can verify the author's membership in one of the
|
||||
whitelisted/trusted GitHub teams. For this operation, we need a PErsonal Access Token with the
|
||||
`read:org` scope issued by a user that can "see" the specified GitHub organization.
|
||||
Here too, we use token by @mary-poppins.
|
||||
|
||||
4. **Deploy the artifacts to the corresponding PR's directory.**
|
||||
|
||||
With the preceeding steps, we have verified that the uploaded artifacts have been uploaded by
|
||||
Travis and correspond to a PR whose author is a member of a trusted team. Essentially, as long as
|
||||
sub-tasks 1, 2 and 3 can be securely accomplished, it is possible to "project" the trust we have
|
||||
in a team's members through the PR and Travis to the build artifacts.
|
||||
|
||||
5. **Prevent overwriting previously deployed artifacts**.
|
||||
|
||||
In order to enforce this restriction (and ensure that the deployed artifacts validity is
|
||||
preserved throughout their "lifetime"), the server that handles the upload (currently a Node.js
|
||||
Express server) rejects uploads that target an existing directory.
|
||||
_Note: A PR can contain multiple uploads; one for each SHA that was built on Travis._
|
||||
|
||||
6. **Prevent uploaded files from accessing anything outside their directory.**
|
||||
|
||||
Nginx (which is used to serve the uploaded artifacts) has been configured to not follow symlinks
|
||||
outside of the directory where the build artifacts are stored.
|
||||
|
||||
|
||||
## Assumptions / Things to keep in mind
|
||||
|
||||
- Each trusted PR author has full control over the content that is uploaded for their PRs. Part of
|
||||
the security model relies on the trustworthiness of these authors.
|
||||
|
||||
- If anyone gets access to the `PREVIEW_DEPLOYMENT_TOKEN` (a.k.a. `NGBUILDS_IO_KEY` on
|
||||
angular/angular) variable generated for each Travis job, they will be able to impersonate the
|
||||
corresponding PR's author on the preview server for as long as the token is valid (currently 90
|
||||
mins). Because of this, the value of the `PREVIEW_DEPLOYMENT_TOKEN` should not be made publicly
|
||||
accessible (e.g. by printing it on the Travis job log).
|
||||
|
||||
- Travis does only allow specific whitelisted property names to be used with the JWT addon. The only
|
||||
known such property at the time is `SAUCE_ACCESS_KEY` (used for integration with SauceLabs). In
|
||||
order to be able to actually use the JWT addon we had to name the encrypted variable
|
||||
`SAUCE_ACCESS_KEY` (which we later re-assign to `NGBUILDS_IO_KEY`).
|
@ -13,5 +13,8 @@
|
||||
|
||||
|
||||
## Mount disk on boot
|
||||
- ``echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
||||
/mnt/disks/aio-builds ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab``
|
||||
- Run:
|
||||
```
|
||||
echo UUID=`sudo blkid -s UUID -o value /dev/disk/by-id/google-aio-builds` \
|
||||
/mnt/disks/aio-builds ext4 discard,defaults,nofail 0 2 | sudo tee -a /etc/fstab
|
||||
```
|
32
aio/aio-builds-setup/docs/vm-setup--create-docker-image.md
Normal file
32
aio/aio-builds-setup/docs/vm-setup--create-docker-image.md
Normal file
@ -0,0 +1,32 @@
|
||||
# VM setup - Create docker image
|
||||
|
||||
|
||||
## Checkout repository
|
||||
- `git clone <repo-url>`
|
||||
|
||||
|
||||
## Build docker image
|
||||
- `<aio-builds-setup-dir>/scripts/build.sh [<name>[:<tag>] [--build-arg <NAME>=<value> ...]]`
|
||||
- You can overwrite the default environment variables inside the image, by passing new values using
|
||||
`--build-arg`.
|
||||
|
||||
**Note:** The build script has to execute docker commands with `sudo`.
|
||||
|
||||
|
||||
## Example
|
||||
The following commands would create a docker image from GitHub repo `foo/bar` to be deployed on the
|
||||
`foobar-builds.io` domain and accepting PR deployments from authors that are members of the
|
||||
`bar-core` and `bar-docs-authors` teams of organization `foo`:
|
||||
|
||||
- `git clone https://github.com/foo/bar.git foobar`
|
||||
- Run:
|
||||
```
|
||||
./foobar/aio-builds-setup/scripts/build.sh foobar-builds \
|
||||
--build-arg AIO_REPO_SLUG=foo/bar \
|
||||
--build-arg AIO_DOMAIN_NAME=foobar-builds.io \
|
||||
--build-arg AIO_GITHUB_ORGANIZATION=foo \
|
||||
--build-arg AIO_GITHUB_TEMA_SLUGS=bar-core,bar-docs-authors
|
||||
```
|
||||
|
||||
A full list of the available environment variables can be found
|
||||
[here](image-config--environment-variables.md).
|
@ -0,0 +1,74 @@
|
||||
# VM setup - Create host directories and files
|
||||
|
||||
|
||||
## Create directory with secrets
|
||||
For security reasons, sensitive info (such as tokens and passwords) are not hardcoded into the
|
||||
docker image, nor passed as environment variables at runtime. They are passed to the docker
|
||||
container from the host VM as files inside a directory. Each file's name is the name of the variable
|
||||
and the file content is the value. These are read from inside the running container when necessary.
|
||||
|
||||
More info on how to create `secrets` directory and files can be found
|
||||
[here](vm-setup--set-up-secrets.md).
|
||||
|
||||
|
||||
## Create directory for build artifacts
|
||||
The uploaded build artifacts should be kept on a directory outside the docker container, so it is
|
||||
easier to replace the container without losing the uploaded builds. For portability across VMs a
|
||||
persistent disk can be used (as described [here](vm-setup--attach-persistent-disk.md)).
|
||||
|
||||
**Note:** The directories created inside that directory will be owned by user `www-data`.
|
||||
|
||||
|
||||
## Create SSL certificates (Optional for dev)
|
||||
The host VM can attach a directory containing the SSL certificate and key to be used by the nginx
|
||||
server for serving the uploaded build artifacts. More info on how to attach the directory when
|
||||
starting the container can be found [here](vm-setup--start-docker-container.md).
|
||||
|
||||
In order for the container to be able to find the certificate and key, they should be named
|
||||
`<DOMAIN_NAME>.crt` and `<DOMAIN_NAME>.key` respectively. For example, for a domain name
|
||||
`ngbuild.io`, nginx will look for files `ngbuilds.io.crt` and `ngbuilds.io.key`. More info on how to
|
||||
specify the domain name see [here](vm-setup--create-docker-image.md).
|
||||
|
||||
If no directory is attached, nginx will use an internal self-signed certificate. This is convenient
|
||||
during development, but is not suitable for production.
|
||||
|
||||
**Note:**
|
||||
Since nginx needs to be able to serve requests for both the main domain as well as any subdomain
|
||||
(e.g. `ngbuilds.io/` and `foo-bar.ngbuilds.io/`), the provided certificate needs to be a wildcard
|
||||
certificate covering both the domain and subdomains.
|
||||
|
||||
|
||||
## Create directory for logs (Optional)
|
||||
Optionally, a logs directory can pe passed to the docker container for storing non-system-related
|
||||
logs. If not provided, the logs are kept locally on the container and will be lost whenever the
|
||||
container is replaced (e.g. when updating to use a newer version of the docker image).
|
||||
|
||||
The following files log files are kept in this directory:
|
||||
|
||||
- `clean-up.log`:
|
||||
Output of the `aio-clean-up` command, run as a cronjob for cleaning up the build artifacts of
|
||||
closed PRs.
|
||||
|
||||
- `init.log`:
|
||||
Output of the `aio-init` command, run (by default) when starting the container.
|
||||
|
||||
- `nginx/{access,error}.log`:
|
||||
The access and error logs produced by the nginx server while serving "production" files.
|
||||
|
||||
- `nginx-test/{access,error}.log`:
|
||||
The access and error logs produced by the nginx server while serving "test" files. This is only
|
||||
used when running tests locally from inside the container, e.g. with the `aio-verify-setup`
|
||||
command. (See [here](overview--scripts-and-commands.md) for more info.)
|
||||
|
||||
- `upload-server-{prod,test,verify-setup}-*.log`:
|
||||
The logs produced by the Node.js upload-server while serving either:
|
||||
- `-prod`: "Production" files (g.g during normal operation).
|
||||
- `-test`: "Test" files (e.g. when a test instance is started with the `aio-upload-server-test`
|
||||
command).
|
||||
- `-verify-setup`: "Test" files, but while running `aio-verify-setup`.
|
||||
|
||||
(See [here](overview--scripts-and-commands.md) for more info the commands mentioned above.)
|
||||
|
||||
- `verify-setup.log`:
|
||||
The output of the `aio-verify-setup` command (e.g. Jasmine output), except for upload-server
|
||||
output which is logged to `upload-server-verify-setup-*.log` (see above).
|
@ -0,0 +1,92 @@
|
||||
# VM setup - Start docker container
|
||||
|
||||
|
||||
## The `docker run` command
|
||||
Once everything has been setup and configured, a docker container can be started with the following
|
||||
command:
|
||||
|
||||
```
|
||||
sudo docker run \
|
||||
-d \
|
||||
--dns 127.0.0.1 \
|
||||
--name <instance-name> \
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
--restart unless-stopped \
|
||||
[-v <host-cert-dir>:/etc/ssl/localcerts:ro] \
|
||||
-v <host-secrets-dir>:/aio-secrets:ro \
|
||||
-v <host-builds-dir>:/var/www/aio-builds \
|
||||
[-v <host-logs-dir>:/var/log/aio] \
|
||||
<name>[:<tag>]
|
||||
```
|
||||
|
||||
Below is the same command with inline comments explaining each option. The aPI docs for `docker run`
|
||||
can be found [here](https://docs.docker.com/engine/reference/run/).
|
||||
|
||||
```
|
||||
sudo docker run \
|
||||
|
||||
# Start as a daemon.
|
||||
-d \
|
||||
|
||||
# Use the local DNS server.
|
||||
# (This is necessary for mapping internal URLs, e.g. for the Node.js upload-server.)
|
||||
--dns 127.0.0.1 \
|
||||
|
||||
# USe `<instance-name>` as an alias for the container.
|
||||
# Useful for running `docker` commands, e.g.: `docker stop <instance-name>`
|
||||
--name <instance-name> \
|
||||
|
||||
# Map ports of the hosr VM (left) to ports of the docker container (right)
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
|
||||
# Automatically restart the container (unless it was explicitly stopped by the user).
|
||||
# (This ensures that the container will be automatically started on boot.)
|
||||
--restart unless-stopped \
|
||||
|
||||
# The directory the contains the SSL certificates.
|
||||
# (See [here](vm-setup--create-host-dirs-and-files.md) for more info.)
|
||||
# If not provided, the container will use self-signed certificates.
|
||||
[-v <host-cert-dir>:/etc/ssl/localcerts:ro] \
|
||||
|
||||
# The directory the contains the secrets (e.g. GitHub token, JWT secret, etc).
|
||||
# (See [here](vm-setup--set-up-secrets.md) for more info.)
|
||||
-v <host-secrets-dir>:/aio-secrets:ro \
|
||||
|
||||
# The uploaded build artifacts will stored to and served from this directory.
|
||||
# (If you are using a persistent disk - as described [here](vm-setup--attach-persistent-disk.md) -
|
||||
# this will be a directory inside the disk.)
|
||||
-v <host-builds-dir>:/var/www/aio-builds \
|
||||
|
||||
# The directory where the logs are being kept.
|
||||
# (See [here](vm-setup--create-host-dirs-and-files.md) for more info.)
|
||||
# If not provided, the logs will be kept inside the container, which means they will be lost
|
||||
# whenever a new container is created.
|
||||
[-v <host-logs-dir>:/var/log/aio] \
|
||||
|
||||
# The name of the docker image to use (and an optional tag; defaults to `latest`).
|
||||
# (See [here](vm-setup--create-docker-image.md) for instructions on how to create the iamge.)
|
||||
<name>[:<tag>]
|
||||
```
|
||||
|
||||
|
||||
## Example
|
||||
The following command would start a docker container based on the previously created `foobar-builds`
|
||||
docker image, alias it as 'foobar-builds-1' and map predefined directories on the host VM to be used
|
||||
by the container for accesing secrets and SSL certificates and keeping the build artifacts and logs.
|
||||
|
||||
```
|
||||
sudo docker run \
|
||||
-d \
|
||||
--dns 127.0.0.1 \
|
||||
--name foobar-builds-1 \
|
||||
-p 80:80 \
|
||||
-p 443:443 \
|
||||
--restart unless-stopped \
|
||||
-v /etc/ssl/localcerts:/etc/ssl/localcerts:ro \
|
||||
-v /foobar-secrets:/aio-secrets:ro \
|
||||
-v /mnt/disks/foobar-builds:/var/www/aio-builds \
|
||||
-v /foobar-logs:/var/log/aio \
|
||||
foobar-builds
|
||||
```
|
@ -6,7 +6,7 @@ source "`dirname $0`/env.sh"
|
||||
|
||||
# Preverify PR
|
||||
AIO_GITHUB_ORGANIZATION="angular" \
|
||||
AIO_GITHUB_TEAM_SLUGS="angular-core" \
|
||||
AIO_GITHUB_TEAM_SLUGS="angular-core,aio-contributors" \
|
||||
AIO_GITHUB_TOKEN=$(echo ${GITHUB_TEAM_MEMBERSHIP_CHECK_KEY} | rev) \
|
||||
AIO_REPO_SLUG=$TRAVIS_REPO_SLUG \
|
||||
AIO_PREVERIFY_PR=$TRAVIS_PULL_REQUEST \
|
||||
|
@ -1,18 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Bootstrapping
|
||||
@cheatsheetIndex 0
|
||||
@description
|
||||
{@target ts}`import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';`{@endtarget}
|
||||
{@target js}Available from the `ng.platformBrowserDynamic` namespace{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`platformBrowserDynamic().bootstrapModule(AppModule);`|`platformBrowserDynamic().bootstrapModule`
|
||||
syntax(js):
|
||||
`document.addEventListener('DOMContentLoaded', function() {
|
||||
ng.platformBrowserDynamic
|
||||
.platformBrowserDynamic()
|
||||
.bootstrapModule(app.AppModule);
|
||||
});`|`platformBrowserDynamic().bootstrapModule`
|
||||
description:
|
||||
Bootstraps the app, using the root component from the specified `NgModule`. {@target js}Must be wrapped in the event listener to fire when the page loads.{@endtarget}
|
@ -1,34 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Built-in directives
|
||||
@cheatsheetIndex 3
|
||||
@description
|
||||
{@target ts}`import { CommonModule } from '@angular/common';`{@endtarget}
|
||||
{@target js}Available using the `ng.common.CommonModule` module{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<section *ngIf="showSection">`|`*ngIf`
|
||||
description:
|
||||
Removes or recreates a portion of the DOM tree based on the `showSection` expression.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<li *ngFor="let item of list">`|`*ngFor`
|
||||
description:
|
||||
Turns the li element and its contents into a template, and uses that to instantiate a view for each item in list.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [ngSwitch]="conditionExpression">
|
||||
<ng-template [ngSwitchCase]="case1Exp">...</ng-template>
|
||||
<ng-template ngSwitchCase="case2LiteralString">...</ng-template>
|
||||
<ng-template ngSwitchDefault>...</ng-template>
|
||||
</div>`|`[ngSwitch]`|`[ngSwitchCase]`|`ngSwitchCase`|`ngSwitchDefault`
|
||||
description:
|
||||
Conditionally swaps the contents of the div by selecting one of the embedded templates based on the current value of `conditionExpression`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [ngClass]="{active: isActive, disabled: isDisabled}">`|`[ngClass]`
|
||||
description:
|
||||
Binds the presence of CSS classes on the element to the truthiness of the associated map values. The right-hand expression should return {class-name: true/false} map.
|
@ -1,49 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Class decorators
|
||||
@cheatsheetIndex 5
|
||||
@description
|
||||
{@target ts}`import { Directive, ... } from '@angular/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Component({...})
|
||||
class MyComponent() {}`|`@Component({...})`
|
||||
syntax(js):
|
||||
`var MyComponent = ng.core.Component({...}).Class({...})`|`ng.core.Component({...})`
|
||||
description:
|
||||
Declares that a class is a component and provides metadata about the component.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Directive({...})
|
||||
class MyDirective() {}`|`@Directive({...})`
|
||||
syntax(js):
|
||||
`var MyDirective = ng.core.Directive({...}).Class({...})`|`ng.core.Directive({...})`
|
||||
description:
|
||||
Declares that a class is a directive and provides metadata about the directive.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Pipe({...})
|
||||
class MyPipe() {}`|`@Pipe({...})`
|
||||
syntax(js):
|
||||
`var MyPipe = ng.core.Pipe({...}).Class({...})`|`ng.core.Pipe({...})`
|
||||
description:
|
||||
Declares that a class is a pipe and provides metadata about the pipe.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Injectable()
|
||||
class MyService() {}`|`@Injectable()`
|
||||
syntax(js):
|
||||
`var OtherService = ng.core.Class(
|
||||
{constructor: function() { }});
|
||||
var MyService = ng.core.Class(
|
||||
{constructor: [OtherService, function(otherService) { }]});`|`var MyService = ng.core.Class({constructor: [OtherService, function(otherService) { }]});`
|
||||
description:
|
||||
{@target ts}Declares that a class has dependencies that should be injected into the constructor when the dependency injector is creating an instance of this class.
|
||||
{@endtarget}
|
||||
{@target js}
|
||||
Declares a service to inject into a class by providing an array with the services, with the final item being the function to receive the injected services.
|
||||
{@endtarget}
|
@ -1,38 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Component configuration
|
||||
@cheatsheetIndex 7
|
||||
@description
|
||||
{@target js}`ng.core.Component` extends `ng.core.Directive`,
|
||||
so the `ng.core.Directive` configuration applies to components as well{@endtarget}
|
||||
{@target ts}`@Component` extends `@Directive`,
|
||||
so the `@Directive` configuration applies to components as well{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`moduleId: module.id`|`moduleId:`
|
||||
description:
|
||||
If set, the `templateUrl` and `styleUrl` are resolved relative to the component.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`viewProviders: [MyService, { provide: ... }]`|`viewProviders:`
|
||||
syntax(js):
|
||||
`viewProviders: [MyService, { provide: ... }]`|`viewProviders:`
|
||||
description:
|
||||
List of dependency injection providers scoped to this component's view.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`template: 'Hello {{name}}'
|
||||
templateUrl: 'my-component.html'`|`template:`|`templateUrl:`
|
||||
description:
|
||||
Inline template or external template URL of the component's view.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`styles: ['.primary {color: red}']
|
||||
styleUrls: ['my-component.css']`|`styles:`|`styleUrls:`
|
||||
description:
|
||||
List of inline CSS styles or external stylesheet URLs for styling the component’s view.
|
@ -1,30 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Dependency injection configuration
|
||||
@cheatsheetIndex 10
|
||||
@description
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`{ provide: MyService, useClass: MyMockService }`|`provide`|`useClass`
|
||||
syntax(js):
|
||||
`{ provide: MyService, useClass: MyMockService }`|`provide`|`useClass`
|
||||
description:
|
||||
Sets or overrides the provider for `MyService` to the `MyMockService` class.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`{ provide: MyService, useFactory: myFactory }`|`provide`|`useFactory`
|
||||
syntax(js):
|
||||
`{ provide: MyService, useFactory: myFactory }`|`provide`|`useFactory`
|
||||
description:
|
||||
Sets or overrides the provider for `MyService` to the `myFactory` factory function.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`{ provide: MyValue, useValue: 41 }`|`provide`|`useValue`
|
||||
syntax(js):
|
||||
`{ provide: MyValue, useValue: 41 }`|`provide`|`useValue`
|
||||
description:
|
||||
Sets or overrides the provider for `MyValue` to the value `41`.
|
@ -1,86 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Class field decorators for directives and components
|
||||
@cheatsheetIndex 8
|
||||
@description
|
||||
{@target ts}`import { Input, ... } from '@angular/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Input() myProperty;`|`@Input()`
|
||||
syntax(js):
|
||||
`ng.core.Input(myProperty, myComponent);`|`ng.core.Input(`|`);`
|
||||
description:
|
||||
Declares an input property that you can update via property binding (example:
|
||||
`<my-cmp [myProperty]="someExpression">`).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@Output() myEvent = new EventEmitter();`|`@Output()`
|
||||
syntax(js):
|
||||
`myEvent = new ng.core.EventEmitter();
|
||||
ng.core.Output(myEvent, myComponent);`|`ng.core.Output(`|`);`
|
||||
description:
|
||||
Declares an output property that fires events that you can subscribe to with an event binding (example: `<my-cmp (myEvent)="doSomething()">`).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@HostBinding('class.valid') isValid;`|`@HostBinding('class.valid')`
|
||||
syntax(js):
|
||||
`ng.core.HostBinding('class.valid',
|
||||
'isValid', myComponent);`|`ng.core.HostBinding('class.valid', 'isValid'`|`);`
|
||||
description:
|
||||
Binds a host element property (here, the CSS class `valid`) to a directive/component property (`isValid`).
|
||||
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@HostListener('click', ['$event']) onClick(e) {...}`|`@HostListener('click', ['$event'])`
|
||||
syntax(js):
|
||||
`ng.core.HostListener('click',
|
||||
['$event'], onClick(e) {...}, myComponent);`|`ng.core.HostListener('click', ['$event'], onClick(e)`|`);`
|
||||
description:
|
||||
Subscribes to a host element event (`click`) with a directive/component method (`onClick`), optionally passing an argument (`$event`).
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@ContentChild(myPredicate) myChildComponent;`|`@ContentChild(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ContentChild(myPredicate,
|
||||
'myChildComponent', myComponent);`|`ng.core.ContentChild(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the first result of the component content query (`myPredicate`) to a property (`myChildComponent`) of the class.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@ContentChildren(myPredicate) myChildComponents;`|`@ContentChildren(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ContentChildren(myPredicate,
|
||||
'myChildComponents', myComponent);`|`ng.core.ContentChildren(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the results of the component content query (`myPredicate`) to a property (`myChildComponents`) of the class.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@ViewChild(myPredicate) myChildComponent;`|`@ViewChild(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ViewChild(myPredicate,
|
||||
'myChildComponent', myComponent);`|`ng.core.ViewChild(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the first result of the component view query (`myPredicate`) to a property (`myChildComponent`) of the class. Not available for directives.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@ViewChildren(myPredicate) myChildComponents;`|`@ViewChildren(myPredicate)`
|
||||
syntax(js):
|
||||
`ng.core.ViewChildren(myPredicate,
|
||||
'myChildComponents', myComponent);`|`ng.core.ViewChildren(myPredicate,`|`);`
|
||||
description:
|
||||
Binds the results of the component view query (`myPredicate`) to a property (`myChildComponents`) of the class. Not available for directives.
|
@ -1,23 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Directive configuration
|
||||
@cheatsheetIndex 6
|
||||
@description
|
||||
{@target ts}`@Directive({ property1: value1, ... })`{@endtarget}
|
||||
{@target js}`ng.core.Directive({ property1: value1, ... }).Class({...})`{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`selector: '.cool-button:not(a)'`|`selector:`
|
||||
description:
|
||||
Specifies a CSS selector that identifies this directive within a template. Supported selectors include `element`,
|
||||
`[attribute]`, `.class`, and `:not()`.
|
||||
|
||||
Does not support parent-child relationship selectors.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`providers: [MyService, { provide: ... }]`|`providers:`
|
||||
syntax(js):
|
||||
`providers: [MyService, { provide: ... }]`|`providers:`
|
||||
description:
|
||||
List of dependency injection providers for this directive and its children.
|
@ -1,12 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Forms
|
||||
@cheatsheetIndex 4
|
||||
@description
|
||||
{@target ts}`import { FormsModule } from '@angular/forms';`{@endtarget}
|
||||
{@target js}Available using the `ng.forms.FormsModule` module{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<input [(ngModel)]="userName">`|`[(ngModel)]`
|
||||
description:
|
||||
Provides two-way data-binding, parsing, and validation for form controls.
|
@ -1,86 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Directive and component change detection and lifecycle hooks
|
||||
@cheatsheetIndex 9
|
||||
@description
|
||||
{@target ts}(implemented as class methods){@endtarget}
|
||||
{@target js}(implemented as component properties){@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`constructor(myService: MyService, ...) { ... }`|`constructor(myService: MyService, ...)`
|
||||
syntax(js):
|
||||
`constructor: function(MyService, ...) { ... }`|`constructor: function(MyService, ...)`
|
||||
description:
|
||||
Called before any other lifecycle hook. Use it to inject dependencies, but avoid any serious work here.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngOnChanges(changeRecord) { ... }`|`ngOnChanges(changeRecord)`
|
||||
syntax(js):
|
||||
`ngOnChanges: function(changeRecord) { ... }`|`ngOnChanges: function(changeRecord)`
|
||||
description:
|
||||
Called after every change to input properties and before processing content or child views.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngOnInit() { ... }`|`ngOnInit()`
|
||||
syntax(js):
|
||||
`ngOnInit: function() { ... }`|`ngOnInit: function()`
|
||||
description:
|
||||
Called after the constructor, initializing input properties, and the first call to `ngOnChanges`.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngDoCheck() { ... }`|`ngDoCheck()`
|
||||
syntax(js):
|
||||
`ngDoCheck: function() { ... }`|`ngDoCheck: function()`
|
||||
description:
|
||||
Called every time that the input properties of a component or a directive are checked. Use it to extend change detection by performing a custom check.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngAfterContentInit() { ... }`|`ngAfterContentInit()`
|
||||
syntax(js):
|
||||
`ngAfterContentInit: function() { ... }`|`ngAfterContentInit: function()`
|
||||
description:
|
||||
Called after `ngOnInit` when the component's or directive's content has been initialized.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngAfterContentChecked() { ... }`|`ngAfterContentChecked()`
|
||||
syntax(js):
|
||||
`ngAfterContentChecked: function() { ... }`|`ngAfterContentChecked: function()`
|
||||
description:
|
||||
Called after every check of the component's or directive's content.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngAfterViewInit() { ... }`|`ngAfterViewInit()`
|
||||
syntax(js):
|
||||
`ngAfterViewInit: function() { ... }`|`ngAfterViewInit: function()`
|
||||
description:
|
||||
Called after `ngAfterContentInit` when the component's view has been initialized. Applies to components only.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngAfterViewChecked() { ... }`|`ngAfterViewChecked()`
|
||||
syntax(js):
|
||||
`ngAfterViewChecked: function() { ... }`|`ngAfterViewChecked: function()`
|
||||
description:
|
||||
Called after every check of the component's view. Applies to components only.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`ngOnDestroy() { ... }`|`ngOnDestroy()`
|
||||
syntax(js):
|
||||
`ngOnDestroy: function() { ... }`|`ngOnDestroy: function()`
|
||||
description:
|
||||
Called once, before the instance is destroyed.
|
@ -1,58 +0,0 @@
|
||||
@cheatsheetSection
|
||||
NgModules
|
||||
@cheatsheetIndex 1
|
||||
@description
|
||||
{@target ts}`import { NgModule } from '@angular/core';`{@endtarget}
|
||||
{@target js}Available from the `ng.core` namespace{@endtarget}
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`@NgModule({ declarations: ..., imports: ...,
|
||||
exports: ..., providers: ..., bootstrap: ...})
|
||||
class MyModule {}`|`NgModule`
|
||||
description:
|
||||
Defines a module that contains components, directives, pipes, and providers.
|
||||
|
||||
syntax(js):
|
||||
`ng.core.NgModule({declarations: ..., imports: ...,
|
||||
exports: ..., providers: ..., bootstrap: ...}).
|
||||
Class({ constructor: function() {}})`
|
||||
description:
|
||||
Defines a module that contains components, directives, pipes, and providers.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`declarations: [MyRedComponent, MyBlueComponent, MyDatePipe]`|`declarations:`
|
||||
description:
|
||||
List of components, directives, and pipes that belong to this module.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`imports: [BrowserModule, SomeOtherModule]`|`imports:`
|
||||
description:
|
||||
List of modules to import into this module. Everything from the imported modules
|
||||
is available to `declarations` of this module.
|
||||
|
||||
syntax(js):
|
||||
`imports: [ng.platformBrowser.BrowserModule, SomeOtherModule]`|`imports:`
|
||||
description:
|
||||
List of modules to import into this module. Everything from the imported modules
|
||||
is available to `declarations` of this module.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`exports: [MyRedComponent, MyDatePipe]`|`exports:`
|
||||
description:
|
||||
List of components, directives, and pipes visible to modules that import this module.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`providers: [MyService, { provide: ... }]`|`providers:`
|
||||
description:
|
||||
List of dependency injection providers visible both to the contents of this module and to importers of this module.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`bootstrap: [MyAppComponent]`|`bootstrap:`
|
||||
description:
|
||||
List of components to bootstrap when this module is bootstrapped.
|
@ -1,170 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Routing and navigation
|
||||
@cheatsheetIndex 11
|
||||
@description
|
||||
{@target ts}`import { Routes, RouterModule, ... } from '@angular/router';`{@endtarget}
|
||||
{@target js}Available from the `ng.router` namespace{@endtarget}
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`const routes: Routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{ path: 'path/:routeParam', component: MyComponent },
|
||||
{ path: 'staticPath', component: ... },
|
||||
{ path: '**', component: ... },
|
||||
{ path: 'oldPath', redirectTo: '/staticPath' },
|
||||
{ path: ..., component: ..., data: { message: 'Custom' } }
|
||||
]);
|
||||
|
||||
const routing = RouterModule.forRoot(routes);`|`Routes`
|
||||
syntax(js):
|
||||
`var routes = [
|
||||
{ path: '', component: HomeComponent },
|
||||
{ path: ':routeParam', component: MyComponent },
|
||||
{ path: 'staticPath', component: ... },
|
||||
{ path: '**', component: ... },
|
||||
{ path: 'oldPath', redirectTo: '/staticPath' },
|
||||
{ path: ..., component: ..., data: { message: 'Custom' } }
|
||||
]);
|
||||
|
||||
var routing = ng.router.RouterModule.forRoot(routes);`|`ng.router.Routes`
|
||||
description:
|
||||
Configures routes for the application. Supports static, parameterized, redirect, and wildcard routes. Also supports custom route data and resolve.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`
|
||||
<router-outlet></router-outlet>
|
||||
<router-outlet name="aux"></router-outlet>
|
||||
`|`router-outlet`
|
||||
description:
|
||||
Marks the location to load the component of the active route.
|
||||
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`
|
||||
<a routerLink="/path">
|
||||
<a [routerLink]="[ '/path', routeParam ]">
|
||||
<a [routerLink]="[ '/path', { matrixParam: 'value' } ]">
|
||||
<a [routerLink]="[ '/path' ]" [queryParams]="{ page: 1 }">
|
||||
<a [routerLink]="[ '/path' ]" fragment="anchor">
|
||||
`|`[routerLink]`
|
||||
description:
|
||||
Creates a link to a different view based on a route instruction consisting of a route path, required and optional parameters, query parameters, and a fragment. To navigate to a root route, use the `/` prefix; for a child route, use the `./`prefix; for a sibling or parent, use the `../` prefix.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<a [routerLink]="[ '/path' ]" routerLinkActive="active">`
|
||||
description:
|
||||
The provided classes are added to the element when the `routerLink` becomes the current active route.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`class CanActivateGuard implements CanActivate {
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||
}
|
||||
|
||||
{ path: ..., canActivate: [CanActivateGuard] }`|`CanActivate`
|
||||
syntax(js):
|
||||
`var CanActivateGuard = ng.core.Class({
|
||||
canActivate: function(route, state) {
|
||||
// return Observable/Promise boolean or boolean
|
||||
}
|
||||
});
|
||||
|
||||
{ path: ..., canActivate: [CanActivateGuard] }`|`CanActivate`
|
||||
description:
|
||||
An interface for defining a class that the router should call first to determine if it should activate this component. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`class CanDeactivateGuard implements CanDeactivate<T> {
|
||||
canDeactivate(
|
||||
component: T,
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||
}
|
||||
|
||||
{ path: ..., canDeactivate: [CanDeactivateGuard] }`|`CanDeactivate`
|
||||
syntax(js):
|
||||
`var CanDeactivateGuard = ng.core.Class({
|
||||
canDeactivate: function(component, route, state) {
|
||||
// return Observable/Promise boolean or boolean
|
||||
}
|
||||
});
|
||||
|
||||
{ path: ..., canDeactivate: [CanDeactivateGuard] }`|`CanDeactivate`
|
||||
description:
|
||||
An interface for defining a class that the router should call first to determine if it should deactivate this component after a navigation. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`class CanActivateChildGuard implements CanActivateChild {
|
||||
canActivateChild(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||
}
|
||||
|
||||
{ path: ..., canActivateChild: [CanActivateGuard],
|
||||
children: ... }`|`CanActivateChild`
|
||||
syntax(js):
|
||||
`var CanActivateChildGuard = ng.core.Class({
|
||||
canActivateChild: function(route, state) {
|
||||
// return Observable/Promise boolean or boolean
|
||||
}
|
||||
});
|
||||
|
||||
{ path: ..., canActivateChild: [CanActivateChildGuard],
|
||||
children: ... }`|`CanActivateChild`
|
||||
description:
|
||||
An interface for defining a class that the router should call first to determine if it should activate the child route. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`class ResolveGuard implements Resolve<T> {
|
||||
resolve(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot
|
||||
): Observable<any>|Promise<any>|any { ... }
|
||||
}
|
||||
|
||||
{ path: ..., resolve: [ResolveGuard] }`|`Resolve`
|
||||
syntax(js):
|
||||
`var ResolveGuard = ng.core.Class({
|
||||
resolve: function(route, state) {
|
||||
// return Observable/Promise value or value
|
||||
}
|
||||
});
|
||||
|
||||
{ path: ..., resolve: [ResolveGuard] }`|`Resolve`
|
||||
description:
|
||||
An interface for defining a class that the router should call first to resolve route data before rendering the route. Should return a value or an Observable/Promise that resolves to a value.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax(ts):
|
||||
`class CanLoadGuard implements CanLoad {
|
||||
canLoad(
|
||||
route: Route
|
||||
): Observable<boolean>|Promise<boolean>|boolean { ... }
|
||||
}
|
||||
|
||||
{ path: ..., canLoad: [CanLoadGuard], loadChildren: ... }`|`CanLoad`
|
||||
syntax(js):
|
||||
`var CanLoadGuard = ng.core.Class({
|
||||
canLoad: function(route) {
|
||||
// return Observable/Promise boolean or boolean
|
||||
}
|
||||
});
|
||||
|
||||
{ path: ..., canLoad: [CanLoadGuard], loadChildren: ... }`|`CanLoad`
|
||||
description:
|
||||
An interface for defining a class that the router should call first to check if the lazy loaded module should be loaded. Should return a boolean or an Observable/Promise that resolves to a boolean.
|
||||
|
@ -1,94 +0,0 @@
|
||||
@cheatsheetSection
|
||||
Template syntax
|
||||
@cheatsheetIndex 2
|
||||
@description
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<input [value]="firstName">`|`[value]`
|
||||
description:
|
||||
Binds property `value` to the result of expression `firstName`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [attr.role]="myAriaRole">`|`[attr.role]`
|
||||
description:
|
||||
Binds attribute `role` to the result of expression `myAriaRole`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [class.extra-sparkle]="isDelightful">`|`[class.extra-sparkle]`
|
||||
description:
|
||||
Binds the presence of the CSS class `extra-sparkle` on the element to the truthiness of the expression `isDelightful`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div [style.width.px]="mySize">`|`[style.width.px]`
|
||||
description:
|
||||
Binds style property `width` to the result of expression `mySize` in pixels. Units are optional.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<button (click)="readRainbow($event)">`|`(click)`
|
||||
description:
|
||||
Calls method `readRainbow` when a click event is triggered on this button element (or its children) and passes in the event object.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<div title="Hello {{ponyName}}">`|`{{ponyName}}`
|
||||
description:
|
||||
Binds a property to an interpolated string, for example, "Hello Seabiscuit". Equivalent to:
|
||||
`<div [title]="'Hello ' + ponyName">`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Hello {{ponyName}}</p>`|`{{ponyName}}`
|
||||
description:
|
||||
Binds text content to an interpolated string, for example, "Hello Seabiscuit".
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<my-cmp [(title)]="name">`|`[(title)]`
|
||||
description:
|
||||
Sets up two-way data binding. Equivalent to: `<my-cmp [title]="name" (titleChange)="name=$event">`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<video #movieplayer ...>
|
||||
<button (click)="movieplayer.play()">
|
||||
</video>`|`#movieplayer`|`(click)`
|
||||
description:
|
||||
Creates a local variable `movieplayer` that provides access to the `video` element instance in data-binding and event-binding expressions in the current template.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p *myUnless="myExpression">...</p>`|`*myUnless`
|
||||
description:
|
||||
The `*` symbol turns the current element into an embedded template. Equivalent to:
|
||||
`<ng-template [myUnless]="myExpression"><p>...</p></ng-template>`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Card No.: {{cardNumber | myCardNumberFormatter}}</p>`|`{{cardNumber | myCardNumberFormatter}}`
|
||||
description:
|
||||
Transforms the current value of expression `cardNumber` via the pipe called `myCardNumberFormatter`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Employer: {{employer?.companyName}}</p>`|`{{employer?.companyName}}`
|
||||
description:
|
||||
The safe navigation operator (`?`) means that the `employer` field is optional and if `undefined`, the rest of the expression should be ignored.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<svg:rect x="0" y="0" width="100" height="100"/>`|`svg:`
|
||||
description:
|
||||
An SVG snippet template needs an `svg:` prefix on its root element to disambiguate the SVG element from an HTML component.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<svg>
|
||||
<rect x="0" y="0" width="100" height="100"/>
|
||||
</svg>`|`svg`
|
||||
description:
|
||||
An `<svg>` root element is detected as an SVG element automatically, without the prefix.
|
@ -17,15 +17,15 @@ by mapping AngularJS syntax to the equivalent Angular syntax.
|
||||
|
||||
## Contents
|
||||
|
||||
* [Template basics](#template-basics)—binding and local variables.
|
||||
* [Template basics](guide/ajs-quick-reference#template-basics)—binding and local variables.
|
||||
|
||||
* [Template directives](#template-directives)—built-in directives `ngIf` and `ngClass`.
|
||||
* [Template directives](guide/ajs-quick-reference#template-directives)—built-in directives `ngIf` and `ngClass`.
|
||||
|
||||
* [Filters/pipes](#filters-pipes)—built-in *filters*, known as *pipes* in Angular.
|
||||
* [Filters/pipes](guide/ajs-quick-reference#filters-pipes)—built-in *filters*, known as *pipes* in Angular.
|
||||
|
||||
* [Modules/controllers/components](#controllers-components)—*modules* in Angular are slightly different from *modules* in AngularJS, and *controllers* are *components* in Angular.
|
||||
* [Modules/controllers/components](guide/ajs-quick-reference#controllers-components)—*modules* in Angular are slightly different from *modules* in AngularJS, and *controllers* are *components* in Angular.
|
||||
|
||||
* [Style sheets](#style-sheets)—more options for CSS than in AngularJS.
|
||||
* [Style sheets](guide/ajs-quick-reference#style-sheets)—more options for CSS than in AngularJS.
|
||||
|
||||
## Template basics
|
||||
Templates are the user-facing part of an Angular application and are written in HTML.
|
||||
@ -86,8 +86,8 @@ The following table lists some of the key AngularJS template features with their
|
||||
The context of the binding is implied and is always the
|
||||
associated component, so it needs no reference variable.
|
||||
|
||||
For more information, see the [Interpolation](../guide/template-syntax.html#interpolation)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information, see the [Interpolation](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -117,7 +117,7 @@ The following table lists some of the key AngularJS template features with their
|
||||
Many (but not all) of the built-in filters from AngularJS are
|
||||
built-in pipes in Angular.
|
||||
|
||||
For more information, see [Filters/pipes](#filters-pipes) below.
|
||||
For more information, see [Filters/pipes](guide/ajs-quick-reference#filters-pipes) below.
|
||||
</td>
|
||||
|
||||
|
||||
@ -145,8 +145,8 @@ The following table lists some of the key AngularJS template features with their
|
||||
|
||||
Angular has true template input variables that are explicitly defined using the `let` keyword.
|
||||
|
||||
For more information, see the [ngFor micro-syntax](../guide/template-syntax.html#microsyntax)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information, see the [ngFor micro-syntax](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -155,7 +155,7 @@ The following table lists some of the key AngularJS template features with their
|
||||
|
||||
</table>
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
## Template directives
|
||||
AngularJS provides more than seventy built-in directives for templates.
|
||||
@ -219,7 +219,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
in `main.ts`
|
||||
and the application's root component (`AppComponent`) in `app.module.ts`.
|
||||
|
||||
For more information see the [Setup](../guide/setup.html) page.
|
||||
For more information see the [Setup](guide/setup) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -262,8 +262,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
Angular also has **class binding**, which is a good way to add or remove a single class,
|
||||
as shown in the third example.
|
||||
|
||||
For more information see the [Attribute, class, and style bindings](../guide/template-syntax.html#other-bindings)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information see the [Attribute, class, and style bindings](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
|
||||
</td>
|
||||
|
||||
@ -309,8 +309,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
For a list of DOM events, see: https://developer.mozilla.org/en-US/docs/Web/Events.
|
||||
|
||||
For more information, see the [Event binding](../guide/template-syntax.html#event-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information, see the [Event binding](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
|
||||
</td>
|
||||
|
||||
@ -340,7 +340,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
In Angular, the template no longer specifies its associated controller.
|
||||
Rather, the component specifies its associated template as part of the component class decorator.
|
||||
|
||||
For more information, see [Architecture Overview](../guide/architecture.html#component).
|
||||
For more information, see [Architecture Overview](guide/architecture).
|
||||
|
||||
</td>
|
||||
|
||||
@ -353,14 +353,14 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
<td>
|
||||
### ng-hide
|
||||
In AngularJS, the `ng-hide` directive shows or hides the associated HTML element based on
|
||||
an expression. For more information, see [ng-show](#ng-show).
|
||||
an expression. For more information, see [ng-show](guide/ajs-quick-reference#ng-show).
|
||||
</td>
|
||||
|
||||
|
||||
<td>
|
||||
### Bind to the `hidden` property
|
||||
In Angular, you use property binding; there is no built-in *hide* directive.
|
||||
For more information, see [ng-show](#ng-show).
|
||||
For more information, see [ng-show](guide/ajs-quick-reference#ng-show).
|
||||
</td>
|
||||
|
||||
|
||||
@ -396,15 +396,15 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
Angular uses property binding; there is no built-in *href* directive.
|
||||
Place the element's `href` property in square brackets and set it to a quoted template expression.
|
||||
|
||||
For more information see the [Property binding](../guide/template-syntax.html#property-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information see the [Property binding](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
|
||||
In Angular, `href` is no longer used for routing. Routing uses `routerLink`, as shown in the following example.
|
||||
|
||||
{@example 'cb-ajs-quick-reference/ts/src/app/app.component.html' region='router-link'}
|
||||
|
||||
For more information on routing, see the [RouterLink binding](../guide/router.html#router-link)
|
||||
section of the [Routing & Navigation](../guide/router.html) page.
|
||||
For more information on routing, see the [RouterLink binding](guide/router)
|
||||
section of the [Routing & Navigation](guide/router) page.
|
||||
|
||||
</td>
|
||||
|
||||
@ -438,7 +438,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
In this example, the `<table>` element is removed from the DOM unless the `movies` array has a length.
|
||||
|
||||
The (*) before `ngIf` is required in this example.
|
||||
For more information, see [Structural Directives](../guide/structural-directives.html).
|
||||
For more information, see [Structural Directives](guide/structural-directives).
|
||||
</td>
|
||||
|
||||
|
||||
@ -468,7 +468,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
For more information on two-way binding with `ngModel`, see the [NgModel—Two-way binding to
|
||||
form elements with `[(ngModel)]`](../guide/template-syntax.html#ngModel)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -505,7 +505,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
the `let` keyword identifies `movie` as an input variable;
|
||||
the list preposition is `of`, not `in`.
|
||||
|
||||
For more information, see [Structural Directives](../guide/structural-directives.html).
|
||||
For more information, see [Structural Directives](guide/structural-directives).
|
||||
</td>
|
||||
|
||||
|
||||
@ -542,8 +542,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
In this example, the `<div>` element is hidden if the `favoriteHero` variable is not truthy.
|
||||
|
||||
For more information on property binding, see the [Property binding](../guide/template-syntax.html#property-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information on property binding, see the [Property binding](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -572,8 +572,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
Angular uses property binding; there is no built-in *src* directive.
|
||||
Place the `src` property in square brackets and set it to a quoted template expression.
|
||||
|
||||
For more information on property binding, see the [Property binding](../guide/template-syntax.html#property-binding)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information on property binding, see the [Property binding](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -608,11 +608,11 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
Angular also has **style binding**, which is good way to set a single style. This is shown in the second example.
|
||||
|
||||
For more information on style binding, see the [Style binding](../guide/template-syntax.html#style-binding) section of the
|
||||
[Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information on style binding, see the [Style binding](guide/template-syntax) section of the
|
||||
[Template Syntax](guide/template-syntax) page.
|
||||
|
||||
For more information on the `ngStyle` directive, see [NgStyle](../guide/template-syntax.html#ngStyle)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information on the `ngStyle` directive, see [NgStyle](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -664,8 +664,8 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
The (*) before `ngSwitchCase` and `ngSwitchDefault` is required in this example.
|
||||
|
||||
For more information, see [The NgSwitch directives](../guide/template-syntax.html#ngSwitch)
|
||||
section of the [Template Syntax](../guide/template-syntax.html) page.
|
||||
For more information, see [The NgSwitch directives](guide/template-syntax)
|
||||
section of the [Template Syntax](guide/template-syntax) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -674,7 +674,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
</table>
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
{@a filters-pipes}
|
||||
@ -682,7 +682,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
## Filters/pipes
|
||||
Angular **pipes** provide formatting and transformation for data in the template, similar to AngularJS **filters**.
|
||||
Many of the built-in filters in AngularJS have corresponding pipes in Angular.
|
||||
For more information on pipes, see [Pipes](../guide/pipes.html).
|
||||
For more information on pipes, see [Pipes](guide/pipes).
|
||||
|
||||
<table width="100%">
|
||||
|
||||
@ -896,7 +896,7 @@ For more information on pipes, see [Pipes](../guide/pipes.html).
|
||||
</code-example>
|
||||
|
||||
Displays the collection in the order specified by the expression.
|
||||
In this example, the movie title orders the movieList.
|
||||
In this example, the movie title orders the `movieList`.
|
||||
</td>
|
||||
|
||||
|
||||
@ -913,7 +913,7 @@ For more information on pipes, see [Pipes](../guide/pipes.html).
|
||||
|
||||
</table>
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
{@a controllers-components}
|
||||
@ -975,8 +975,8 @@ The Angular code is shown using TypeScript.
|
||||
This is a nonissue in Angular because ES 2015 modules
|
||||
handle the namespacing for you.
|
||||
|
||||
For more information on modules, see the [Modules](../guide/architecture.html#modules) section of the
|
||||
[Architecture Overview](../guide/architecture.html).
|
||||
For more information on modules, see the [Modules](guide/architecture) section of the
|
||||
[Architecture Overview](guide/architecture).
|
||||
</td>
|
||||
|
||||
|
||||
@ -1005,7 +1005,7 @@ The Angular code is shown using TypeScript.
|
||||
- `imports`: specifies the list of other modules that this module depends upon
|
||||
- `declaration`: keeps track of your components, pipes, and directives.
|
||||
|
||||
For more information on modules, see [Angular Modules (NgModule)](../guide/ngmodule.html).
|
||||
For more information on modules, see [Angular Modules (NgModule)](guide/ngmodule).
|
||||
</td>
|
||||
|
||||
|
||||
@ -1043,8 +1043,8 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
This is how you associate a template with logic, which is defined in the component class.
|
||||
|
||||
For more information, see the [Components](../guide/architecture.html#components)
|
||||
section of the [Architecture Overview](../guide/architecture.html) page.
|
||||
For more information, see the [Components](guide/architecture)
|
||||
section of the [Architecture Overview](guide/architecture) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -1073,8 +1073,8 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
NOTE: If you are using TypeScript with AngularJS, you must use the `export` keyword to export the component class.
|
||||
|
||||
For more information, see the [Components](../guide/architecture.html#components)
|
||||
section of the [Architecture Overview](../guide/architecture.html) page.
|
||||
For more information, see the [Components](guide/architecture)
|
||||
section of the [Architecture Overview](guide/architecture) page.
|
||||
</td>
|
||||
|
||||
|
||||
@ -1108,8 +1108,8 @@ The Angular code is shown using TypeScript.
|
||||
This example injects a `MovieService`.
|
||||
The first parameter's TypeScript type tells Angular what to inject, even after minification.
|
||||
|
||||
For more information, see the [Dependency injection](../guide/architecture.html#dependency-injection)
|
||||
section of the [Architecture Overview](../guide/architecture.html).
|
||||
For more information, see the [Dependency injection](guide/architecture)
|
||||
section of the [Architecture Overview](guide/architecture).
|
||||
</td>
|
||||
|
||||
|
||||
@ -1118,7 +1118,7 @@ The Angular code is shown using TypeScript.
|
||||
|
||||
</table>
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
||||
|
||||
|
||||
{@a style-sheets}
|
||||
@ -1192,4 +1192,4 @@ also encapsulate a style sheet within a specific component.
|
||||
|
||||
</table>
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/ajs-quick-reference#top)
|
@ -30,16 +30,16 @@ add it to your page.
|
||||
|
||||
# Contents
|
||||
|
||||
* [Example: Transitioning between two states](#example-transitioning-between-states).
|
||||
* [States and transitions](#states-and-transitions).
|
||||
* [Example: Entering and leaving](#example-entering-and-leaving).
|
||||
* [Example: Entering and leaving from different states](#example-entering-and-leaving-from-different-states).
|
||||
* [Animatable properties and units](#animatable-properties-and-units).
|
||||
* [Automatic property calculation](#automatic-property-calculation).
|
||||
* [Animation timing](#animation-timing).
|
||||
* [Multi-step animations with keyframes](#multi-step-animations-with-keyframes).
|
||||
* [Parallel animation groups](#parallel-animation-groups).
|
||||
* [Animation callbacks](#animation-callbacks).
|
||||
* [Example: Transitioning between two states](guide/animations#example-transitioning-between-states).
|
||||
* [States and transitions](guide/animations#states-and-transitions).
|
||||
* [Example: Entering and leaving](guide/animations#example-entering-and-leaving).
|
||||
* [Example: Entering and leaving from different states](guide/animations#example-entering-and-leaving-from-different-states).
|
||||
* [Animatable properties and units](guide/animations#animatable-properties-and-units).
|
||||
* [Automatic property calculation](guide/animations#automatic-property-calculation).
|
||||
* [Animation timing](guide/animations#animation-timing).
|
||||
* [Multi-step animations with keyframes](guide/animations#multi-step-animations-with-keyframes).
|
||||
* [Parallel animation groups](guide/animations#parallel-animation-groups).
|
||||
* [Animation callbacks](guide/animations#animation-callbacks).
|
||||
|
||||
The examples in this page are available as a <live-example></live-example>.
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Ahead-of-Time Compilation
|
||||
|
||||
@intro
|
||||
Learn how to use Ahead-of-time compilation
|
||||
Learn how to use Ahead-of-time compilation.
|
||||
|
||||
@description
|
||||
This cookbook describes how to radically improve performance by compiling _Ahead of Time_ (AOT)
|
||||
@ -11,16 +11,16 @@ during a build process.
|
||||
|
||||
{@a toc}
|
||||
## Table of Contents
|
||||
* [Overview](#overview)
|
||||
* [_Ahead-of-Time_ vs _Just-in-Time_](#aot-jit)
|
||||
* [Compile with AOT](#compile)
|
||||
* [Bootstrap](#bootstrap)
|
||||
* [Tree Shaking](#tree-shaking)
|
||||
* [Load the bundle](#load)
|
||||
* [Serve the app](#serve)
|
||||
* [Workflow and convenience script](#workflow)
|
||||
* [Source Code](#source-code)
|
||||
* [Tour of Heroes](#toh)
|
||||
* [Overview](guide/aot-compiler#overview)
|
||||
* [_Ahead-of-Time_ vs _Just-in-Time_](guide/aot-compiler#aot-jit)
|
||||
* [Compile with AOT](guide/aot-compiler#compile)
|
||||
* [Bootstrap](guide/aot-compiler#bootstrap)
|
||||
* [Tree Shaking](guide/aot-compiler#tree-shaking)
|
||||
* [Load the bundle](guide/aot-compiler#load)
|
||||
* [Serve the app](guide/aot-compiler#serve)
|
||||
* [Workflow and convenience script](guide/aot-compiler#workflow)
|
||||
* [Source Code](guide/aot-compiler#source-code)
|
||||
* [Tour of Heroes](guide/aot-compiler#toh)
|
||||
|
||||
|
||||
{@a overview}
|
||||
@ -128,7 +128,7 @@ then modify it to look as follows.
|
||||
|
||||
The `compilerOptions` section is unchanged except for one property.
|
||||
**Set the `module` to `es2015`**.
|
||||
This is important as explained later in the [Tree Shaking](#tree-shaking) section.
|
||||
This is important as explained later in the [Tree Shaking](guide/aot-compiler#tree-shaking) section.
|
||||
|
||||
What's really new is the `ngc` section at the bottom called `angularCompilerOptions`.
|
||||
Its `"genDir"` property tells the compiler
|
||||
@ -198,7 +198,7 @@ The AOT path changes application bootstrapping.
|
||||
Instead of bootstrapping `AppModule`, you bootstrap the application with the generated module factory, `AppModuleNgFactory`.
|
||||
|
||||
Make a copy of `main.ts` and name it `main-jit.ts`.
|
||||
This is the JIT version; set it aside as you may need it [later](#run-jit "Running with JIT").
|
||||
This is the JIT version; set it aside as you may need it [later](guide/aot-compiler#run-jit "Running with JIT").
|
||||
|
||||
Open `main.ts` and convert it to AOT compilation.
|
||||
Switch from the `platformBrowserDynamic.bootstrap` used in JIT compilation to
|
||||
@ -408,7 +408,7 @@ The same source code can be built both ways. Here's one way to do that.
|
||||
{@example 'cb-aot-compiler/ts/src/index-jit.html' region='jit'}
|
||||
|
||||
Notice the slight change to the `system.import` which now specifies `src/app/main-jit`.
|
||||
That's the JIT version of the bootstrap file that we preserved [above](#bootstrap)
|
||||
That's the JIT version of the bootstrap file that we preserved [above](guide/aot-compiler#bootstrap)
|
||||
Open a _different_ terminal window and enter.
|
||||
<code-example language="none" class="code-shell">
|
||||
npm start
|
||||
@ -434,7 +434,7 @@ Now you can develop JIT and AOT, side-by-side.
|
||||
|
||||
The sample above is a trivial variation of the QuickStart app.
|
||||
In this section you apply what you've learned about AOT compilation and Tree Shaking
|
||||
to an app with more substance, the tutorial [_Tour of Heroes_](../tutorial/toh-pt6.html).
|
||||
to an app with more substance, the tutorial [_Tour of Heroes_](tutorial/toh-pt6).
|
||||
|
||||
### JIT in development, AOT in production
|
||||
|
||||
@ -472,7 +472,7 @@ It does not need `SystemJS`, so that script is absent from its `index.html`
|
||||
***main.ts***
|
||||
|
||||
JIT and AOT applications boot in much the same way but require different Angular libraries to do so.
|
||||
The key differences, covered in the [Bootstrap](#bootstrap) section above,
|
||||
The key differences, covered in the [Bootstrap](guide/aot-compiler#bootstrap) section above,
|
||||
are evident in these `main` files which can and should reside in the same folder:
|
||||
|
||||
<md-tab-group>
|
@ -7,17 +7,17 @@ Tell Angular how to construct and bootstrap the app in the root "AppModule".
|
||||
@description
|
||||
An Angular module class describes how the application parts fit together.
|
||||
Every application has at least one Angular module, the _root_ module
|
||||
that you [bootstrap](#main) to launch the application.
|
||||
that you [bootstrap](guide/appmodule#main) to launch the application.
|
||||
You can call it anything you want. The conventional name is `AppModule`.
|
||||
|
||||
The [setup](setup.html) instructions produce a new project with the following minimal `AppModule`.
|
||||
The [setup](guide/setup) instructions produce a new project with the following minimal `AppModule`.
|
||||
You'll evolve this module as your application grows.
|
||||
|
||||
|
||||
{@example 'setup/ts/src/app/app.module.ts'}
|
||||
|
||||
After the `import` statements, you come to a class adorned with the
|
||||
**`@NgModule`** [_decorator_](glossary.html#decorator '"Decorator" explained').
|
||||
**`@NgModule`** [_decorator_](guide/glossary).
|
||||
|
||||
The `@NgModule` decorator identifies `AppModule` as an Angular module class (also called an `NgModule` class).
|
||||
`@NgModule` takes a _metadata_ object that tells Angular how to compile and launch the application.
|
||||
@ -26,7 +26,7 @@ The `@NgModule` decorator identifies `AppModule` as an Angular module class (als
|
||||
* **_declarations_** — the application's lone component, which is also ...
|
||||
* **_bootstrap_** — the _root_ component that Angular creates and inserts into the `index.html` host web page.
|
||||
|
||||
The [Angular Modules (NgModule)](ngmodule.html) guide dives deeply into the details of Angular modules.
|
||||
The [Angular Modules (NgModule)](guide/ngmodule) guide dives deeply into the details of Angular modules.
|
||||
All you need to know at the moment is a few basics about these three properties.
|
||||
|
||||
|
||||
@ -76,7 +76,7 @@ You must declare _every_ component in an `NgModule` class.
|
||||
If you use a component without declaring it, you'll see a clear error message in the browser console.
|
||||
|
||||
You'll learn to create two other kinds of classes —
|
||||
[directives](attribute-directives.html) and [pipes](pipes.html) —
|
||||
[directives](guide/attribute-directives) and [pipes](guide/pipes) —
|
||||
that you must also add to the `declarations` array.
|
||||
|
||||
|
||||
@ -93,7 +93,7 @@ Do not put any other kind of class in `declarations`; _not_ `NgModule` classes,
|
||||
{@a bootstrap-array}
|
||||
### The _bootstrap_ array
|
||||
|
||||
You launch the application by [_bootstrapping_](#main) the root `AppModule`.
|
||||
You launch the application by [_bootstrapping_](guide/appmodule#main) the root `AppModule`.
|
||||
Among other things, the _bootstrapping_ process creates the component(s) listed in the `bootstrap` array
|
||||
and inserts each one into the browser DOM.
|
||||
|
||||
@ -154,4 +154,4 @@ As your app grows, you'll consider subdividing it into multiple "feature" module
|
||||
some of which can be loaded later ("lazy loaded") if and when the user chooses
|
||||
to visit those features.
|
||||
|
||||
When you're ready to explore these possibilities, visit the [Angular Modules (NgModule)](ngmodule.html) guide.
|
||||
When you're ready to explore these possibilities, visit the [Angular Modules (NgModule)](guide/ngmodule) guide.
|
@ -2,7 +2,7 @@
|
||||
Architecture Overview
|
||||
|
||||
@intro
|
||||
The basic building blocks of Angular applications
|
||||
The basic building blocks of Angular applications.
|
||||
|
||||
@description
|
||||
You write Angular applications by composing HTML *templates* with Angularized markup,
|
||||
@ -22,14 +22,14 @@ You'll learn the details in the pages that follow. For now, focus on the big pic
|
||||
|
||||
The architecture diagram identifies the eight main building blocks of an Angular application:
|
||||
|
||||
* [Modules](#modules)
|
||||
* [Components](#components)
|
||||
* [Templates](#templates)
|
||||
* [Metadata](#metadata)
|
||||
* [Data binding](#data-binding)
|
||||
* [Directives](#directives)
|
||||
* [Services](#services)
|
||||
* [Dependency injection](#dependency-injection)
|
||||
* [Modules](guide/architecture#modules)
|
||||
* [Components](guide/architecture#components)
|
||||
* [Templates](guide/architecture#templates)
|
||||
* [Metadata](guide/architecture#metadata)
|
||||
* [Data binding](guide/architecture#data-binding)
|
||||
* [Directives](guide/architecture#directives)
|
||||
* [Services](guide/architecture#services)
|
||||
* [Dependency injection](guide/architecture#dependency-injection)
|
||||
|
||||
Learn these building blocks, and you're on your way.
|
||||
|
||||
@ -78,7 +78,7 @@ For example, this `HeroListComponent` has a `heroes` property that returns !{_an
|
||||
that it acquires from a service.
|
||||
`HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
Angular creates, updates, and destroys components as the user moves through the application.
|
||||
Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](lifecycle-hooks.html), like `ngOnInit()` declared above.
|
||||
Your app can take action at each moment in this lifecycle through optional [lifecycle hooks](guide/lifecycle-hooks), like `ngOnInit()` declared above.
|
||||
|
||||
<div class='l-hr'>
|
||||
|
||||
@ -99,7 +99,7 @@ template for our `HeroListComponent`:
|
||||
|
||||
{@example 'architecture/ts/src/app/hero-list.component.html'}
|
||||
|
||||
Although this template uses typical HTML elements like `<h2>` and `<p>`, it also has some differences. Code like `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>` uses Angular's [template syntax](template-syntax.html).
|
||||
Although this template uses typical HTML elements like `<h2>` and `<p>`, it also has some differences. Code like `*ngFor`, `{{hero.name}}`, `(click)`, `[hero]`, and `<hero-detail>` uses Angular's [template syntax](guide/template-syntax).
|
||||
|
||||
|
||||
In the last line of the template, the `<hero-detail>` tag is a custom element that represents a new component, `HeroDetailComponent`.
|
||||
@ -126,7 +126,7 @@ Notice how `<hero-detail>` rests comfortably among native HTML elements. Custom
|
||||
</figure>
|
||||
|
||||
<p style="padding-top:10px">Metadata tells Angular how to process a class.</p>
|
||||
<br class="l-clear-both">[Looking back at the code](#component-code) for `HeroListComponent`, you can see that it's just a class.
|
||||
<br class="l-clear-both">[Looking back at the code](guide/architecture#component-code) for `HeroListComponent`, you can see that it's just a class.
|
||||
There is no evidence of a framework, no "Angular" in it at all.
|
||||
|
||||
In fact, `HeroListComponent` really is *just a class*. It's not a component until you *tell Angular about it*.
|
||||
@ -144,7 +144,7 @@ where it finds a `<hero-list>` tag in *parent* HTML.
|
||||
For example, if an app's HTML contains `<hero-list></hero-list>`, then
|
||||
Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
- `templateUrl`: module-relative address of this component's HTML template, shown [above](#templates).
|
||||
- `templateUrl`: module-relative address of this component's HTML template, shown [above](guide/architecture#templates).
|
||||
- `providers`: !{_array} of **dependency injection providers** for services that the component requires.
|
||||
This is one way to tell Angular that the component's constructor requires a `HeroService`
|
||||
so it can get the list of heroes to display.
|
||||
@ -178,14 +178,14 @@ Angular supports **data binding**,
|
||||
a mechanism for coordinating parts of a template with parts of a component.
|
||||
Add binding markup to the template HTML to tell Angular how to connect both sides.
|
||||
|
||||
As the diagram shows, there are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](#templates) template has three forms:
|
||||
* The `{{hero.name}}` [*interpolation*](displaying-data.html#interpolation)
|
||||
As the diagram shows, there are four forms of data binding syntax. Each form has a direction — to the DOM, from the DOM, or in both directions.<br class="l-clear-both">The `HeroListComponent` [example](guide/architecture#templates) template has three forms:
|
||||
* The `{{hero.name}}` [*interpolation*](guide/displaying-data)
|
||||
displays the component's `hero.name` property value within the `<li>` element.
|
||||
|
||||
* The `[hero]` [*property binding*](template-syntax.html#property-binding) passes the value of `selectedHero` from
|
||||
* The `[hero]` [*property binding*](guide/template-syntax) passes the value of `selectedHero` from
|
||||
the parent `HeroListComponent` to the `hero` property of the child `HeroDetailComponent`.
|
||||
|
||||
* The `(click)` [*event binding*](user-input.html#click) calls the component's `selectHero` method when the user clicks a hero's name.
|
||||
* The `(click)` [*event binding*](guide/user-input) calls the component's `selectHero` method when the user clicks a hero's name.
|
||||
|
||||
**Two-way data binding** is an important fourth form
|
||||
that combines property and event binding in a single notation, using the `ngModel` directive.
|
||||
@ -234,9 +234,9 @@ sometimes by name but more often as the target of an assignment or a binding.
|
||||
|
||||
**Structural** directives alter layout by adding, removing, and replacing elements in DOM.
|
||||
|
||||
The [example template](#templates) uses two built-in structural directives:
|
||||
* [`*ngFor`](displaying-data.html#ngFor) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||
* [`*ngIf`](displaying-data.html#ngIf) includes the `HeroDetail` component only if a selected hero exists.
|
||||
The [example template](guide/architecture#templates) uses two built-in structural directives:
|
||||
* [`*ngFor`](guide/displaying-data) tells Angular to stamp out one `<li>` per hero in the `heroes` list.
|
||||
* [`*ngIf`](guide/displaying-data) includes the `HeroDetail` component only if a selected hero exists.
|
||||
**Attribute** directives alter the appearance or behavior of an existing element.
|
||||
In templates they look like regular HTML attributes, hence the name.
|
||||
|
||||
@ -245,9 +245,9 @@ an example of an attribute directive. `ngModel` modifies the behavior of
|
||||
an existing element (typically an `<input>`)
|
||||
by setting its display value property and responding to change events.
|
||||
Angular has a few more directives that either alter the layout structure
|
||||
(for example, [ngSwitch](template-syntax.html#ngSwitch))
|
||||
(for example, [ngSwitch](guide/template-syntax))
|
||||
or modify aspects of DOM elements and components
|
||||
(for example, [ngStyle](template-syntax.html#ngStyle) and [ngClass](template-syntax.html#ngClass)).
|
||||
(for example, [ngStyle](guide/template-syntax) and [ngClass](guide/template-syntax)).
|
||||
|
||||
Of course, you can also write your own directives. Components such as
|
||||
`HeroListComponent` are one kind of custom directive.
|
||||
@ -336,7 +336,7 @@ Registering at a component level means you get a new instance of the
|
||||
service with each new instance of that component.
|
||||
|
||||
<!-- We've vastly oversimplified dependency injection for this overview.
|
||||
The full story is in the [dependency injection](dependency-injection.html) page. -->
|
||||
The full story is in the [dependency injection](guide/dependency-injection) page. -->
|
||||
|
||||
Points to remember about dependency injection:
|
||||
|
||||
@ -359,14 +359,14 @@ Points to remember about dependency injection:
|
||||
|
||||
You've learned the basics about the eight main building blocks of an Angular application:
|
||||
|
||||
* [Modules](#modules)
|
||||
* [Components](#components)
|
||||
* [Templates](#templates)
|
||||
* [Metadata](#metadata)
|
||||
* [Data binding](#data-binding)
|
||||
* [Directives](#directives)
|
||||
* [Services](#services)
|
||||
* [Dependency injection](#dependency-injection)
|
||||
* [Modules](guide/architecture#modules)
|
||||
* [Components](guide/architecture#components)
|
||||
* [Templates](guide/architecture#templates)
|
||||
* [Metadata](guide/architecture#metadata)
|
||||
* [Data binding](guide/architecture#data-binding)
|
||||
* [Directives](guide/architecture#directives)
|
||||
* [Services](guide/architecture#services)
|
||||
* [Dependency injection](guide/architecture#dependency-injection)
|
||||
|
||||
That's a foundation for everything else in an Angular application,
|
||||
and it's more than enough to get going.
|
||||
@ -375,7 +375,7 @@ But it doesn't include everything you need to know.
|
||||
Here is a brief, alphabetical list of other important Angular features and services.
|
||||
Most of them are covered in this documentation (or soon will be).
|
||||
|
||||
> [**Animations**](animations.html): Animate component behavior
|
||||
> [**Animations**](guide/animations): Animate component behavior
|
||||
without deep knowledge of animation techniques or CSS with Angular's animation library.
|
||||
|
||||
> **Change detection**: The change detection documentation will cover how Angular decides that a component property value has changed,
|
||||
@ -384,18 +384,18 @@ when to update the screen, and how it uses **zones** to intercept asynchronous a
|
||||
> **Events**: The events documentation will cover how to use components and services to raise events with mechanisms for
|
||||
publishing and subscribing to events.
|
||||
|
||||
> [**Forms**](forms.html): Support complex data entry scenarios with HTML-based validation and dirty checking.
|
||||
> [**Forms**](guide/forms): Support complex data entry scenarios with HTML-based validation and dirty checking.
|
||||
|
||||
> [**HTTP**](server-communication.html): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
|
||||
> [**HTTP**](guide/server-communication): Communicate with a server to get data, save data, and invoke server-side actions with an HTTP client.
|
||||
|
||||
> [**Lifecycle hooks**](lifecycle-hooks.html): Tap into key moments in the lifetime of a component, from its creation to its destruction,
|
||||
> [**Lifecycle hooks**](guide/lifecycle-hooks): Tap into key moments in the lifetime of a component, from its creation to its destruction,
|
||||
by implementing the lifecycle hook interfaces.
|
||||
|
||||
> [**Pipes**](pipes.html): Use pipes in your templates to improve the user experience by transforming values for display. Consider this `currency` pipe expression:
|
||||
> [**Pipes**](guide/pipes): Use pipes in your templates to improve the user experience by transforming values for display. Consider this `currency` pipe expression:
|
||||
>
|
||||
> > `price | currency:'USD':true`
|
||||
>
|
||||
> It displays a price of 42.33 as `$42.33`.
|
||||
|
||||
> [**Router**](router.html): Navigate from page to page within the client
|
||||
> [**Router**](guide/router): Navigate from page to page within the client
|
||||
application and never leave the browser.
|
||||
|
@ -9,19 +9,15 @@ An **Attribute** directive changes the appearance or behavior of a DOM element.
|
||||
|
||||
# Contents
|
||||
|
||||
|
||||
* [Directives overview](#directive-overview)
|
||||
* [Build a simple attribute directive](#write-directive)
|
||||
* [Apply the attribute directive to an element in a template](#apply-directive)
|
||||
* [Respond to user-initiated events](#respond-to-user)
|
||||
* [Pass values into the directive with an _@Input_ data binding](#bindings)
|
||||
* [Bind to a second property](#second-property)
|
||||
* [Directives overview](guide/attribute-directives#directive-overview)
|
||||
* [Build a simple attribute directive](guide/attribute-directives#write-directive)
|
||||
* [Apply the attribute directive to an element in a template](guide/attribute-directives#apply-directive)
|
||||
* [Respond to user-initiated events](guide/attribute-directives#respond-to-user)
|
||||
* [Pass values into the directive with an _@Input_ data binding](guide/attribute-directives#bindings)
|
||||
* [Bind to a second property](guide/attribute-directives#second-property)
|
||||
|
||||
Try the <live-example title="Attribute Directive example"></live-example>.
|
||||
|
||||
|
||||
|
||||
{@a directive-overview}
|
||||
## Directives overview
|
||||
|
||||
There are three kinds of directives in Angular:
|
||||
@ -31,20 +27,19 @@ There are three kinds of directives in Angular:
|
||||
1. Attribute directives—change the appearance or behavior of an element, component, or another directive.
|
||||
|
||||
*Components* are the most common of the three directives.
|
||||
You saw a component for the first time in the [QuickStart](../quickstart.html) guide.
|
||||
You saw a component for the first time in the [QuickStart](quickstart) guide.
|
||||
|
||||
*Structural Directives* change the structure of the view.
|
||||
Two examples are [NgFor](template-syntax.html#ngFor) and [NgIf](template-syntax.html#ngIf).
|
||||
Learn about them in the [Structural Directives](structural-directives.html) guide.
|
||||
Two examples are [NgFor](guide/template-syntax) and [NgIf](guide/template-syntax).
|
||||
Learn about them in the [Structural Directives](guide/structural-directives) guide.
|
||||
|
||||
*Attribute directives* are used as attributes of elements.
|
||||
The built-in [NgStyle](template-syntax.html#ngStyle) directive in the [Template Syntax](template-syntax.html) guide, for example,
|
||||
The built-in [NgStyle](guide/template-syntax) directive in the
|
||||
[Template Syntax](guide/template-syntax) guide, for example,
|
||||
can change several element styles at the same time.
|
||||
|
||||
|
||||
|
||||
{@a write-directive}
|
||||
## Build a simple attribute directive
|
||||
|
||||
An attribute directive minimally requires building a controller class annotated with
|
||||
`@Directive`, which specifies the selector that identifies
|
||||
the attribute.
|
||||
@ -53,35 +48,45 @@ The controller class implements the desired directive behavior.
|
||||
This page demonstrates building a simple _myHighlight_ attribute
|
||||
directive to set an element's background color
|
||||
when the user hovers over that element. You can apply it like this:
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.1.html' region='applied'}
|
||||
|
||||
### Write the directive code
|
||||
Follow the [setup](setup.html) instructions for creating a new local project
|
||||
|
||||
Follow the [setup](guide/setup) instructions for creating a new local project
|
||||
named <span ngio-ex>attribute-directives</span>.
|
||||
Create the following source file in `src/app` with the following code:
|
||||
|
||||
Create the following source file in the indicated folder:
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.1.ts'}
|
||||
|
||||
The `import` statement specifies symbols from the Angular `core`:
|
||||
|
||||
1. `Directive` provides the functionality of the `@Directive` decorator.
|
||||
1. `ElementRef` [injects](guide/dependency-injection) into the directive's constructor
|
||||
so the code can access the DOM element.
|
||||
1. `Input` allows data to flow from the binding expression into the directive.
|
||||
|
||||
Next, the `@Directive` decorator function contains the directive metadata in a configuration object
|
||||
as an argument.
|
||||
`@Directive` requires a CSS selector to identify
|
||||
the HTML in the template that is associated with the directive.
|
||||
The [CSS selector for an attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors)
|
||||
is the attribute name in square brackets.
|
||||
Here, the directive's selector is `[myHighlight]`.
|
||||
Angular locates all elements in the template that have an attribute named `myHighlight`.
|
||||
|
||||
### Why not call it "highlight"?
|
||||
|
||||
Though *highlight* is a more concise name than *myHighlight* and would work,
|
||||
a best practice is to prefix selector names to ensure
|
||||
they don't conflict with standard HTML attributes.
|
||||
This also reduces the risk of colliding with third-party directive names.
|
||||
|
||||
Make sure you do **not** prefix the `highlight` directive name with **`ng`** because
|
||||
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose. For a simple demo, the short prefix, `my`, helps distinguish your custom directive.
|
||||
|
||||
<p>
|
||||
After the <code> @Directive </code> metadata comes the directive's controller class, called <code> HighlightDirective </code> , which contains the logic for the directive.
|
||||
</p>
|
||||
that prefix is reserved for Angular and using it could cause bugs that are difficult to diagnose.
|
||||
For a simple demo, the short prefix, `my`, helps distinguish your custom directive.
|
||||
After the `@Directive` metadata comes the directive's controller class,
|
||||
called `HighlightDirective`, which contains the logic for the directive.
|
||||
<span if-docs="ts">Exporting `HighlightDirective` makes it accessible to other components.</span>
|
||||
|
||||
Angular creates a new instance of the directive's controller class for
|
||||
each matching element, injecting an Angular `ElementRef`
|
||||
@ -89,29 +94,28 @@ into the constructor.
|
||||
`ElementRef` is a service that grants direct access to the DOM element
|
||||
through its `nativeElement` property.
|
||||
|
||||
|
||||
|
||||
{@a apply-directive}
|
||||
## Apply the attribute directive
|
||||
|
||||
To use the new `HighlightDirective`, create a template that
|
||||
applies the directive as an attribute to a paragraph (`<p>`) element.
|
||||
In Angular terms, the `<p>` element is the attribute **host**.
|
||||
<p>
|
||||
Put the template in its own <code> </code> file that looks like this:
|
||||
</p>
|
||||
|
||||
Put the template in its own <span ngio-ex>app.component.html</span>
|
||||
file that looks like this:
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.1.html'}
|
||||
|
||||
Now reference this template in the `AppComponent`:
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.ts'}
|
||||
|
||||
Next, add an `import` statement to fetch the `Highlight` directive and
|
||||
add that class to the `declarations` NgModule metadata. This way Angular
|
||||
recognizes the directive when it encounters `myHighlight` in the template.
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.module.ts'}
|
||||
|
||||
Now when the app runs, the `myHighlight` directive highlights the paragraph text.
|
||||
@ -123,8 +127,10 @@ Now when the app runs, the `myHighlight` directive highlights the paragraph text
|
||||
|
||||
### Your directive isn't working?
|
||||
|
||||
Did you remember to add the directive to the `declarations` attribute of `@NgModule`? It is easy to forget!
|
||||
Did you remember to add the directive to the `declarations` attribute of `@NgModule`?
|
||||
It is easy to forget!
|
||||
Open the console in the browser tools and look for an error like this:
|
||||
|
||||
<code-example format="nocode">
|
||||
EXCEPTION: Template parse errors:
|
||||
Can't bind to 'myHighlight' since it isn't a known property of 'p'.
|
||||
@ -140,9 +146,6 @@ It created an instance of the `HighlightDirective` class and
|
||||
injected a reference to the `<p>` element into the directive's constructor
|
||||
which sets the `<p>` element's background style to yellow.
|
||||
|
||||
|
||||
|
||||
{@a respond-to-user}
|
||||
## Respond to user-initiated events
|
||||
|
||||
Currently, `myHighlight` simply sets an element color.
|
||||
@ -152,14 +155,10 @@ and respond by setting or clearing the highlight color.
|
||||
|
||||
Begin by adding `HostListener` to the list of imported symbols;
|
||||
add the `Input` symbol as well because you'll need it soon.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.ts' region='imports'}
|
||||
|
||||
Then add two eventhandlers that respond when the mouse enters or leaves, each adorned by the `HostListener` !{_decorator}.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='mouse-methods'}
|
||||
|
||||
The `@HostListener` !{_decorator} lets you subscribe to events of the DOM element that hosts an attribute directive, the `<p>` in this case.
|
||||
Then add two eventhandlers that respond when the mouse enters or leaves,
|
||||
each adorned by the `HostListener` !{_decorator}.
|
||||
The `@HostListener` !{_decorator} lets you subscribe to events of the DOM
|
||||
element that hosts an attribute directive, the `<p>` in this case.
|
||||
|
||||
Of course you could reach into the DOM with standard JavaScript and and attach event listeners manually.
|
||||
There are at least three problems with _that_ approach:
|
||||
@ -169,24 +168,19 @@ There are at least three problems with _that_ approach:
|
||||
1. Talking to DOM API directly isn't a best practice.
|
||||
The handlers delegate to a helper method that sets the color on the DOM element, `#{_priv}el`,
|
||||
which you declare and initialize in the constructor.
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='ctor'}
|
||||
|
||||
Here's the updated directive in full:
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts'}
|
||||
|
||||
Run the app and confirm that the background color appears when the mouse hovers over the `p` and
|
||||
disappears as it moves out.
|
||||
Run the app and confirm that the background color appears when
|
||||
the mouse hovers over the `p` and disappears as it moves out.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/devguide/attribute-directives/highlight-directive-anim.gif" alt="Second Highlight"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
|
||||
{@a bindings}
|
||||
## Pass values into the directive with an _@Input_ data binding
|
||||
|
||||
Currently the highlight color is hard-coded _within_ the directive. That's inflexible.
|
||||
@ -194,9 +188,6 @@ In this section, you give the developer the power to set the highlight color whi
|
||||
|
||||
Start by adding a `highlightColor` property to the directive class like this:
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='color'}
|
||||
|
||||
|
||||
|
||||
{@a input}
|
||||
### Binding to an _@Input_ property
|
||||
@ -204,33 +195,18 @@ Start by adding a `highlightColor` property to the directive class like this:
|
||||
Notice the `@Input` !{_decorator}. It adds metadata to the class that makes the directive's `highlightColor` property available for binding.
|
||||
|
||||
It's called an *input* property because data flows from the binding expression _into_ the directive.
|
||||
Without that input metadata, Angular rejects the binding; see [below](#why-input "Why add @Input?") for more about that.
|
||||
Without that input metadata, Angular rejects the binding; see [below](guide/attribute-directives#why-input "Why add @Input?") for more about that.
|
||||
|
||||
Try it by adding the following directive binding variations to the `AppComponent` template:
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.1.html' region='color-1'}
|
||||
|
||||
Add a `color` property to the `AppComponent`.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.1.ts' region='class'}
|
||||
|
||||
Let it control the highlight color with a property binding.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.1.html' region='color-2'}
|
||||
|
||||
That's good, but it would be nice to _simultaneously_ apply the directive and set the color _in the same attribute_ like this.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.html' region='color'}
|
||||
|
||||
The `[myHighlight]` attribute binding both applies the highlighting directive to the `<p>` element
|
||||
and sets the directive's highlight color with a property binding.
|
||||
You're re-using the directive's attribute selector (`[myHighlight]`) to do both jobs.
|
||||
That's a crisp, compact syntax.
|
||||
|
||||
You'll have to rename the directive's `highlightColor` property to `myHighlight` because that's now the color property binding name.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='color-2'}
|
||||
|
||||
This is disagreeable. The word, `myHighlight`, is a terrible property name and it doesn't convey the property's intent.
|
||||
|
||||
|
||||
@ -244,64 +220,58 @@ _Inside_ the directive the property is known as `highlightColor`.
|
||||
_Outside_ the directive, where you bind to it, it's known as `myHighlight`.
|
||||
|
||||
You get the best of both worlds: the property name you want and the binding syntax you want:
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.html' region='color'}
|
||||
|
||||
Now that you're binding to `highlightColor`, modify the `onMouseEnter()` method to use it.
|
||||
If someone neglects to bind to `highlightColor`, highlight in "red" by default.
|
||||
If someone neglects to bind to `highlightColor`, highlight in red:
|
||||
Here's the latest version of the directive class.
|
||||
## Write a harness to try it
|
||||
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.3.ts' region='mouse-enter'}
|
||||
|
||||
Here's the latest version of the directive class.## Write a harness to try itIt may be difficult to imagine how this directive actually works.
|
||||
It may be difficult to imagine how this directive actually works.
|
||||
In this section, you'll turn `AppComponent` into a harness that
|
||||
lets you pick the highlight color with a radio button and bind your color choice to the directive.
|
||||
|
||||
Update `app.component.html` as follows:
|
||||
Revise the `AppComponent.color` so that it has no initial value.Here is the harness and directive in action.
|
||||
Update <span ngio-ex>app.component.html</span> as follows:
|
||||
Revise the `AppComponent.color` so that it has no initial value.
|
||||
Here are the harness and directive in action.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/devguide/attribute-directives/highlight-directive-v2-anim.gif" alt="Highlight v.2"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
|
||||
|
||||
{@a second-property}
|
||||
## Bind to a second property
|
||||
|
||||
This highlight directive has a single customizable property. In a real app, it may need more.
|
||||
|
||||
At the moment, the default color—the color that prevails until
|
||||
the user picks a highlight color—is hard-coded as "red".
|
||||
Let the template developer set the default color.
|
||||
|
||||
Add a second **input** property to `HighlightDirective` called `defaultColor`:Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
|
||||
Add a second **input** property to `HighlightDirective` called `defaultColor`:
|
||||
Revise the directive's `onMouseEnter` so that it first tries to highlight with the `highlightColor`,
|
||||
then with the `defaultColor`, and falls back to "red" if both properties are undefined.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.ts' region='mouse-enter'}
|
||||
|
||||
How do you bind to a second property when you're already binding to the `myHighlight` attribute name?
|
||||
|
||||
As with components, you can add as many directive property bindings as you need by stringing them along in the template.
|
||||
The developer should be able to write the following template HTML to both bind to the `AppComponent.color`
|
||||
and fall back to "violet" as the default color.
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.html' region='defaultColor'}
|
||||
|
||||
Angular knows that the `defaultColor` binding belongs to the `HighlightDirective`
|
||||
because you made it _public_ with the `@Input` !{_decorator}.
|
||||
|
||||
Here's how the harness should work when you're done coding.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/devguide/attribute-directives/highlight-directive-final-anim.gif" alt="Final Highlight"> </img>
|
||||
</figure>
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
This page covered how to:
|
||||
- [Build an **attribute directive**](#write-directive) that modifies the behavior of an element.
|
||||
- [Apply the directive](#apply-directive) to an element in a template.
|
||||
- [Respond to **events**](#respond-to-user) that change the directive's behavior.
|
||||
- [**Bind** values to the directive](#bindings).
|
||||
|
||||
- [Build an **attribute directive**](guide/attribute-directives#write-directive) that modifies the behavior of an element.
|
||||
- [Apply the directive](guide/attribute-directives#apply-directive) to an element in a template.
|
||||
- [Respond to **events**](guide/attribute-directives#respond-to-user) that change the directive's behavior.
|
||||
- [**Bind** values to the directive](guide/attribute-directives#bindings).
|
||||
|
||||
The final source code follows:
|
||||
|
||||
@ -341,20 +311,11 @@ The final source code follows:
|
||||
|
||||
You can also experience and download the <live-example title="Attribute Directive example"></live-example>.
|
||||
|
||||
|
||||
{@a why-input}
|
||||
|
||||
### Appendix: Why add _@Input_?
|
||||
|
||||
In this demo, the `hightlightColor` property is an ***input*** property of
|
||||
the `HighlightDirective`. You've seen it applied without an alias:
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.2.ts' region='color'}
|
||||
|
||||
You've seen it with an alias:
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/highlight.directive.ts' region='color'}
|
||||
|
||||
Either way, the `@Input` !{_decorator} tells Angular that this property is
|
||||
_public_ and available for binding by a parent component.
|
||||
Without `@Input`, Angular refuses to bind to the property.
|
||||
@ -377,20 +338,17 @@ Only then can it be bound by some other component or directive.
|
||||
You can tell if `@Input` is needed by the position of the property name in a binding.
|
||||
|
||||
* When it appears in the template expression to the ***right*** of the equals (=),
|
||||
it belongs to the template's component and does not require the `@Input` !{_decorator}.
|
||||
it belongs to the template's component and does not require the `@Input` !{_decorator}.
|
||||
|
||||
* When it appears in **square brackets** ([ ]) to the **left** of the equals (=),
|
||||
the property belongs to some _other_ component or directive;
|
||||
that property must be adorned with the `@Input` !{_decorator}.
|
||||
the property belongs to some _other_ component or directive;
|
||||
that property must be adorned with the `@Input` !{_decorator}.
|
||||
|
||||
Now apply that reasoning to the following example:
|
||||
|
||||
{@example 'attribute-directives/ts/src/app/app.component.html' region='color'}
|
||||
|
||||
* The `color` property in the expression on the right belongs to the template's component.
|
||||
The template and its component trust each other.
|
||||
The `color` property doesn't require the `@Input` !{_decorator}.
|
||||
The template and its component trust each other.
|
||||
The `color` property doesn't require the `@Input` !{_decorator}.
|
||||
|
||||
* The `myHighlight` property on the left refers to an _aliased_ property of the `MyHighlightDirective`,
|
||||
not a property of the template's component. There are trust issues.
|
||||
Therefore, the directive property must carry the `@Input` !{_decorator}.
|
||||
* The `myHighlight` property on the left refers to an _aliased_ property of the `HighlightDirective`,
|
||||
not a property of the template's component. There are trust issues.
|
||||
Therefore, the directive property must carry the `@Input` !{_decorator}.
|
@ -244,7 +244,7 @@ using <a href="https://saucelabs.com/" target="_blank">SauceLabs</a> and
|
||||
Angular is built on the latest standards of the web platform.
|
||||
Targeting such a wide range of browsers is challenging because they do not support all features of modern browsers.
|
||||
|
||||
You compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`)
|
||||
You can compensate by loading polyfill scripts ("polyfills") on the host web page (`index.html`)
|
||||
that implement missing features in JavaScript.
|
||||
|
||||
{@example 'quickstart/ts/src/index.html' region='polyfills'}
|
||||
@ -252,12 +252,12 @@ that implement missing features in JavaScript.
|
||||
A particular browser may require at least one polyfill to run _any_ Angular application.
|
||||
You may need additional polyfills for specific features.
|
||||
|
||||
The tables below will help you determine which polyfills to load, depending on the browsers you target and the features you use.
|
||||
The tables below can help you determine which polyfills to load, depending on the browsers you target and the features you use.
|
||||
|
||||
|
||||
~~~ {.alert.is-important}
|
||||
|
||||
The suggested polyfills are the ones we know will run full Angular applications.
|
||||
The suggested polyfills are the ones that run full Angular applications.
|
||||
You may need additional polyfills to support features not covered by this list.
|
||||
Note that polyfills cannot magically transform an old, slow browser into a modern, fast one.
|
||||
|
||||
@ -307,7 +307,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
|
||||
<td>
|
||||
[ES6](#core-es6)
|
||||
[ES6](guide/browser-support#core-es6)
|
||||
</td>
|
||||
|
||||
|
||||
@ -322,7 +322,7 @@ These are the polyfills required to run an Angular application on each supported
|
||||
|
||||
|
||||
<td>
|
||||
[ES6<br>classList](#classlist)
|
||||
[ES6<br>classList](guide/browser-support#classlist)
|
||||
|
||||
</td>
|
||||
|
||||
@ -370,7 +370,7 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
<td>
|
||||
[Web Animations](#web-animations)
|
||||
[Web Animations](guide/browser-support#web-animations)
|
||||
</td>
|
||||
|
||||
|
||||
@ -390,7 +390,7 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
<td>
|
||||
[Intl API](#intl)
|
||||
[Intl API](guide/browser-support#intl)
|
||||
</td>
|
||||
|
||||
|
||||
@ -410,7 +410,7 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
<td>
|
||||
[classList](#classlist)
|
||||
[classList](guide/browser-support#classlist)
|
||||
</td>
|
||||
|
||||
|
||||
@ -430,7 +430,7 @@ Here are the features which may require additional polyfills:
|
||||
|
||||
|
||||
<td>
|
||||
[Typed Array](#typedarray) <br>[Blob](#blob)<br>[FormData](#formdata)
|
||||
[Typed Array](guide/browser-support#typedarray) <br>[Blob](guide/browser-support#blob)<br>[FormData](guide/browser-support#formdata)
|
||||
</td>
|
||||
|
||||
|
||||
@ -457,7 +457,7 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
|
||||
|
||||
<th>
|
||||
Licence
|
||||
License
|
||||
</th>
|
||||
|
||||
|
||||
@ -517,7 +517,7 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
|
||||
|
||||
<td>
|
||||
MIT / Unicode licence
|
||||
MIT / Unicode license
|
||||
</td>
|
||||
|
||||
|
||||
@ -611,4 +611,5 @@ Below are the polyfills which are used to test the framework itself. They are a
|
||||
|
||||
</table>
|
||||
|
||||
\* Figures are for minified and gzipped code, computed with the <a href="http://closure-compiler.appspot.com/home" target="_blank">closure compiler</a>
|
||||
\* Figures are for minified and gzipped code,
|
||||
computed with the <a href="http://closure-compiler.appspot.com/home" target="_blank">closure compiler</a>.
|
@ -2,48 +2,48 @@
|
||||
Dependency Injection
|
||||
|
||||
@intro
|
||||
Techniques for Dependency Injection
|
||||
Techniques for Dependency Injection.
|
||||
|
||||
@description
|
||||
Dependency Injection is a powerful pattern for managing code dependencies.
|
||||
In this cookbook we will explore many of the features of Dependency Injection (DI) in Angular.
|
||||
<a id="toc"></a>## Table of contents
|
||||
|
||||
[Application-wide dependencies](#app-wide-dependencies)
|
||||
[Application-wide dependencies](guide/cb-dependency-injection#app-wide-dependencies)
|
||||
|
||||
[External module configuration](#external-module-configuration)
|
||||
[External module configuration](guide/cb-dependency-injection#external-module-configuration)
|
||||
|
||||
[*@Injectable* and nested service dependencies](#nested-dependencies)
|
||||
[*@Injectable* and nested service dependencies](guide/cb-dependency-injection#nested-dependencies)
|
||||
|
||||
[Limit service scope to a component subtree](#service-scope)
|
||||
[Limit service scope to a component subtree](guide/cb-dependency-injection#service-scope)
|
||||
|
||||
[Multiple service instances (sandboxing)](#multiple-service-instances)
|
||||
[Multiple service instances (sandboxing)](guide/cb-dependency-injection#multiple-service-instances)
|
||||
|
||||
[Qualify dependency lookup with *@Optional* and *@Host*](#qualify-dependency-lookup)
|
||||
[Qualify dependency lookup with *@Optional* and *@Host*](guide/cb-dependency-injection#qualify-dependency-lookup)
|
||||
|
||||
[Inject the component's DOM element](#component-element)
|
||||
[Inject the component's DOM element](guide/cb-dependency-injection#component-element)
|
||||
|
||||
[Define dependencies with providers](#providers)
|
||||
* [The *provide* object literal](#provide)
|
||||
* [useValue - the *value provider*](#usevalue)
|
||||
* [useClass - the *class provider*](#useclass)
|
||||
* [useExisting - the *alias provider*](#useexisting)
|
||||
* [useFactory - the *factory provider*](#usefactory)
|
||||
[Define dependencies with providers](guide/cb-dependency-injection#providers)
|
||||
* [The *provide* object literal](guide/cb-dependency-injection#provide)
|
||||
* [useValue - the *value provider*](guide/cb-dependency-injection#usevalue)
|
||||
* [useClass - the *class provider*](guide/cb-dependency-injection#useclass)
|
||||
* [useExisting - the *alias provider*](guide/cb-dependency-injection#useexisting)
|
||||
* [useFactory - the *factory provider*](guide/cb-dependency-injection#usefactory)
|
||||
|
||||
[Provider token alternatives](#tokens)
|
||||
* [class-interface](#class-interface)
|
||||
* [OpaqueToken](#opaque-token)
|
||||
[Provider token alternatives](guide/cb-dependency-injection#tokens)
|
||||
* [class-interface](guide/cb-dependency-injection#class-interface)
|
||||
* [OpaqueToken](guide/cb-dependency-injection#opaque-token)
|
||||
|
||||
[Inject into a derived class](#di-inheritance)
|
||||
[Inject into a derived class](guide/cb-dependency-injection#di-inheritance)
|
||||
|
||||
[Find a parent component by injection](#find-parent)
|
||||
* [Find parent with a known component type](#known-parent)
|
||||
* [Cannot find a parent by its base class](#base-parent)
|
||||
* [Find a parent by its class-interface](#class-interface-parent)
|
||||
* [Find a parent in a tree of parents (*@SkipSelf*)](#parent-tree)
|
||||
* [A *provideParent* helper function](#provideparent)
|
||||
[Find a parent component by injection](guide/cb-dependency-injection#find-parent)
|
||||
* [Find parent with a known component type](guide/cb-dependency-injection#known-parent)
|
||||
* [Cannot find a parent by its base class](guide/cb-dependency-injection#base-parent)
|
||||
* [Find a parent by its class-interface](guide/cb-dependency-injection#class-interface-parent)
|
||||
* [Find a parent in a tree of parents (*@SkipSelf*)](guide/cb-dependency-injection#parent-tree)
|
||||
* [A *provideParent* helper function](guide/cb-dependency-injection#provideparent)
|
||||
|
||||
[Break circularities with a forward class reference (*forwardRef*)](#forwardref)
|
||||
[Break circularities with a forward class reference (*forwardRef*)](guide/cb-dependency-injection#forwardref)
|
||||
**See the <live-example name="cb-dependency-injection"></live-example>**
|
||||
of the code supporting this cookbook.
|
||||
|
||||
@ -62,7 +62,7 @@ Service classes can act as their own providers which is why listing them in the
|
||||
is all the registration we need.
|
||||
A *provider* is something that can create or deliver a service.
|
||||
Angular creates a service instance from a class provider by "new-ing" it.
|
||||
Learn more about providers [below](#providers).Now that we've registered these services,
|
||||
Learn more about providers [below](guide/cb-dependency-injection#providers).Now that we've registered these services,
|
||||
Angular can inject them into the constructor of *any* component or service, *anywhere* in the application.
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='ctor'}
|
||||
@ -79,7 +79,7 @@ We do this when (a) we expect the service to be injectable everywhere
|
||||
or (b) we must configure another application global service _before it starts_.
|
||||
|
||||
We see an example of the second case here, where we configure the Component Router with a non-default
|
||||
[location strategy](../guide/router.html#location-strategy) by listing its provider
|
||||
[location strategy](guide/router) by listing its provider
|
||||
in the `providers` list of the `AppModule`.
|
||||
|
||||
|
||||
@ -136,7 +136,7 @@ Technically, the `@Injectable()`decorator is only _required_ for a service class
|
||||
The `LoggerService` doesn't depend on anything. The logger would work if we omitted `@Injectable()`
|
||||
and the generated code would be slightly smaller.
|
||||
|
||||
But the service would break the moment we gave it a dependency and we'd have to go back and
|
||||
But the service would break the moment we gave it a dependency and we'd have to go back
|
||||
and add `@Injectable()` to fix it. We add `@Injectable()` from the start for the sake of consistency and to avoid future pain.
|
||||
|
||||
|
||||
@ -264,7 +264,7 @@ But when this component is projected into a *parent* component, that parent comp
|
||||
We look at this second, more interesting case in our next example.
|
||||
|
||||
### Demonstration
|
||||
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](#hero-bios-component).
|
||||
The `HeroBiosAndContactsComponent` is a revision of the `HeroBiosComponent` that we looked at [above](guide/cb-dependency-injection#hero-bios-component).
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='hero-bios-and-contacts'}
|
||||
|
||||
@ -325,7 +325,7 @@ Although we strive to avoid it, many visual effects and 3rd party tools (such as
|
||||
require DOM access.
|
||||
|
||||
To illustrate, we've written a simplified version of the `HighlightDirective` from
|
||||
the [Attribute Directives](../guide/attribute-directives.html) chapter.
|
||||
the [Attribute Directives](guide/attribute-directives) chapter.
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/highlight.directive.ts'}
|
||||
|
||||
@ -362,7 +362,7 @@ Here's a typical example:
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/hero-bios.component.ts' region='ctor'}
|
||||
|
||||
Angular asks the injector for the service associated with the `LoggerService` and
|
||||
Angular asks the injector for the service associated with the `LoggerService`
|
||||
and assigns the returned value to the `logger` parameter.
|
||||
|
||||
Where did the injector get that value?
|
||||
@ -371,7 +371,7 @@ If it doesn't, it may be able to make one with the help of a ***provider***.
|
||||
A *provider* is a recipe for delivering a service associated with a *token*.
|
||||
If the injector doesn't have a provider for the requested *token*, it delegates the request
|
||||
to its parent injector, where the process repeats until there are no more injectors.
|
||||
If the search is futile, the injector throws an error ... unless the request was [optional](#optional).
|
||||
If the search is futile, the injector throws an error ... unless the request was [optional](guide/cb-dependency-injection#optional).
|
||||
|
||||
Let's return our attention to providers themselves.A new injector has no providers.
|
||||
Angular initializes the injectors it creates with some providers it cares about.
|
||||
@ -408,7 +408,7 @@ It's visually simple: a few properties and the output of a logger. The code behi
|
||||
#### The *provide* object literal
|
||||
|
||||
The `provide` object literal takes a *token* and a *definition object*.
|
||||
The *token* is usually a class but [it doesn't have to be](#tokens).
|
||||
The *token* is usually a class but [it doesn't have to be](guide/cb-dependency-injection#tokens).
|
||||
|
||||
The *definition* object has one main property, (e.g. `useValue`) that indicates how the provider
|
||||
should create or return the provided value.
|
||||
@ -433,7 +433,7 @@ The `Hero` provider token is a class which makes sense because the value is a `H
|
||||
and the consumer of the injected hero would want the type information.
|
||||
|
||||
The `TITLE` provider token is *not a class*.
|
||||
It's a special kind of provider lookup key called an [OpaqueToken](#opaquetoken).
|
||||
It's a special kind of provider lookup key called an [OpaqueToken](guide/cb-dependency-injection#opaquetoken).
|
||||
We often use an `OpaqueToken` when the dependency is a simple value like a string, a number, or a function.
|
||||
|
||||
The value of a *value provider* must be defined *now*. We can't create the value later.
|
||||
@ -486,7 +486,7 @@ creating ***two ways to access the same service object***.
|
||||
Narrowing an API through an aliasing interface is _one_ important use case for this technique.
|
||||
We're aliasing for that very purpose here.
|
||||
Imagine that the `LoggerService` had a large API (it's actually only three methods and a property).
|
||||
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](#class-interface):
|
||||
We want to shrink that API surface to just the two members exposed by the `MinimalLogger` [*class-interface*](guide/cb-dependency-injection#class-interface):
|
||||
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/date-logger.service.ts' region='minimal-logger'}
|
||||
@ -718,7 +718,7 @@ after injecting an `AlexComponent` into her constructor:
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='cathy'}
|
||||
|
||||
We added the [@Optional](#optional) qualifier for safety but
|
||||
We added the [@Optional](guide/cb-dependency-injection#optional) qualifier for safety but
|
||||
the <live-example name="cb-dependency-injection"></live-example>
|
||||
confirms that the `alex` parameter is set.
|
||||
|
||||
@ -741,7 +741,7 @@ That's not possible because TypeScript interfaces disappear from the transpiled
|
||||
which doesn't support interfaces. There's no artifact we could look for.We're not claiming this is good design.
|
||||
We are asking *can a component inject its parent via the parent's base class*?
|
||||
|
||||
The sample's `CraigComponent` explores this question. [Looking back](#alex)
|
||||
The sample's `CraigComponent` explores this question. [Looking back](guide/cb-dependency-injection#alex)
|
||||
we see that the `Alex` component *extends* (*inherits*) from a class named `Base`.
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-class-signature'}
|
||||
@ -758,14 +758,14 @@ confirms that the `alex` parameter is null.
|
||||
<a id="class-interface-parent"></a>
|
||||
### Find a parent by its class-interface
|
||||
|
||||
We can find a parent component with a [class-interface](#class-interface).
|
||||
We can find a parent component with a [class-interface](guide/cb-dependency-injection#class-interface).
|
||||
|
||||
The parent must cooperate by providing an *alias* to itself in the name of a *class-interface* token.
|
||||
|
||||
Recall that Angular always adds a component instance to its own injector;
|
||||
that's why we could inject *Alex* into *Cathy* [earlier](#known-parent).
|
||||
that's why we could inject *Alex* into *Cathy* [earlier](guide/cb-dependency-injection#known-parent).
|
||||
|
||||
We write an [*alias provider*](#useexisting) — a `provide` object literal with a `useExisting` definition —
|
||||
We write an [*alias provider*](guide/cb-dependency-injection#useexisting) — a `provide` object literal with a `useExisting` definition —
|
||||
that creates an *alternative* way to inject the same component instance
|
||||
and add that provider to the `providers` array of the `@Component` metadata for the `AlexComponent`:
|
||||
|
||||
@ -774,8 +774,8 @@ and add that provider to the `providers` array of the `@Component` metadata for
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-providers'}
|
||||
|
||||
[Parent](#parent-token) is the provider's *class-interface* token.
|
||||
The [*forwardRef*](#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself.
|
||||
[Parent](guide/cb-dependency-injection#parent-token) is the provider's *class-interface* token.
|
||||
The [*forwardRef*](guide/cb-dependency-injection#forwardref) breaks the circular reference we just created by having the `AlexComponent` refer to itself.
|
||||
|
||||
*Carol*, the third of *Alex*'s child components, injects the parent into its `parent` parameter, the same way we've done it before:
|
||||
|
||||
@ -802,8 +802,8 @@ Here's *Barry*:
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='barry'}
|
||||
|
||||
*Barry*'s `providers` array looks just like [*Alex*'s](#alex-providers).
|
||||
If we're going to keep writing [*alias providers*](#useexisting) like this we should create a [helper function](#provideparent).
|
||||
*Barry*'s `providers` array looks just like [*Alex*'s](guide/cb-dependency-injection#alex-providers).
|
||||
If we're going to keep writing [*alias providers*](guide/cb-dependency-injection#useexisting) like this we should create a [helper function](guide/cb-dependency-injection#provideparent).
|
||||
|
||||
For now, focus on *Barry*'s constructor:
|
||||
<md-tab-group>
|
||||
@ -841,7 +841,7 @@ Here's *Alice*, *Barry* and family in action:
|
||||
|
||||
{@a parent-token}
|
||||
### The *Parent* class-interface
|
||||
We [learned earlier](#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class.
|
||||
We [learned earlier](guide/cb-dependency-injection#class-interface) that a *class-interface* is an abstract class used as an interface rather than as a base class.
|
||||
|
||||
Our example defines a `Parent` *class-interface* .
|
||||
|
||||
@ -870,7 +870,7 @@ It doesn't in this example *only* to demonstrate that the code will compile and
|
||||
### A *provideParent* helper function
|
||||
|
||||
Writing variations of the same parent *alias provider* gets old quickly,
|
||||
especially this awful mouthful with a [*forwardRef*](#forwardref):
|
||||
especially this awful mouthful with a [*forwardRef*](guide/cb-dependency-injection#forwardref):
|
||||
|
||||
{@example 'cb-dependency-injection/ts/src/app/parent-finder.component.ts' region='alex-providers'}
|
||||
|
@ -2,7 +2,7 @@
|
||||
Cookbook
|
||||
|
||||
@intro
|
||||
A collection of recipes for common Angular application scenarios
|
||||
A collection of recipes for common Angular application scenarios.
|
||||
|
||||
@description
|
||||
The *Cookbook* offers answers to common implementation questions.
|
@ -13,18 +13,18 @@ Now you can download the sample code for any guide and run it locally.
|
||||
Look for the new download links next to the "live example" links.
|
||||
|
||||
## Template Syntax/Structural Directives: refreshed (2017-02-06)
|
||||
The [_Template-Syntax_](template-syntax.html) and [_Structural Directives_](structural-directives.html)
|
||||
The [_Template-Syntax_](guide/template-syntax) and [_Structural Directives_](guide/structural-directives)
|
||||
guides were significantly revised for clarity, accuracy, and current recommended practices.
|
||||
Discusses `<ng-container>`.
|
||||
Revised samples are more clear and cover all topics discussed.
|
||||
|
||||
## NEW: Samples re-structured with `src/` folder (2017-02-02)
|
||||
All documentation samples have been realigned with the default folder structure of the angular-cli.
|
||||
That's a step along the road to basing our sample in the angular-cli.
|
||||
All documentation samples have been realigned with the default folder structure of the Angular CLI.
|
||||
That's a step along the road to basing the sample in the Angular CLI.
|
||||
But it's also good in its own right.
|
||||
It helps clearly separate app code from setup and configuration files.
|
||||
|
||||
We've updated all samples with an `src/` folder at the project root.
|
||||
All samples now have a `src/` folder at the project root.
|
||||
The former `app/` folder moves under `src/`.
|
||||
Read about moving your existing project to this structure in
|
||||
<a href="https://github.com/angular/quickstart#updating-to-a-newer-version-of-the-quickstart-repo" target="_blank" target="Migrating samples/quickstart app to the src folder">
|
||||
@ -38,83 +38,83 @@ Notably:
|
||||
* Added `lite-server` configuration (`bs-config.json`) to serve `src/`.
|
||||
|
||||
## NEW: Reactive Forms guide (2017-01-31)
|
||||
The new [**Reactive Forms**](reactive-forms.html) guide explains how and why to build a "reactive form".
|
||||
The new [**Reactive Forms**](guide/reactive-forms) guide explains how and why to build a "reactive form".
|
||||
"Reactive Forms" are the code-based counterpart to the declarative "Template Driven" forms approach
|
||||
introduced in the [Forms](forms.html) guide.
|
||||
introduced in the [Forms](guide/forms) guide.
|
||||
Check it out before you decide how to add forms to your app.
|
||||
Remember also that you can use both techniques in the same app,
|
||||
choosing the approach that best fits each scenario.
|
||||
|
||||
## NEW: Deployment guide (2017-01-30)
|
||||
The new [Deployment](deployment.html) guide describes techniques for putting your application on a server.
|
||||
The new [Deployment](guide/deployment) guide describes techniques for putting your application on a server.
|
||||
It includes important advice on optimizing for production.
|
||||
|
||||
## Hierarchical Dependency Injection: refreshed (2017-01-13)
|
||||
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) guide significantly revised.
|
||||
Closes issue #3086
|
||||
Revised samples are more clear and cover all topics discussed.
|
||||
[Hierarchical Dependency Injection](guide/hierarchical-dependency-injection) guide is significantly revised.
|
||||
Closes issue #3086.
|
||||
Revised samples are clearer and cover all topics discussed.
|
||||
|
||||
## Miscellaneous (2017-01-05)
|
||||
* [Setup](setup.html) guide:
|
||||
* [Setup](guide/setup) guide:
|
||||
added (optional) instructions on how to remove _non-essential_ files.
|
||||
* No longer consolidate RxJS operator imports in `rxjs-extensions` file; each file should import what it needs.
|
||||
* All samples prepend template/style URLS URLs w/ `./` ... and so should you.
|
||||
* [Style Guide](style-guide.html): copy edits and revised rules.
|
||||
* All samples prepend template/style URLs with `./` as a best practice.
|
||||
* [Style Guide](guide/style-guide): copy edits and revised rules.
|
||||
|
||||
## Router: more detail (2016-12-21)
|
||||
Added more information to the [Router](router.html) guide
|
||||
Added more information to the [Router](guide/router) guide
|
||||
including sections named outlets, wildcard routes, and preload strategies.
|
||||
|
||||
## Http: how to set default request headers (and other request options) (2016-12-14)
|
||||
## HTTP: how to set default request headers (and other request options) (2016-12-14)
|
||||
Added section on how to set default request headers (and other request options) to
|
||||
[Http](server-communication.html#override-default-request-options) guide.
|
||||
[HTTP](guide/server-communication) guide.
|
||||
|
||||
## Testing: added component test plunkers (2016-12-02)
|
||||
Added two plunkers that each test _one simple component_ so you can write a component test plunker of your own: <live-example name="setup" plnkr="quickstart-specs">one</live-example> for the QuickStart seed's `AppComponent` and <live-example name="testing" plnkr="banner-specs">another</live-example> for the Testing guide's `BannerComponent`.
|
||||
Linked to these plunkers in [Testing](testing.html#live-examples) and [Setup anatomy](setup-systemjs-anatomy) guides.
|
||||
Linked to these plunkers in [Testing](guide/testing) and [Setup anatomy](guide/setup-systemjs-anatomy) guides.
|
||||
|
||||
## Internationalization: pluralization and _select_ (2016-11-30)
|
||||
The [Internationalization (i18n)](../cookbook/i18n.html) guide explains how to handle pluralization and
|
||||
The [Internationalization (i18n)](cookbook/i18n) guide explains how to handle pluralization and
|
||||
translation of alternative texts with `select`.
|
||||
The sample demonstrates these features too.
|
||||
|
||||
## Testing: karma file updates (2016-11-30)
|
||||
* karma.config + karma-test-shim can handle multiple spec source paths;
|
||||
see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294)
|
||||
* Displays Jasmine Runner output in the karma-launched browser
|
||||
* `karma.config` + `karma-test-shim` can handle multiple spec source paths;
|
||||
see quickstart issue: [angular/quickstart#294](https://github.com/angular/quickstart/issues/294).
|
||||
* Displays Jasmine Runner output in the karma-launched browser.
|
||||
|
||||
## QuickStart Rewrite (2016-11-18)
|
||||
The QuickStart is completely rewritten so that it actually is quick.
|
||||
It references a minimal "Hello Angular" app running in Plunker.
|
||||
The new [Setup](setup.html) page tells you how to install a local development environment
|
||||
The new [Setup](guide/setup) page tells you how to install a local development environment
|
||||
by downloading (or cloning) the QuickStart github repository.
|
||||
You are no longer asked to copy-and-paste code into setup files that were not explained anyway.
|
||||
|
||||
## Sync with Angular v.2.2.0 (2016-11-14)
|
||||
Docs and code samples updated and tested with Angular v.2.2.0
|
||||
Docs and code samples updated and tested with Angular v.2.2.0.
|
||||
|
||||
## UPDATE: NgUpgrade Guide for the AOT friendly _upgrade/static_ module (2016-11-14)
|
||||
The updated [NgUpgrade Guide](upgrade.html) guide covers the
|
||||
The updated [NgUpgrade Guide](guide/upgrade) guide covers the
|
||||
new AOT friendly `upgrade/static` module
|
||||
released in v.2.2.0, which is the recommended
|
||||
facility for migrating from AngularJS to Angular.
|
||||
The documentation for the version prior to v.2.2.0 has been removed.
|
||||
|
||||
## ES6 described in "TypeScript to JavaScript" (2016-11-14)
|
||||
The updated "[TypeScript to JavaScript](../cookbook/ts-to-js.html)" cookbook
|
||||
The updated [TypeScript to JavaScript](cookbook/ts-to-js) cookbook
|
||||
now explains how to write apps in ES6/7
|
||||
by translating the common idioms in the TypeScript documentation examples
|
||||
(and elsewhere on the web) to ES6/7 and ES5.
|
||||
|
||||
## Sync with Angular v.2.1.1 (2016-10-21)
|
||||
Docs and code samples updated and tested with Angular v.2.1.0
|
||||
Docs and code samples updated and tested with Angular v.2.1.1.
|
||||
|
||||
## npm _@types_ packages replace _typings_ (2016-10-20)
|
||||
Documentation samples now get TypeScript type information for 3rd party libraries
|
||||
from npm `@types` packages rather than with the _typings_ tooling.
|
||||
The `typings.json` file is gone.
|
||||
|
||||
The "[AngularJS Upgrade](upgrade.html)" guide reflects this change.
|
||||
The [AngularJS Upgrade](guide/upgrade) guide reflects this change.
|
||||
The `package.json` installs `@types/angular` and several `@types/angular-...`
|
||||
packages in support of upgrade; these are not needed for pure Angular development.
|
||||
|
||||
@ -135,29 +135,29 @@ _before_ the user navigates to them for improved perceived performance.
|
||||
New `:enter` and `:leave` aliases make animation more natural.
|
||||
|
||||
## Sync with Angular v.2.1.0 (2016-10-12)
|
||||
Docs and code samples updated and tested with Angular v.2.1.0
|
||||
Docs and code samples updated and tested with Angular v.2.1.0.
|
||||
|
||||
## NEW "Ahead of time (AOT) Compilation" cookbook (2016-10-11)
|
||||
The NEW [Ahead of time (AOT) Compilation](../cookbook/aot-compiler.html) cookbook
|
||||
The NEW [Ahead of time (AOT) Compilation](cookbook/aot-compiler) cookbook
|
||||
explains what AOT compilation is and why you'd want it.
|
||||
It demonstrates the basics with a QuickStart app
|
||||
followed by the more advanced considerations of compiling and bundling the Tour of Heroes.
|
||||
|
||||
## Sync with Angular v.2.0.2 (2016-10-6)
|
||||
Docs and code samples updated and tested with Angular v.2.0.2
|
||||
Docs and code samples updated and tested with Angular v.2.0.2.
|
||||
|
||||
## "Routing and Navigation" guide with the _Router Module_ (2016-10-5)
|
||||
The [Routing and Navigation](router.html) guide now locates route configuration
|
||||
The [Routing and Navigation](guide/router) guide now locates route configuration
|
||||
in a _Routing Module_.
|
||||
The _Routing Module_ replaces the previous _routing object_ involving the `ModuleWithProviders`.
|
||||
|
||||
All guided samples with routing use the _Routing Module_ and prose content has been updated,
|
||||
most conspicuously in the
|
||||
[NgModule](ngmodule.html) guide and [NgModule FAQ](../cookbook/ngmodule-faq.html) cookbook.
|
||||
[NgModule](guide/ngmodule) guide and [NgModule FAQ](cookbook/ngmodule-faq) cookbook.
|
||||
|
||||
## New "Internationalization" Cookbook (2016-09-30)
|
||||
|
||||
Added a new [Internationalization (i18n)](../cookbook/i18n.html) cookbook that shows how
|
||||
Added a new [Internationalization (i18n)](cookbook/i18n) cookbook that shows how
|
||||
to use Angular "i18n" facilities to translate template text into multiple languages.
|
||||
|
||||
## "angular-in-memory-web-api" package rename (2016-09-27)
|
||||
@ -171,21 +171,21 @@ The new "angular-in-memory-web-api" has new features.
|
||||
|
||||
## "Style Guide" with _NgModules_ (2016-09-27)
|
||||
|
||||
[StyleGuide](style-guide.html) explains our recommended conventions for Angular modules (NgModule).
|
||||
[StyleGuide](guide/style-guide) explains recommended conventions for Angular modules (NgModule).
|
||||
Barrels now are far less useful and have been removed from the style guide;
|
||||
they remain valuable but are not a matter of Angular style.
|
||||
We also relaxed the rule that discouraged use of the `@Component.host` property.
|
||||
Also relaxed the rule that discouraged use of the `@Component.host` property.
|
||||
|
||||
## _moduleId: module.id_ everywhere (2016-09-25)
|
||||
|
||||
Sample components that get their templates or styles with `templateUrl` or `styleUrls`
|
||||
have been converted to _module-relative_ URLs.
|
||||
We added the `moduleId: module.id` property-and-value to their `@Component` metadata.
|
||||
Added the `moduleId: module.id` property-and-value to their `@Component` metadata.
|
||||
|
||||
This change is a requirement for compilation with AOT compiler when the app loads
|
||||
modules with SystemJS as the samples currently do.
|
||||
|
||||
## "Lifecycle Hooks" guide simplified (2016-09-24)
|
||||
|
||||
The [Lifecycle Hooks](lifecycle-hooks.html) guide is shorter, simpler, and
|
||||
The [Lifecycle Hooks](guide/lifecycle-hooks) guide is shorter, simpler, and
|
||||
draws more attention to the order in which Angular calls the hooks.
|
@ -2,32 +2,32 @@
|
||||
Component Interaction
|
||||
|
||||
@intro
|
||||
Share information between different directives and components
|
||||
Share information between different directives and components.
|
||||
|
||||
@description
|
||||
<a id="top"></a>This cookbook contains recipes for common component communication scenarios
|
||||
in which two or more components share information.
|
||||
<a id="toc"></a>## Table of contents
|
||||
|
||||
[Pass data from parent to child with input binding](#parent-to-child)
|
||||
[Pass data from parent to child with input binding](guide/component-communication#parent-to-child)
|
||||
|
||||
[Intercept input property changes with a setter](#parent-to-child-setter)
|
||||
[Intercept input property changes with a setter](guide/component-communication#parent-to-child-setter)
|
||||
|
||||
[Intercept input property changes with *ngOnChanges*](#parent-to-child-on-changes)
|
||||
[Intercept input property changes with *ngOnChanges*](guide/component-communication#parent-to-child-on-changes)
|
||||
|
||||
[Parent listens for child event](#child-to-parent)
|
||||
[Parent listens for child event](guide/component-communication#child-to-parent)
|
||||
|
||||
[Parent interacts with child via a *local variable*](#parent-to-child-local-var)
|
||||
[Parent interacts with child via a *local variable*](guide/component-communication#parent-to-child-local-var)
|
||||
|
||||
[Parent calls a *ViewChild*](#parent-to-view-child)
|
||||
[Parent calls a *ViewChild*](guide/component-communication#parent-to-view-child)
|
||||
|
||||
[Parent and children communicate via a service](#bidirectional-service)
|
||||
[Parent and children communicate via a service](guide/component-communication#bidirectional-service)
|
||||
**See the <live-example name="cb-component-communication"></live-example>**.
|
||||
|
||||
<a id="parent-to-child"></a>## Pass data from parent to child with input binding
|
||||
|
||||
`HeroChildComponent` has two ***input properties***,
|
||||
typically adorned with [@Input decorations](../guide/template-syntax.html#inputs-outputs).
|
||||
typically adorned with [@Input decorations](guide/template-syntax).
|
||||
|
||||
|
||||
{@example 'cb-component-communication/ts/src/app/hero-child.component.ts'}
|
||||
@ -54,7 +54,7 @@ E2E test that all children were instantiated and displayed as expected:
|
||||
|
||||
{@example 'cb-component-communication/e2e-spec.ts' region='parent-to-child'}
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/component-communication#top)
|
||||
|
||||
<a id="parent-to-child-setter"></a>## Intercept input property changes with a setter
|
||||
|
||||
@ -83,14 +83,14 @@ E2E tests of input property setter with empty and non-empty names:
|
||||
|
||||
{@example 'cb-component-communication/e2e-spec.ts' region='parent-to-child-setter'}
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/component-communication#top)
|
||||
|
||||
<a id="parent-to-child-on-changes"></a>## Intercept input property changes with *ngOnChanges*
|
||||
|
||||
Detect and act upon changes to input property values with the `ngOnChanges` method of the `OnChanges` lifecycle hook interface.
|
||||
May prefer this approach to the property setter when watching multiple, interacting input properties.
|
||||
|
||||
Learn about `ngOnChanges` in the [LifeCycle Hooks](../guide/lifecycle-hooks.html) chapter.This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
|
||||
Learn about `ngOnChanges` in the [LifeCycle Hooks](guide/lifecycle-hooks) chapter.This `VersionChildComponent` detects changes to the `major` and `minor` input properties and composes a log message reporting these changes:
|
||||
|
||||
|
||||
{@example 'cb-component-communication/ts/src/app/version-child.component.ts'}
|
||||
@ -114,15 +114,15 @@ the expected `ngOnChanges` calls and values:
|
||||
|
||||
{@example 'cb-component-communication/e2e-spec.ts' region='parent-to-child-onchanges'}
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/component-communication#top)
|
||||
|
||||
<a id="child-to-parent"></a>## Parent listens for child event
|
||||
|
||||
The child component exposes an `EventEmitter` property with which it `emits`events when something happens.
|
||||
The child component exposes an `EventEmitter` property with which it `emits` events when something happens.
|
||||
The parent binds to that event property and reacts to those events.
|
||||
|
||||
The child's `EventEmitter` property is an ***output property***,
|
||||
typically adorned with an [@Output decoration](../guide/template-syntax.html#inputs-outputs)
|
||||
typically adorned with an [@Output decoration](guide/template-syntax)
|
||||
as seen in this `VoterComponent`:
|
||||
|
||||
|
||||
@ -150,7 +150,7 @@ Test that clicking the *Agree* and *Disagree* buttons update the appropriate cou
|
||||
|
||||
{@example 'cb-component-communication/e2e-spec.ts' region='child-to-parent'}
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/component-communication#top)
|
||||
|
||||
## Parent interacts with child via *local variable*
|
||||
|
||||
@ -200,7 +200,7 @@ Test also that clicking the *Stop* button pauses the countdown timer:
|
||||
|
||||
{@example 'cb-component-communication/e2e-spec.ts' region='countdown-timer-tests'}
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/component-communication#top)
|
||||
|
||||
<a id="parent-to-view-child"></a>## Parent calls a *ViewChild*
|
||||
|
||||
@ -214,9 +214,9 @@ must read or write child component values or must call child component methods.
|
||||
When the parent component *class* requires that kind of access,
|
||||
we ***inject*** the child component into the parent as a *ViewChild*.
|
||||
|
||||
We'll illustrate this technique with the same [Countdown Timer](#countdown-timer-example) example.
|
||||
We'll illustrate this technique with the same [Countdown Timer](guide/component-communication#countdown-timer-example) example.
|
||||
We won't change its appearance or behavior.
|
||||
The child [CountdownTimerComponent](#countdown-timer-example) is the same as well.
|
||||
The child [CountdownTimerComponent](guide/component-communication#countdown-timer-example) is the same as well.
|
||||
We are switching from the *local variable* to the *ViewChild* technique
|
||||
solely for the purpose of demonstration.Here is the parent, `CountdownViewChildParentComponent`:
|
||||
|
||||
@ -248,7 +248,7 @@ We use `setTimeout` to wait one tick and then revise the `seconds` method so
|
||||
that it takes future values from the timer component.
|
||||
|
||||
### Test it
|
||||
Use [the same countdown timer tests](#countdown-tests) as before.[Back to top](#top)
|
||||
Use [the same countdown timer tests](guide/component-communication#countdown-tests) as before.[Back to top](guide/component-communication#top)
|
||||
|
||||
<a id="bidirectional-service"></a>## Parent and children communicate via a service
|
||||
|
||||
@ -298,4 +298,4 @@ and verify that the *History* meets expectations:
|
||||
|
||||
{@example 'cb-component-communication/e2e-spec.ts' region='bidirectional-service'}
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/component-communication#top)
|
@ -33,7 +33,7 @@ It would be much easier to write and maintain our application components if we c
|
||||
|
||||
We can if we build our application as `commonjs` modules and load those modules
|
||||
with a suitable package loader such as `systemjs` or `webpack`.
|
||||
Learn why [below](#why-default).
|
||||
Learn why [below](guide/component-relative-paths#why-default).
|
||||
|
||||
The Angular CLI uses these technologies and defaults to the
|
||||
*component-relative path* approach described here.
|
||||
@ -98,7 +98,7 @@ The result looks like this:
|
||||
|
||||
~~~ {.alert.is-helpful}
|
||||
|
||||
Webpack users may prefer [an alternative approach](#webpack).
|
||||
Webpack users may prefer [an alternative approach](guide/component-relative-paths#webpack).
|
||||
|
||||
|
||||
~~~
|
||||
@ -189,6 +189,6 @@ properties that reference *component-relative URLS.
|
||||
{@example 'webpack/ts/src/app/app.component.ts'}
|
||||
|
||||
|
||||
Webpack will do a `require` behind the scenes to load the templates and styles. Read more [here](../guide/webpack.html#highlights).
|
||||
Webpack will do a `require` behind the scenes to load the templates and styles. Read more [here](guide/webpack).
|
||||
|
||||
See the [Introduction to Webpack](../guide/webpack.html).
|
||||
See the [Introduction to Webpack](guide/webpack).
|
@ -16,12 +16,12 @@ This page describes how to load and apply these component styles.
|
||||
|
||||
## Table Of Contents
|
||||
|
||||
* [Using component styles](#using-component-styles)
|
||||
* [Special selectors](#special-selectors)
|
||||
* [Loading styles into components](#loading-styles)
|
||||
* [Controlling view encapsulation: native, emulated, and none](#view-encapsulation)
|
||||
* [Appendix 1: Inspecting the CSS generated in emulated view encapsulation](#inspect-generated-css)
|
||||
* [Appendix 2: Loading styles with relative URLs](#relative-urls)
|
||||
* [Using component styles](guide/component-styles#using-component-styles)
|
||||
* [Special selectors](guide/component-styles#special-selectors)
|
||||
* [Loading styles into components](guide/component-styles#loading-styles)
|
||||
* [Controlling view encapsulation: native, emulated, and none](guide/component-styles#view-encapsulation)
|
||||
* [Appendix 1: Inspecting the CSS generated in emulated view encapsulation](guide/component-styles#inspect-generated-css)
|
||||
* [Appendix 2: Loading styles with relative URLs](guide/component-styles#relative-urls)
|
||||
|
||||
You can run the <live-example></live-example> in Plunker and download the code from there.
|
||||
|
||||
@ -120,7 +120,7 @@ The `/deep/` selector also has the alias `>>>`. You can use either interchangeab
|
||||
|
||||
Use the `/deep/` and `>>>` selectors only with *emulated* view encapsulation.
|
||||
Emulated is the default and most commonly used view encapsulation. For more information, see the
|
||||
[Controlling view encapsulation](#view-encapsulation) section.
|
||||
[Controlling view encapsulation](guide/component-styles#view-encapsulation) section.
|
||||
|
||||
|
||||
~~~
|
||||
@ -201,7 +201,7 @@ Choose from the following modes:
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
For details, see [Appendix 1](#inspect-generated-css).
|
||||
For details, see [Appendix 1](guide/component-styles#inspect-generated-css).
|
||||
* `None` means that Angular does no view encapsulation.
|
||||
Angular adds the CSS to the global styles.
|
||||
The scoping rules, isolations, and protections discussed earlier don't apply.
|
||||
|
@ -11,13 +11,13 @@ we really can't build an Angular application without it.
|
||||
It's used so widely that almost everyone just calls it _DI_.
|
||||
|
||||
In this chapter we'll learn what DI is and why we want it.
|
||||
Then we'll learn [how to use it](#angular-di) in an Angular app.
|
||||
Then we'll learn [how to use it](guide/dependency-injection#angular-di) in an Angular app.
|
||||
|
||||
- [Why dependency injection?](#why-dependency-injection)
|
||||
- [Angular dependency injection](#angular-dependency-injection)
|
||||
- [Injector providers](#injector-providers)
|
||||
- [Dependency injection tokens](#dependency-injection-tokens)
|
||||
- [Summary](#summary)
|
||||
- [Why dependency injection?](guide/dependency-injection#why-dependency-injection)
|
||||
- [Angular dependency injection](guide/dependency-injection#angular-dependency-injection)
|
||||
- [Injector providers](guide/dependency-injection#injector-providers)
|
||||
- [Dependency injection tokens](guide/dependency-injection#dependency-injection-tokens)
|
||||
- [Summary](guide/dependency-injection#summary)
|
||||
|
||||
Run the <live-example></live-example>.
|
||||
|
||||
@ -170,7 +170,7 @@ That sounds nice. What does it do for us when building components in Angular?
|
||||
Let's see, one step at a time.
|
||||
|
||||
We'll begin with a simplified version of the `HeroesComponent`
|
||||
that we built in the [The Tour of Heroes](../tutorial/).
|
||||
that we built in the [The Tour of Heroes](tutorial/).
|
||||
|
||||
<md-tab-group>
|
||||
|
||||
@ -219,7 +219,7 @@ write the service code in its own file.
|
||||
Our `HeroService` exposes a `getHeroes` method that returns
|
||||
the same mock data as before, but none of its consumers need to know that.
|
||||
Notice the `@Injectable()` #{_decorator} above the service class.
|
||||
We'll discuss its purpose [shortly](#injectable).
|
||||
We'll discuss its purpose [shortly](guide/dependency-injection#injectable).
|
||||
|
||||
We aren't even pretending this is a real service.
|
||||
If we were actually getting data from a remote server, the API would have to be
|
||||
@ -239,7 +239,7 @@ We don't have to create an Angular injector.
|
||||
Angular creates an application-wide injector for us during the bootstrap process.
|
||||
We do have to configure the injector by registering the **providers**
|
||||
that create the services our application requires.
|
||||
We'll explain what [providers](#providers) are later in this chapter.
|
||||
We'll explain what [providers](guide/dependency-injection#providers) are later in this chapter.
|
||||
### Registering providers in a component
|
||||
|
||||
Here's a revised `HeroesComponent` that registers the `HeroService`.
|
||||
@ -251,7 +251,7 @@ Here's a revised `HeroesComponent` that registers the `HeroService`.
|
||||
|
||||
The `HeroListComponent` should get heroes from the injected `HeroService`.
|
||||
Per the dependency injection pattern, the component must ask for the service in its
|
||||
constructor, [as we explained earlier](#ctor-injection).
|
||||
constructor, [as we explained earlier](guide/dependency-injection#ctor-injection).
|
||||
It's a small change:
|
||||
|
||||
<md-tab-group>
|
||||
@ -301,10 +301,10 @@ would be explicitly created:
|
||||
{@example 'dependency-injection/ts/src/app/car/car-injector.ts' region='injector-create-and-call'}
|
||||
|
||||
We won't find code like that in the Tour of Heroes or any of our other samples.
|
||||
We *could* write code that [explicitly creates an injector](#explicit-injector) if we *had* to, but we rarely do.
|
||||
We *could* write code that [explicitly creates an injector](guide/dependency-injection#explicit-injector) if we *had* to, but we rarely do.
|
||||
Angular takes care of creating and calling injectors
|
||||
when it creates components for us — whether through HTML markup, as in `<hero-list></hero-list>`,
|
||||
or after navigating to a component with the [router](./router.html).
|
||||
or after navigating to a component with the [router](guide/router).
|
||||
If we let Angular do its job, we'll enjoy the benefits of automated dependency injection.
|
||||
### Singleton services
|
||||
|
||||
@ -314,7 +314,7 @@ In our example, a single `HeroService` instance is shared among the
|
||||
|
||||
However, Angular DI is an hierarchical injection
|
||||
system, which means that nested injectors can create their own service instances.
|
||||
Learn more about that in the [Hierarchical Injectors](./hierarchical-dependency-injection.html) chapter.
|
||||
Learn more about that in the [Hierarchical Injectors](guide/hierarchical-dependency-injection) chapter.
|
||||
### Testing the component
|
||||
|
||||
We emphasized earlier that designing a class for dependency injection makes the class easier to test.
|
||||
@ -327,7 +327,7 @@ under test:
|
||||
{@example 'dependency-injection/ts/src/app/test.component.ts' region='spec'}
|
||||
|
||||
|
||||
Learn more in [Testing](./testing.html).
|
||||
Learn more in [Testing](guide/testing).
|
||||
### When the service needs a service
|
||||
|
||||
Our `HeroService` is very simple. It doesn't have any dependencies of its own.
|
||||
@ -453,7 +453,7 @@ We wrote the `providers` #{_array} like this:
|
||||
|
||||
{@example 'dependency-injection/ts/src/app/providers.component.ts' region='providers-3'}
|
||||
|
||||
The first is the [token](#token) that serves as the key for both locating a dependency value
|
||||
The first is the [token](guide/dependency-injection#token) that serves as the key for both locating a dependency value
|
||||
and registering the provider.
|
||||
|
||||
The second is a !{_secondParam},
|
||||
@ -528,8 +528,8 @@ which makes this object play the logger role.
|
||||
{@example 'dependency-injection/ts/src/app/providers.component.ts' region='providers-7'}
|
||||
|
||||
See more `useValue` examples in the
|
||||
[Non-class dependencies](#non-class-dependencies) and
|
||||
[OpaqueToken](#opaquetoken) sections.
|
||||
[Non-class dependencies](guide/dependency-injection#non-class-dependencies) and
|
||||
[OpaqueToken](guide/dependency-injection#opaquetoken) sections.
|
||||
|
||||
<div id='factory-provider'>
|
||||
|
||||
@ -583,7 +583,7 @@ We inject both the `Logger` and the `UserService` into the factory provider and
|
||||
The `useFactory` field tells Angular that the provider is a factory function
|
||||
whose implementation is the `heroServiceFactory`.
|
||||
|
||||
The `deps` property is #{_an} #{_array} of [provider tokens](#token).
|
||||
The `deps` property is #{_an} #{_array} of [provider tokens](guide/dependency-injection#token).
|
||||
The `Logger` and `UserService` classes serve as tokens for their own class providers.
|
||||
The injector resolves these tokens and injects the corresponding services into the matching factory function parameters.
|
||||
Notice that we captured the factory provider in #{_an} #{exportedvar}, `heroServiceProvider`.
|
||||
@ -647,7 +647,7 @@ This is especially convenient when we consider that most dependency values are p
|
||||
{@example 'dependency-injection/ts/src/app/app.config.ts' region='config'}
|
||||
|
||||
We'd like to make this configuration object available for injection.
|
||||
We know we can register an object with a [value provider](#value-provider).
|
||||
We know we can register an object with a [value provider](guide/dependency-injection#value-provider).
|
||||
### OpaqueToken
|
||||
|
||||
One solution to choosing a provider token for non-class dependencies is
|
||||
@ -700,7 +700,7 @@ adding a parameter to a constructor.
|
||||
Angular dependency injection is more capable than we've described.
|
||||
We can learn more about its advanced features, beginning with its support for
|
||||
nested injectors, in the
|
||||
[Hierarchical Dependency Injection](hierarchical-dependency-injection.html) chapter.
|
||||
[Hierarchical Dependency Injection](guide/hierarchical-dependency-injection) chapter.
|
||||
|
||||
## Appendix: Working with injectors directly
|
||||
|
||||
|
@ -10,21 +10,21 @@ This page describes tools and techniques for deploy and optimize your Angular ap
|
||||
|
||||
{@a toc}
|
||||
## Table of contents
|
||||
* [Overview](#overview)
|
||||
* [Simplest deployment possible](#dev-deploy)
|
||||
* [Optimize for production](#optimize)
|
||||
* [Ahead-of-Time (AOT) compilation](#aot)
|
||||
* [Webpack](#webpack)
|
||||
* [Tree shaking with _rollup_](#rollup)
|
||||
* [Pruned libraries](#prune)
|
||||
* [Measure performance first](#measure)
|
||||
* [Angular configuration](#angular-configuration)
|
||||
* [The `base` tag](#base-tag)
|
||||
* [Enable production mode](#enable-prod-mode)
|
||||
* [Lazy loading](#lazy-loading)
|
||||
* [Server configuration](#server-configuration)
|
||||
* [Routed apps must fallback to `index.html`](#fallback)
|
||||
* [CORS: requesting services from a different server](#cors)
|
||||
* [Overview](guide/deployment#overview)
|
||||
* [Simplest deployment possible](guide/deployment#dev-deploy)
|
||||
* [Optimize for production](guide/deployment#optimize)
|
||||
* [Ahead-of-Time (AOT) compilation](guide/deployment#aot)
|
||||
* [Webpack](guide/deployment#webpack)
|
||||
* [Tree shaking with _rollup_](guide/deployment#rollup)
|
||||
* [Pruned libraries](guide/deployment#prune)
|
||||
* [Measure performance first](guide/deployment#measure)
|
||||
* [Angular configuration](guide/deployment#angular-configuration)
|
||||
* [The `base` tag](guide/deployment#base-tag)
|
||||
* [Enable production mode](guide/deployment#enable-prod-mode)
|
||||
* [Lazy loading](guide/deployment#lazy-loading)
|
||||
* [Server configuration](guide/deployment#server-configuration)
|
||||
* [Routed apps must fallback to `index.html`](guide/deployment#fallback)
|
||||
* [CORS: requesting services from a different server](guide/deployment#cors)
|
||||
|
||||
|
||||
{@a overview}
|
||||
@ -34,20 +34,20 @@ This page describes tools and techniques for deploy and optimize your Angular ap
|
||||
This guide describes techniques for preparing and deploying an Angular application to a server running remotely.
|
||||
The techniques progress from _easy but suboptimal_ to _more optimal and more involved_.
|
||||
|
||||
* The [simple way](#dev-deploy "Simplest deployment possible") is to copy the development environment to the server.
|
||||
* The [simple way](guide/deployment#dev-deploy "Simplest deployment possible") is to copy the development environment to the server.
|
||||
|
||||
* [_Ahead of Time_ compilation (AOT)](#aot "AOT Compilation") is the first of
|
||||
[several optimization strategies](#optimize).
|
||||
You'll also want to read the [detailed instructions in the AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook").
|
||||
* [_Ahead of Time_ compilation (AOT)](guide/deployment#aot "AOT Compilation") is the first of
|
||||
[several optimization strategies](guide/deployment#optimize).
|
||||
You'll also want to read the [detailed instructions in the AOT Cookbook](cookbook/aot-compiler).
|
||||
|
||||
* [Webpack](#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT.
|
||||
The Angular [webpack guide](webpack.html "Webpack: an introduction") can get you started and
|
||||
* [Webpack](guide/deployment#webpack "Webpack Optimization") is a popular general purpose packaging tool with a rich ecosystem, including plugins for AOT.
|
||||
The Angular [webpack guide](guide/webpack) can get you started and
|
||||
_this_ page provides additional optimization advice, but you'll probably have to learn more about webpack on your own.
|
||||
|
||||
* The [Angular configuration](#angular-configuration "Angular configuration") section calls attention to
|
||||
* The [Angular configuration](guide/deployment#angular-configuration "Angular configuration") section calls attention to
|
||||
specific client application changes that could improve performance.
|
||||
|
||||
* The [Server configuration](#server-configuration "Server configuration") section describes
|
||||
* The [Server configuration](guide/deployment#server-configuration "Server configuration") section describes
|
||||
server-side changes that may be necessary, _no matter how you deploy the application_.
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ directly out of the development environment.
|
||||
It's already running locally. You'll just copy it, almost _as is_,
|
||||
to a non-local server that others can reach.
|
||||
|
||||
1. Copy _everything_ (or [_almost_ everything](#node-modules "Loading npm packages from the web"))
|
||||
1. Copy _everything_ (or [_almost_ everything](guide/deployment#node-modules "Loading npm packages from the web"))
|
||||
from the local project folder to a folder on the server.
|
||||
|
||||
1. If you're serving the app out of a subfolder,
|
||||
@ -69,12 +69,12 @@ edit a version of `index.html` to set the `<base href>` appropriately.
|
||||
For example, if the URL to `index.html` is `www.mysite.com/my/app/`, set the _base href_ to
|
||||
`<base href="/my/app/">`.
|
||||
Otherwise, leave it alone.
|
||||
[More on this below](#base-tag).
|
||||
[More on this below](guide/deployment#base-tag).
|
||||
|
||||
1. Configure the server to redirect requests for missing files to `index.html`.
|
||||
[More on this below](#fallback).
|
||||
[More on this below](guide/deployment#fallback).
|
||||
|
||||
1. Enable production mode as [described below](#enable-prod-mode) (optional).
|
||||
1. Enable production mode as [described below](guide/deployment#enable-prod-mode) (optional).
|
||||
|
||||
That's the simplest deployment you can do.
|
||||
|
||||
@ -83,7 +83,7 @@ That's the simplest deployment you can do.
|
||||
|
||||
This is _not_ a production deployment. It's not optimized and it won't be fast for users.
|
||||
It might be good enough for sharing your progress and ideas internally with managers, teammates, and other stakeholders.
|
||||
Be sure to read about [optimizing for production](#optimize "Optimizing for production") below.
|
||||
Be sure to read about [optimizing for production](guide/deployment#optimize "Optimizing for production") below.
|
||||
|
||||
|
||||
|
||||
@ -184,7 +184,7 @@ The following trivial router sample app shows these changes.
|
||||
|
||||
Practice with this sample before attempting these techniques on your application.
|
||||
|
||||
1. Follow the [setup instructions](../guide/setup.html "Angular QuickStart setup") for creating a new project
|
||||
1. Follow the [setup instructions](guide/setup) for creating a new project
|
||||
named <ngio-ex path="simple-deployment"></ngio-ex>.
|
||||
|
||||
1. Add the "Simple deployment" sample files shown above.
|
||||
@ -254,9 +254,9 @@ Apps compiled with AOT launch faster for several reasons.
|
||||
* You don't download the Angular compiler, which is pretty big on its own.
|
||||
* The compiler discards unused Angular directives that a tree-shaking tool can then exclude.
|
||||
|
||||
Learn more about AOT Compilation in the [AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook")
|
||||
Learn more about AOT Compilation in the [AOT Cookbook](cookbook/aot-compiler)
|
||||
which describes running the AOT compiler from the command line
|
||||
and using [_rollup_](#rollup) for bundling, minification, uglification and tree shaking.
|
||||
and using [_rollup_](guide/deployment#rollup) for bundling, minification, uglification and tree shaking.
|
||||
|
||||
|
||||
{@a webpack}
|
||||
@ -264,7 +264,7 @@ and using [_rollup_](#rollup) for bundling, minification, uglification and tree
|
||||
|
||||
<a href="https://webpack.js.org/" target="_blank" title="Webpack 2">Webpack 2</a> is another
|
||||
great option for inlining templates and style-sheets, for bundling, minifying, and uglifying the application.
|
||||
The "[Webpack: an introduction](webpack.html "Webpack: an introduction")" guide will get you started
|
||||
The "[Webpack: an introduction](guide/webpack)" guide will get you started
|
||||
using webpack with Angular.
|
||||
|
||||
Consider configuring _Webpack_ with the official
|
||||
@ -338,7 +338,7 @@ For example, given the `<base href="/my/app/">`, the browser resolves a URL such
|
||||
into a server request for `my/app/some/place/foo.jpg`.
|
||||
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
|
||||
|
||||
See also the [*APP_BASE_HREF*](../api/common/index/APP_BASE_HREF-let.html "API: APP_BASE_HREF") alternative.In development, you typically start the server in the folder that holds `index.html`.
|
||||
See also the [*APP_BASE_HREF*](api/common/index/APP_BASE_HREF-let) alternative.In development, you typically start the server in the folder that holds `index.html`.
|
||||
That's the root folder and you'd add `<base href="/">` near the top of `index.html` because `/` is the root of the app.
|
||||
|
||||
But on the shared or production server, you might serve the app from a subfolder.
|
||||
@ -361,7 +361,7 @@ console:
|
||||
|
||||
Switching to production mode can make it run faster by disabling development specific checks such as the dual change detection cycles.
|
||||
|
||||
To enable [production mode](../api/core/index/enableProdMode-function.html) when running remotely, add the following code to the `main.ts`.
|
||||
To enable [production mode](api/core/index/enableProdMode-function) when running remotely, add the following code to the `main.ts`.
|
||||
|
||||
|
||||
{@example 'deployment/ts/src/main.ts' region='enableProdMode'}
|
||||
@ -375,8 +375,8 @@ You can dramatically reduce launch time by only loading the application modules
|
||||
absolutely must be present when the app starts.
|
||||
|
||||
Configure the Angular Router to defer loading of all other modules (and their associated code), either by
|
||||
[waiting until the app has launched](router.html#preloading "Preloading")
|
||||
or by [_lazy loading_](router.html#asynchronous-routing "Lazy loading")
|
||||
[waiting until the app has launched](guide/router)
|
||||
or by [_lazy loading_](guide/router)
|
||||
them on demand.
|
||||
|
||||
#### Don't eagerly import something from a lazy loaded module
|
||||
@ -545,5 +545,5 @@ Read about how to enable CORS for specific servers at
|
||||
{@a next-steps}
|
||||
|
||||
## Next steps
|
||||
If you want to go beyond the [simple _copy-deploy_](#dev-deploy "Simplest deployment possible") approach,
|
||||
read the [AOT Cookbook](../cookbook/aot-compiler.html "AOT Cookbook") next.
|
||||
If you want to go beyond the [simple _copy-deploy_](guide/deployment#dev-deploy "Simplest deployment possible") approach,
|
||||
read the [AOT Cookbook](cookbook/aot-compiler) next.
|
@ -19,9 +19,9 @@ The final UI looks like this:
|
||||
|
||||
# Contents
|
||||
|
||||
* [Showing component properties with interpolation](#interpolation).
|
||||
* [Showing !{_an} !{_array} property with NgFor](#ngFor).
|
||||
* [Conditional display with NgIf](#ngIf).
|
||||
* [Showing component properties with interpolation](guide/displaying-data#interpolation).
|
||||
* [Showing !{_an} !{_array} property with NgFor](guide/displaying-data#ngFor).
|
||||
* [Conditional display with NgIf](guide/displaying-data#ngIf).
|
||||
|
||||
The <live-example></live-example> demonstrates all of the syntax and code
|
||||
snippets described in this page.
|
||||
@ -31,7 +31,7 @@ The easiest way to display a component property
|
||||
is to bind the property name through interpolation.
|
||||
With interpolation, you put the property name in the view template, enclosed in double curly braces: `{{myHero}}`.
|
||||
|
||||
Follow the [setup](setup.html) instructions for creating a new project
|
||||
Follow the [setup](guide/setup) instructions for creating a new project
|
||||
named <ngio-ex path="displaying-data"></ngio-ex>.
|
||||
|
||||
Then modify the <ngio-ex path="app.component.ts"></ngio-ex> file by
|
||||
@ -93,22 +93,22 @@ It marks that `<li>` element (and its children) as the "repeater template":
|
||||
~~~ {.alert.is-important}
|
||||
|
||||
Don't forget the leading asterisk (\*) in `*ngFor`. It is an essential part of the syntax.
|
||||
For more information, see the [Template Syntax](./template-syntax.html#ngFor) page.
|
||||
For more information, see the [Template Syntax](guide/template-syntax) page.
|
||||
|
||||
|
||||
~~~
|
||||
|
||||
Notice the `hero` in the `ngFor` double-quoted instruction;
|
||||
it is an example of a template input variable. Read
|
||||
more about template input variables in the [microsyntax](./template-syntax.html#ngForMicrosyntax) section of
|
||||
the [Template Syntax](./template-syntax.html) page.
|
||||
more about template input variables in the [microsyntax](guide/template-syntax) section of
|
||||
the [Template Syntax](guide/template-syntax) page.
|
||||
|
||||
Angular duplicates the `<li>` for each item in the list, setting the `hero` variable
|
||||
to the item (the hero) in the current iteration. Angular uses that variable as the
|
||||
context for the interpolation in the double curly braces.
|
||||
|
||||
In this case, `ngFor` is displaying !{_an} !{_array}, but `ngFor` can
|
||||
repeat items for any [iterable](!{_iterableUrl}) object.Now the heroes appear in an unordered list.
|
||||
repeat items for any [iterable](guide/!{_iterableUrl}) object.Now the heroes appear in an unordered list.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/devguide/displaying-data/hero-names-list.png" alt="After ngfor"> </img>
|
||||
@ -150,7 +150,7 @@ To see it in action, add the following paragraph at the bottom of the template:
|
||||
~~~ {.alert.is-important}
|
||||
|
||||
Don't forget the leading asterisk (\*) in `*ngIf`. It is an essential part of the syntax.
|
||||
Read more about `ngIf` and `*` in the [ngIf section](./template-syntax.html#ngIf) of the [Template Syntax](./template-syntax.html) page.
|
||||
Read more about `ngIf` and `*` in the [ngIf section](guide/template-syntax) of the [Template Syntax](guide/template-syntax) page.
|
||||
|
||||
|
||||
~~~
|
||||
@ -160,8 +160,8 @@ The template expression inside the double quotes,
|
||||
When the component's list of heroes has more than three items, Angular adds the paragraph
|
||||
to the DOM and the message appears. If there are three or fewer items, Angular omits the
|
||||
paragraph, so no message appears. For more information,
|
||||
see the [template expressions](./template-syntax.html#template-expressions) section of the
|
||||
[Template Syntax](./template-syntax.html) page.
|
||||
see the [template expressions](guide/template-syntax) section of the
|
||||
[Template Syntax](guide/template-syntax) page.
|
||||
|
||||
|
||||
~~~ {.alert.is-helpful}
|
||||
|
@ -2,7 +2,7 @@
|
||||
Dynamic Component Loader
|
||||
|
||||
@intro
|
||||
Load components dynamically
|
||||
Load components dynamically.
|
||||
|
||||
@description
|
||||
Component templates are not always fixed. An application may need to load new components at runtime.
|
||||
@ -11,11 +11,11 @@ In this cookbook we show how to use `ComponentFactoryResolver` to add components
|
||||
|
||||
<a id="toc"></a>## Table of contents
|
||||
|
||||
[Dynamic Component Loading](#dynamic-loading)
|
||||
[Dynamic Component Loading](guide/dynamic-component-loader#dynamic-loading)
|
||||
|
||||
[Where to load the component](#where-to-load)
|
||||
[Where to load the component](guide/dynamic-component-loader#where-to-load)
|
||||
|
||||
[Loading components](#loading-components)
|
||||
[Loading components](guide/dynamic-component-loader#loading-components)
|
||||
|
||||
<a id="dynamic-loading"></a>## Dynamic Component Loading
|
||||
|
@ -2,7 +2,7 @@
|
||||
Dynamic Forms
|
||||
|
||||
@intro
|
||||
Render dynamic forms with FormGroup
|
||||
Render dynamic forms with FormGroup.
|
||||
|
||||
@description
|
||||
We can't always justify the cost and time to build handcrafted forms,
|
||||
@ -21,15 +21,15 @@ The agency is constantly tinkering with the application process.
|
||||
We can create the forms on the fly *without changing our application code*.
|
||||
<a id="toc"></a>## Table of contents
|
||||
|
||||
[Bootstrap](#bootstrap)
|
||||
[Bootstrap](guide/dynamic-form#bootstrap)
|
||||
|
||||
[Question Model](#object-model)
|
||||
[Question Model](guide/dynamic-form#object-model)
|
||||
|
||||
[Form Component](#form-component)
|
||||
[Form Component](guide/dynamic-form#form-component)
|
||||
|
||||
[Questionnaire Metadata](#questionnaire-metadata)
|
||||
[Questionnaire Metadata](guide/dynamic-form#questionnaire-metadata)
|
||||
|
||||
[Dynamic Template](#dynamic-template)
|
||||
[Dynamic Template](guide/dynamic-form#dynamic-template)
|
||||
**See the <live-example name="cb-dynamic-form"></live-example>**.
|
||||
|
||||
<a id="bootstrap"></a>## Bootstrap
|
||||
@ -129,7 +129,7 @@ The `ngSwitch` determines which type of question to display.
|
||||
In both components we're relying on Angular's **formGroup** to connect the template HTML to the
|
||||
underlying control objects, populated from the question model with display and validation rules.
|
||||
|
||||
`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`.
|
||||
`formControlName` and `formGroup` are directives defined in `ReactiveFormsModule`. Our templates can access these directives directly since we imported `ReactiveFormsModule` from `AppModule`.
|
||||
<a id="questionnaire-metadata"></a>## Questionnaire data`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
|
||||
|
||||
The set of questions we have defined for the job application is returned from the `QuestionService`.
|
||||
@ -165,4 +165,4 @@ The final form looks like this:
|
||||
<img src="assets/images/cookbooks/dynamic-form/dynamic-form.png" alt="Dynamic-Form"> </img>
|
||||
</figure>
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/dynamic-form#top)
|
@ -2,7 +2,7 @@
|
||||
Form Validation
|
||||
|
||||
@intro
|
||||
Validate user's form entries
|
||||
Validate user's form entries.
|
||||
|
||||
@description
|
||||
|
||||
@ -12,21 +12,21 @@ We can improve overall data quality by validating user input for accuracy and co
|
||||
|
||||
In this cookbook we show how to validate user input in the UI and display useful validation messages
|
||||
using first the template-driven forms and then the reactive forms approach.
|
||||
Learn more about these choices in the [Forms chapter.](../guide/forms.html)
|
||||
Learn more about these choices in the [Forms chapter.](guide/forms)
|
||||
|
||||
|
||||
{@a toc}
|
||||
## Table of Contents
|
||||
|
||||
[Simple Template-Driven Forms](#template1)
|
||||
[Simple Template-Driven Forms](guide/form-validation#template1)
|
||||
|
||||
[Template-Driven Forms with validation messages in code](#template2)
|
||||
[Template-Driven Forms with validation messages in code](guide/form-validation#template2)
|
||||
|
||||
[Reactive Forms with validation in code](#reactive)
|
||||
[Reactive Forms with validation in code](guide/form-validation#reactive)
|
||||
|
||||
[Custom validation](#custom-validation)
|
||||
[Custom validation](guide/form-validation#custom-validation)
|
||||
|
||||
[Testing](#testing)
|
||||
[Testing](guide/form-validation#testing)
|
||||
|
||||
|
||||
{@a live-example}
|
||||
@ -85,7 +85,7 @@ The full template repeats this kind of layout for each data entry control on the
|
||||
We shouldn't show errors for a new hero before the user has had a chance to edit the value.
|
||||
The checks for `dirty` and `touched` prevent premature display of errors.
|
||||
|
||||
Learn about `dirty` and `touched` in the [Forms](../guide/forms.html) chapter.The component class manages the hero model used in the data binding
|
||||
Learn about `dirty` and `touched` in the [Forms](guide/forms) chapter.The component class manages the hero model used in the data binding
|
||||
as well as other code to support the view.
|
||||
|
||||
|
||||
@ -148,8 +148,8 @@ The `<input>` element HTML is almost the same. There are noteworthy differences:
|
||||
- The hard-code error message `<divs>` are gone.
|
||||
|
||||
- There's a new attribute, `forbiddenName`, that is actually a custom validation directive.
|
||||
It invalidates the control if the user enters "bob" anywhere in the name ([try it](#live-example)).
|
||||
We discuss [custom validation directives](#custom-validation) later in this cookbook.
|
||||
It invalidates the control if the user enters "bob" anywhere in the name ([try it](guide/form-validation#live-example)).
|
||||
We discuss [custom validation directives](guide/form-validation#custom-validation) later in this cookbook.
|
||||
|
||||
- The `#name` template variable is gone because we no longer refer to the Angular control for this element.
|
||||
|
||||
@ -179,7 +179,7 @@ the name of that variable as a string (`'heroForm'` in this case).
|
||||
- The `heroForm` object changes several times during the life of the component, most notably when we add a new hero.
|
||||
We'll have to re-inspect it periodically.
|
||||
|
||||
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](../guide/lifecycle-hooks.html#afterview)
|
||||
- Angular calls the `ngAfterViewChecked` [lifecycle hook method](guide/lifecycle-hooks)
|
||||
when anything changes in the view.
|
||||
That's the right time to see if there's a new `heroForm` object.
|
||||
|
||||
@ -245,7 +245,7 @@ Here's how we imported it in the `HeroFormTemplateModule`.
|
||||
We haven't talked about the `SharedModule` or its `SubmittedComponent` which appears at the bottom of every
|
||||
form template in this cookbook.
|
||||
|
||||
They're not germane to the validation story. Look at the [live example](#live-example) if you're interested.
|
||||
They're not germane to the validation story. Look at the [live example](guide/form-validation#live-example) if you're interested.
|
||||
|
||||
|
||||
|
||||
@ -258,7 +258,7 @@ At runtime, Angular interprets the template and derives its _form control model_
|
||||
|
||||
**Reactive Forms** takes a different approach.
|
||||
You create the form control model in code. You write the template with form elements
|
||||
and`form...` directives from the Angular `ReactiveFormsModule`.
|
||||
and `form...` directives from the Angular `ReactiveFormsModule`.
|
||||
At runtime, Angular binds the template elements to your control model based on your instructions.
|
||||
|
||||
This approach requires a bit more effort. *You have to write the control model and manage it*.
|
||||
@ -266,7 +266,7 @@ This approach requires a bit more effort. *You have to write the control model a
|
||||
In return, you can
|
||||
* add, change, and remove validation functions on the fly
|
||||
* manipulate the control model dynamically from within the component
|
||||
* [test](#testing) validation and control logic with isolated unit tests.
|
||||
* [test](guide/form-validation#testing) validation and control logic with isolated unit tests.
|
||||
|
||||
The third cookbook sample re-writes the hero form in _reactive forms_ style.
|
||||
|
||||
@ -346,7 +346,7 @@ Here's the section of code devoted to that process, paired with the template-dri
|
||||
|
||||
- we inject the `FormBuilder` in a constructor.
|
||||
|
||||
- we call a `buildForm` method in the `ngOnInit` [lifecycle hook method](../guide/lifecycle-hooks.html#hooks-overview)
|
||||
- we call a `buildForm` method in the `ngOnInit` [lifecycle hook method](guide/lifecycle-hooks)
|
||||
because that's when we'll have the hero data. We'll call it again in the `addHero` method.
|
||||
A real app would retrieve the hero asynchronously from a data service, a task best performed in the `ngOnInit` hook.- the `buildForm` method uses the `FormBuilder` (`fb`) to declare the form control model.
|
||||
Then it attaches the same `onValueChanged` handler (there's a one line difference)
|
||||
@ -363,9 +363,9 @@ Most of the validator functions are stock validators provided by Angular as stat
|
||||
Angular has stock validators that correspond to the standard HTML validation attributes.
|
||||
|
||||
The `forbiddenNames` validator on the `"name"` control is a custom validator,
|
||||
discussed in a separate [section below](#custom-validation).
|
||||
discussed in a separate [section below](guide/form-validation#custom-validation).
|
||||
|
||||
Learn more about `FormBuilder` in a _forthcoming_ chapter on reactive forms.
|
||||
Learn more about `FormBuilder` in the [Introduction to FormBuilder](guide/reactive-forms) section of Reactive Forms guide.
|
||||
#### Committing hero value changes
|
||||
|
||||
In two-way data binding, the user's changes flow automatically from the controls back to the data model properties.
|
||||
@ -410,7 +410,7 @@ Here's the complete reactive component file, compared to the two template-driven
|
||||
</md-tab-group>
|
||||
|
||||
|
||||
Run the [live example](#live-example) to see how the reactive form behaves
|
||||
Run the [live example](guide/form-validation#live-example) to see how the reactive form behaves
|
||||
and to compare all of the files in this cookbook sample.
|
||||
|
||||
|
||||
@ -461,7 +461,7 @@ The rest of the directive is unremarkable and we present it here without further
|
||||
{@example 'cb-form-validation/ts/src/app/shared/forbidden-name.directive.ts' region='directive'}
|
||||
|
||||
|
||||
See the [Attribute Directives](../guide/attribute-directives.html) chapter.
|
||||
See the [Attribute Directives](guide/attribute-directives) chapter.
|
||||
|
||||
|
||||
|
@ -29,7 +29,7 @@ You can run the <live-example></live-example> in Plunker and download the code f
|
||||
|
||||
## Template-driven forms
|
||||
|
||||
You can build forms by writing templates in the Angular [template syntax](./template-syntax.html) with
|
||||
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
|
||||
the form-specific directives and techniques described in this page.
|
||||
|
||||
You can also use a reactive (or model-driven) approach to build forms.
|
||||
@ -74,7 +74,7 @@ You'll build this form in small steps:
|
||||
1. Disable the form’s *Submit* button until the form is valid.
|
||||
## Setup
|
||||
|
||||
Follow the [setup](setup.html) instructions for creating a new project
|
||||
Follow the [setup](guide/setup) instructions for creating a new project
|
||||
named <span ngio-ex>angular-forms</span>.
|
||||
|
||||
## Create the Hero model class
|
||||
@ -119,8 +119,8 @@ Understanding this component requires only the Angular concepts covered in previ
|
||||
- You defined dummy data for `model` and `powers`, as befits a demo.
|
||||
Down the road, you can inject a data service to get and save real data
|
||||
or perhaps expose these properties as inputs and outputs
|
||||
(see [Input and output properties](./template-syntax.html#inputs-outputs) on the
|
||||
[Template Syntax](./template-syntax.html) page) for binding to a
|
||||
(see [Input and output properties](guide/template-syntax) on the
|
||||
[Template Syntax](guide/template-syntax) page) for binding to a
|
||||
parent component. This is not a concern now and these future changes won't affect the form.
|
||||
- You added a `diagnostic` property to return a JSON representation of the model.
|
||||
It'll help you see what you're doing during development; you've left yourself a cleanup note to discard it later.
|
||||
@ -230,7 +230,7 @@ You maintain that list internally (in `HeroFormComponent`).
|
||||
|
||||
You'll add a `select` to the
|
||||
form and bind the options to the `powers` list using `ngFor`,
|
||||
a technique seen previously in the [Displaying Data](./displaying-data.html) page.
|
||||
a technique seen previously in the [Displaying Data](guide/displaying-data) page.
|
||||
|
||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
||||
This code repeats the `<option>` tag for each power in the list of powers.
|
||||
@ -247,8 +247,8 @@ Running the app right now would be disappointing.
|
||||
|
||||
You don't see hero data because you're not binding to the `Hero` yet.
|
||||
You know how to do that from earlier pages.
|
||||
[Displaying Data](./displaying-data.html) teaches property binding.
|
||||
[User Input](./user-input.html) shows how to listen for DOM events with an
|
||||
[Displaying Data](guide/displaying-data) teaches property binding.
|
||||
[User Input](guide/user-input) shows how to listen for DOM events with an
|
||||
event binding and how to update a component property with the displayed value.
|
||||
|
||||
Now you need to display, listen, and extract at the same time.
|
||||
@ -278,8 +278,8 @@ back again.
|
||||
|
||||
That's *two-way data binding*.
|
||||
For more information, see
|
||||
[Two-way binding with NgModel](template-syntax.html#ngModel) on the
|
||||
the [Template Syntax](template-syntax.html) page.
|
||||
[Two-way binding with NgModel](guide/template-syntax) on the
|
||||
the [Template Syntax](guide/template-syntax) page.
|
||||
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
|
||||
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
|
||||
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
|
||||
@ -287,7 +287,7 @@ Defining a `name` attribute is a requirement when using `[(ngModel)]` in combina
|
||||
Internally, Angular creates `FormControl` instances and
|
||||
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
|
||||
Each `FormControl` is registered under the name you assigned to the `name` attribute.
|
||||
Read more in [The NgForm directive](#ngForm), later in this page.
|
||||
Read more in [The NgForm directive](guide/forms#ngForm), later in this page.
|
||||
Add similar `[(ngModel)]` bindings and `name` attributes to *Alter Ego* and *Hero Power*.
|
||||
You'll ditch the input box binding message
|
||||
and add a new binding (at the top) to the component's `diagnostic` property.
|
||||
@ -401,7 +401,7 @@ You can leverage those class names to change the appearance of the control.
|
||||
|
||||
</table>
|
||||
|
||||
Temporarily add a [template reference variable](./template-syntax.html#ref-vars) named `spy`
|
||||
Temporarily add a [template reference variable](guide/template-syntax) named `spy`
|
||||
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
|
||||
Now run the app and look at the _Name_ input box.
|
||||
Follow these steps *precisely*:
|
||||
@ -458,7 +458,7 @@ When the user deletes the name, the form should look like this:
|
||||
</figure>
|
||||
|
||||
To achieve this effect, extend the `<input>` tag with the following:
|
||||
- A [template reference variable](./template-syntax.html#ref-vars).
|
||||
- A [template reference variable](guide/template-syntax).
|
||||
- The "*is required*" message in a nearby `<div>`, which you'll display only if the control is invalid.
|
||||
|
||||
Here's an example of an error message added to the _name_ input box:
|
||||
@ -466,7 +466,7 @@ You need a template reference variable to access the input box's Angular control
|
||||
Here you created a variable called `name` and gave it the value "ngModel".
|
||||
|
||||
Why "ngModel"?
|
||||
A directive's [exportAs](../api/core/index/Directive-decorator.html) property
|
||||
A directive's [exportAs](api/core/index/Directive-decorator) property
|
||||
tells Angular how to link the reference variable to the directive.
|
||||
You set `name` to `ngModel` because the `ngModel` directive's `exportAs` property happens to be "ngModel".
|
||||
You control visibility of the name error message by binding properties of the `name`
|
||||
@ -481,7 +481,7 @@ you'll see the error message immediately, before you've done anything.
|
||||
|
||||
Some developers want to the message to display only when the user makes an invalid change.
|
||||
Hiding the message while the control is "pristine" achieves that goal.
|
||||
You'll see the significance of this choice when you [add a new hero](#new-hero) to the form.
|
||||
You'll see the significance of this choice when you [add a new hero](guide/forms#new-hero) to the form.
|
||||
|
||||
The hero *Alter Ego* is optional so you can leave that be.
|
||||
|
||||
@ -540,7 +540,7 @@ The variable `heroForm` is now a reference to the `NgForm` directive that govern
|
||||
### The _NgForm_ directive
|
||||
|
||||
What `NgForm` directive?
|
||||
You didn't add an [NgForm](../api/forms/index/NgForm-directive.html) directive.
|
||||
You didn't add an [NgForm](api/forms/index/NgForm-directive) directive.
|
||||
|
||||
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
||||
|
||||
|
@ -2,5 +2,5 @@
|
||||
Glossary
|
||||
|
||||
@intro
|
||||
Brief definitions of the most important words in the Angular vocabulary
|
||||
Brief definitions of the most important words in the Angular vocabulary.
|
||||
|
||||
|
@ -6,7 +6,7 @@ Angular's hierarchical dependency injection system supports nested injectors in
|
||||
|
||||
@description
|
||||
You learned the basics of Angular Dependency injection in the
|
||||
[Dependency Injection](./dependency-injection.html) guide.
|
||||
[Dependency Injection](guide/dependency-injection) guide.
|
||||
|
||||
Angular has a _Hierarchical Dependency Injection_ system.
|
||||
There is actually a tree of injectors that parallel an application's component tree.
|
||||
@ -18,7 +18,7 @@ Try the <live-example></live-example>.
|
||||
|
||||
## The injector tree
|
||||
|
||||
In the [Dependency Injection](./dependency-injection.html) guide,
|
||||
In the [Dependency Injection](guide/dependency-injection) guide,
|
||||
you learned how to configure a dependency injector and how to retrieve dependencies where you need them.
|
||||
|
||||
In fact, there is no such thing as ***the*** injector.
|
||||
@ -51,8 +51,9 @@ If it runs out of ancestors, Angular throws an error.
|
||||
|
||||
You can cap the bubbling. An intermediate component can declare that it is the "host" component.
|
||||
The hunt for providers will climb no higher than the injector for that host component.
|
||||
This a topic for another day.
|
||||
This is a topic for another day.
|
||||
### Re-providing a service at different levels
|
||||
|
||||
You can re-register a provider for a particular dependency token at multiple levels of the injector tree.
|
||||
You don't *have* to re-register providers. You shouldn't do so unless you have a good reason.
|
||||
But you *can*.
|
||||
@ -67,6 +68,7 @@ All requests bubble up to the root <span if-docs="ts"><code>NgModule</code></spa
|
||||
## Component injectors
|
||||
|
||||
The ability to configure one or more providers at different levels opens up interesting and useful possibilities.
|
||||
|
||||
### Scenario: service isolation
|
||||
|
||||
Architectural reasons may lead you to restrict access to a service to the application domain where it belongs.
|
||||
@ -74,27 +76,23 @@ Architectural reasons may lead you to restrict access to a service to the applic
|
||||
The guide sample includes a `VillainsListComponent` that displays a list of villains.
|
||||
It gets those villains from a `VillainsService`.
|
||||
|
||||
While you could provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`),
|
||||
While you _could_ provide `VillainsService` in the root `AppModule` (that's where you'll find the `HeroesService`),
|
||||
that would make the `VillainsService` available everywhere in the application, including the _Hero_ workflows.
|
||||
|
||||
If you later modify the `VillainsService`, you could break something in a hero component somewhere.
|
||||
That's not supposed to happen but the way you've provided the service creates that risk.
|
||||
If you later modified the `VillainsService`, you could break something in a hero component somewhere.
|
||||
That's not supposed to happen but providing the service in the root `AppModule` creates that risk.
|
||||
|
||||
Instead, provide the `VillainsService` in the `providers` metadata of the `VillainsListComponent` like this:
|
||||
|
||||
|
||||
{@example 'hierarchical-dependency-injection/ts/src/app/villains-list.component.ts' region='metadata'}
|
||||
|
||||
By providing `VillainsService` in the `VillainsListComponent` metadata — and nowhere else —,
|
||||
By providing `VillainsService` in the `VillainsListComponent` metadata and nowhere else,
|
||||
the service becomes available only in the `VillainsListComponent` and its sub-component tree.
|
||||
It's still a singleton, but it's a singleton that exist solely in the _villain_ domain.
|
||||
|
||||
You are confident that a hero component can't access it. You've reduced your exposure to error.
|
||||
Now you know that a hero component can't access it. You've reduced your exposure to error.
|
||||
|
||||
### Scenario: multiple edit sessions
|
||||
|
||||
Many applications allow users to work on several open tasks at the same time.
|
||||
For example, in a tax preparation application, the preparer could be working several tax returns,
|
||||
For example, in a tax preparation application, the preparer could be working on several tax returns,
|
||||
switching from one to the other throughout the day.
|
||||
|
||||
This guide demonstrates that scenario with an example in the Tour of Heroes theme.
|
||||
@ -103,28 +101,30 @@ Imagine an outer `HeroListComponent` that displays a list of super heroes.
|
||||
To open a hero's tax return, the preparer clicks on a hero name, which opens a component for editing that return.
|
||||
Each selected hero tax return opens in its own component and multiple returns can be open at the same time.
|
||||
|
||||
Each tax return component
|
||||
* is its own tax return editing session.
|
||||
* can change a tax return without affecting a return in another component.
|
||||
* has the ability to save the changes to its tax return or cancel them.
|
||||
Each tax return component has the following characteristics:
|
||||
* Is its own tax return editing session.
|
||||
* Can change a tax return without affecting a return in another component.
|
||||
* Has the ability to save the changes to its tax return or cancel them.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src="assets/images/devguide/dependency-injection/hid-heroes-anim.gif" width="400" alt="Heroes in action"> </img>
|
||||
</figure>
|
||||
|
||||
One might suppose that the `TaxReturnComponent` has logic to manage and restore changes.
|
||||
One might suppose that the `HeroTaxReturnComponent` has logic to manage and restore changes.
|
||||
That would be a pretty easy task for a simple hero tax return.
|
||||
In the real world, with a rich tax return data model, the change management would be tricky.
|
||||
You might delegate that management to a helper service, as this example does.
|
||||
|
||||
Here is the `HeroTaxReturnService`.
|
||||
It caches a single `HeroTaxReturn`, tracks changes to that return, and can save or restore it.
|
||||
It also delegates to the application-wide, singleton `HeroService`, which it gets by injection.
|
||||
It also delegates to the application-wide singleton `HeroService`, which it gets by injection.
|
||||
|
||||
|
||||
{@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.service.ts'}
|
||||
|
||||
Here is the `HeroTaxReturnComponent` that makes use of it.
|
||||
|
||||
|
||||
{@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts'}
|
||||
|
||||
The _tax-return-to-edit_ arrives via the input property which is implemented with getters and setters.
|
||||
@ -138,23 +138,19 @@ Each component would overwrite the tax return that belonged to another hero.
|
||||
What a mess!
|
||||
|
||||
Look closely at the metadata for the `HeroTaxReturnComponent`. Notice the `providers` property.
|
||||
|
||||
|
||||
{@example 'hierarchical-dependency-injection/ts/src/app/hero-tax-return.component.ts' region='providers'}
|
||||
|
||||
The `HeroTaxReturnComponent` has its own provider of the `HeroTaxReturnService`.
|
||||
Recall that every component _instance_ has its own injector.
|
||||
Providing the service at the component level ensures that _every_ instance of the component gets its own, private instance of the service.
|
||||
No tax return overwriting. No mess.
|
||||
|
||||
The rest of the scenario code relies on other Angular features and techniques that you can learn about elsewhere in the documentation.
|
||||
You can review it and download it from the <live-example></live-example>
|
||||
You can review it and download it from the <live-example></live-example>.
|
||||
### Scenario: specialized providers
|
||||
|
||||
Another reason to re-provide a service is to substitute a _more specialized_ implementation of that service,
|
||||
deeper in the component tree.
|
||||
|
||||
Consider again the Car example from the [Dependency Injection](./dependency-injection.html) guide.
|
||||
Consider again the Car example from the [Dependency Injection](guide/dependency-injection) guide.
|
||||
Suppose you configured the root injector (marked as A) with _generic_ providers for
|
||||
`CarService`, `EngineService` and `TiresService`.
|
||||
|
||||
@ -181,4 +177,4 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
|
||||
|
||||
|
||||
The code for this _cars_ scenario is in the `car.components.ts` and `car.services.ts` files of the sample
|
||||
which you can review and download from the <live-example></live-example>
|
||||
which you can review and download from the <live-example></live-example>.
|
@ -12,17 +12,17 @@ Angular's _internationalization_ (_i18n_) tools help make your app available in
|
||||
|
||||
## Table of contents
|
||||
|
||||
* [Angular and i18n template translation](#angular-i18n)
|
||||
* [Mark text with the _i18n_ attribute](#i18n-attribute)
|
||||
* [Add _i18n-..._ translation attributes](#translate-attributes)
|
||||
* [Handle singular and plural](#cardinality)
|
||||
* [Select among alternative texts](#select)
|
||||
* [Create a translation source file with the **_ng-xi18n_ extraction tool**](#ng-xi18n)
|
||||
* [Translate text messages](#translate)
|
||||
* [Merge the completed translation file into the app](#merge)
|
||||
* [Merge with the JIT compiler](#jit)
|
||||
* [Internationalization with the AOT compiler](#aot)
|
||||
* [Translation file maintenance and _id_ changes](#maintenance)
|
||||
* [Angular and i18n template translation](guide/i18n#angular-i18n)
|
||||
* [Mark text with the _i18n_ attribute](guide/i18n#i18n-attribute)
|
||||
* [Add _i18n-..._ translation attributes](guide/i18n#translate-attributes)
|
||||
* [Handle singular and plural](guide/i18n#cardinality)
|
||||
* [Select among alternative texts](guide/i18n#select)
|
||||
* [Create a translation source file with the **_ng-xi18n_ extraction tool**](guide/i18n#ng-xi18n)
|
||||
* [Translate text messages](guide/i18n#translate)
|
||||
* [Merge the completed translation file into the app](guide/i18n#merge)
|
||||
* [Merge with the JIT compiler](guide/i18n#jit)
|
||||
* [Internationalization with the AOT compiler](guide/i18n#aot)
|
||||
* [Translation file maintenance and _id_ changes](guide/i18n#maintenance)
|
||||
**Try this** <live-example name="cb-i18n" title="i18n Example in Spanish">live example</live-example>
|
||||
of a JIT-compiled app, translated into Spanish.
|
||||
|
||||
@ -346,7 +346,7 @@ replace the `<target/>` tag with the Spanish greeting:
|
||||
Note that the tool generates the `id`. **Don't touch it.**
|
||||
Its value depends on the content of the message and its assigned meaning.
|
||||
Change either factor and the `id` changes as well.
|
||||
See the **[translation file maintenance discussion](#maintenance)**.
|
||||
See the **[translation file maintenance discussion](guide/i18n#maintenance)**.
|
||||
|
||||
|
||||
~~~
|
||||
@ -465,8 +465,8 @@ You provide the Angular compiler with three new pieces of information:
|
||||
_How_ you provide this information depends upon whether you compile with
|
||||
the JIT (_Just-in-Time_) compiler or the AOT (_Ahead-of-Time_) compiler.
|
||||
|
||||
* With [JIT](#jit), you provide the information at bootstrap time.
|
||||
* With [AOT](#aot), you pass the information as `ngc` options.
|
||||
* With [JIT](guide/i18n#jit), you provide the information at bootstrap time.
|
||||
* With [AOT](guide/i18n#aot), you pass the information as `ngc` options.
|
||||
|
||||
|
||||
{@a jit}
|
||||
@ -524,10 +524,10 @@ and the corresponding translation file:
|
||||
It must return a `Promise` because this function could read a translation file asynchronously from the server.
|
||||
|
||||
1. It creates a transaction filename from the locale according to the name and location convention
|
||||
[described earlier](#localization-folder).
|
||||
[described earlier](guide/i18n#localization-folder).
|
||||
|
||||
1. The `getTranslationsWithSystemJs` method reads the translation and returns the contents as a string.
|
||||
Notice that it appends `!text` to the filename, telling SystemJS to use the [text plugin](#text-plugin).
|
||||
Notice that it appends `!text` to the filename, telling SystemJS to use the [text plugin](guide/i18n#text-plugin).
|
||||
|
||||
1. The callback composes a providers array with the three translation providers.
|
||||
|
||||
@ -574,8 +574,8 @@ It does explain the few steps necessary to tell the AOT compiler to apply a tran
|
||||
Internationalization with the AOT compiler requires
|
||||
some setup specifically for AOT compilation.
|
||||
Start with the application project as shown
|
||||
[just before merging the translation file](#app-pre-translation)
|
||||
and refer to the [AOT cookbook](aot-compiler.html) to make it _AOT-ready_.
|
||||
[just before merging the translation file](guide/i18n#app-pre-translation)
|
||||
and refer to the [AOT cookbook](guide/aot-compiler) to make it _AOT-ready_.
|
||||
|
||||
Next, issue an `ngc` compile command for each supported language (including English).
|
||||
The result is a separate version of the application for each language.
|
@ -2,11 +2,11 @@
|
||||
Documentation Overview
|
||||
|
||||
@intro
|
||||
How to read and use this documentation
|
||||
How to read and use this documentation.
|
||||
|
||||
@description
|
||||
This page describes the Angular documentation at a high level.
|
||||
If you're new to Angular, you may want to visit "[Learning Angular](learning-angular.html)" first.
|
||||
If you're new to Angular, you may want to visit "[Learning Angular](guide/learning-angular)" first.
|
||||
|
||||
## Themes
|
||||
|
||||
@ -132,7 +132,7 @@ Each page includes code snippets from a sample application that accompanies the
|
||||
You can reuse these snippets in your applications.
|
||||
|
||||
Look for a link to a running version of that sample, often near the top of the page,
|
||||
such as this <live-example nodownload name="architecture"></live-example> from the [Architecture](architecture.html) page.
|
||||
such as this <live-example nodownload name="architecture"></live-example> from the [Architecture](guide/architecture) page.
|
||||
<span if-docs="ts">
|
||||
The link launches a browser-based, code editor where you can inspect, modify, save, and download the code.
|
||||
</span>
|
||||
@ -142,10 +142,10 @@ Just download, unzip, run `npm install` to install the dependencies and run it w
|
||||
|
||||
## Reference pages
|
||||
|
||||
* The [Cheat Sheet](cheatsheet.html) lists Angular syntax for common scenarios.
|
||||
* The [Glossary](glossary.html) defines terms that Angular developers should know.
|
||||
<li if-docs="ts">The [Change Log](change-log.html) announces what's new and changed in the documentation.</li>
|
||||
* The [API Reference](../api/) is the authority on every public-facing member of the Angular libraries.
|
||||
* The [Cheat Sheet](guide/cheatsheet) lists Angular syntax for common scenarios.
|
||||
* The [Glossary](guide/glossary) defines terms that Angular developers should know.
|
||||
<li if-docs="ts">The [Change Log](guide/change-log) announces what's new and changed in the documentation.</li>
|
||||
* The [API Reference](api/) is the authority on every public-facing member of the Angular libraries.
|
||||
|
||||
## Feedback
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Learning Angular
|
||||
|
||||
@intro
|
||||
A suggested path through the documentation for Angular newcomers
|
||||
A suggested path through the documentation for Angular newcomers.
|
||||
|
||||
@description
|
||||
|
||||
@ -15,32 +15,32 @@ You don't have to read the documentation straight through. Most pages stand on
|
||||
Those new to Angular may wish to follow this popular learning path.
|
||||
<br class="l-clear-left">
|
||||
|
||||
1. [Setup](setup.html "Setup locally withe Quickstart seed") for local Angular development, if you haven't already done so.
|
||||
1. [Setup](guide/setup) for local Angular development, if you haven't already done so.
|
||||
|
||||
1. Take the [*Tour of Heroes* tutorial](../tutorial "Tour of Heroes").
|
||||
1. Take the [*Tour of Heroes* tutorial](tutorial "Tour of Heroes").
|
||||
|
||||
The *Tour of Heroes* takes you step-by-step from [setup](setup.html)
|
||||
The *Tour of Heroes* takes you step-by-step from [setup](guide/setup)
|
||||
to a full-featured example that demonstrates the essential characteristics of a professional application:
|
||||
a sensible project structure, data binding, master/detail, services, dependency injection, navigation, and remote data access.
|
||||
|
||||
1. <a id="architecture"></a>Read the [Architecture](architecture.html) overview for the big picture.
|
||||
1. <a id="architecture"></a>Read the [Architecture](guide/architecture) overview for the big picture.
|
||||
|
||||
1. [The Root Module](appmodule.html) introduces the `NgModule` class that tells Angular how to compile and run your application.
|
||||
1. [The Root Module](guide/appmodule) introduces the `NgModule` class that tells Angular how to compile and run your application.
|
||||
|
||||
1. [Displaying Data](displaying-data.html) shows how data binding puts component property values on screen.
|
||||
1. [Displaying Data](guide/displaying-data) shows how data binding puts component property values on screen.
|
||||
|
||||
1. [User Input](user-input.html) explains how to respond to user-initiated DOM events.
|
||||
1. [User Input](guide/user-input) explains how to respond to user-initiated DOM events.
|
||||
|
||||
1. [Forms](forms.html) covers data entry and validation within the UI.
|
||||
1. [Forms](guide/forms) covers data entry and validation within the UI.
|
||||
|
||||
1. [Dependency Injection](dependency-injection.html) is the way to build large, maintainable applications
|
||||
1. [Dependency Injection](guide/dependency-injection) is the way to build large, maintainable applications
|
||||
from small, single-purpose parts.
|
||||
|
||||
1. [Template Syntax](template-syntax.html) is a comprehensive study of Angular template HTML.
|
||||
1. [Template Syntax](guide/template-syntax) is a comprehensive study of Angular template HTML.
|
||||
|
||||
After reading the above sections, feel free to skip around among the other pages on this site.
|
||||
|
||||
### Next Step
|
||||
|
||||
Try the [tutorial](../tutorial "Tour of Heroes") if you're ready to start coding or
|
||||
visit the [Architecture](architecture.html "Basic Concepts") page if you prefer to learn the basic concepts first.
|
||||
Try the [tutorial](tutorial "Tour of Heroes") if you're ready to start coding or
|
||||
visit the [Architecture](guide/architecture) page if you prefer to learn the basic concepts first.
|
@ -492,7 +492,7 @@ created under test or before you decide to display it.
|
||||
Constructors should do no more than set the initial local variables to simple values.
|
||||
|
||||
An `ngOnInit` is a good place for a component to fetch its initial data. The
|
||||
[Tutorial](../tutorial/toh-pt4.html#oninit) and [HTTP](server-communication.html#oninit) chapter
|
||||
[Tutorial](tutorial/toh-pt4) and [HTTP](guide/server-communication) chapter
|
||||
show how.
|
||||
|
||||
|
||||
@ -523,7 +523,7 @@ This example monitors the `OnChanges` hook.
|
||||
{@example 'lifecycle-hooks/ts/src/app/on-changes.component.ts' region='ng-on-changes'}
|
||||
|
||||
The `ngOnChanges` method takes an object that maps each changed property name to a
|
||||
[SimpleChange](../api/core/index/SimpleChange-class.html) object holding the current and previous property values.
|
||||
[SimpleChange](api/core/index/SimpleChange-class) object holding the current and previous property values.
|
||||
This hook iterates over the changed properties and logs them.
|
||||
|
||||
The example component, `OnChangesComponent`, has two input properties: `hero` and `power`.
|
||||
@ -588,7 +588,7 @@ The `AfterViewComponent` displays this child view *within its template*:
|
||||
|
||||
The following hooks take action based on changing values *within the child view*
|
||||
which can only be reached by querying for the child view via the property decorated with
|
||||
[@ViewChild](../api/core/index/ViewChild-decorator.html).
|
||||
[@ViewChild](api/core/index/ViewChild-decorator).
|
||||
|
||||
|
||||
{@example 'lifecycle-hooks/ts/src/app/after-view.component.ts' region='hooks'}
|
||||
@ -626,7 +626,7 @@ The *AfterContent* sample explores the `AfterContentInit` and `AfterContentCheck
|
||||
into the component's template in a designated spot.
|
||||
|
||||
AngularJS developers know this technique as *transclusion*.
|
||||
Consider this variation on the [previous _AfterView_](#afterview) example.
|
||||
Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afterview) example.
|
||||
This time, instead of including the child view within the template, it imports the content from
|
||||
the `AfterContentComponent`'s parent. Here's the parent's template.
|
||||
|
||||
@ -661,7 +661,7 @@ projected into the component.
|
||||
|
||||
The following *AfterContent* hooks take action based on changing values in a *content child*
|
||||
which can only be reached by querying for it via the property decorated with
|
||||
[@ContentChild](../api/core/index/ContentChild-decorator.html).
|
||||
[@ContentChild](api/core/index/ContentChild-decorator).
|
||||
|
||||
|
||||
{@example 'lifecycle-hooks/ts/src/app/after-content.component.ts' region='hooks'}
|
||||
@ -672,7 +672,7 @@ which can only be reached by querying for it via the property decorated with
|
||||
### No unidirectional flow worries with _AfterContent..._
|
||||
|
||||
This component's `doSomething` method update's the component's data-bound `comment` property immediately.
|
||||
There's no [need to wait](#wait-a-tick).
|
||||
There's no [need to wait](guide/lifecycle-hooks#wait-a-tick).
|
||||
|
||||
Recall that Angular calls both *AfterContent* hooks before calling either of the *AfterView* hooks.
|
||||
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||
|
@ -8,5 +8,5 @@
|
||||
|
||||
This guide has been withdrawn.
|
||||
The essential information about this feature
|
||||
is in the [Structural Directives](structural-directives.html#ngcontainer) guide.
|
||||
is in the [Structural Directives](guide/structural-directives) guide.
|
||||
The original draft has been retained for possible future use.
|
@ -17,7 +17,7 @@ making some of them public so external components can use them.
|
||||
And there are many more options covered here.
|
||||
|
||||
Before reading this page, read the
|
||||
[The Root Module](appmodule.html) page, which introduces NgModules and the essentials
|
||||
[The Root Module](guide/appmodule) page, which introduces NgModules and the essentials
|
||||
of creating and maintaining a single root `AppModule` for the entire application.
|
||||
|
||||
This page covers NgModules in greater depth.
|
||||
@ -29,21 +29,21 @@ This page covers NgModules in greater depth.
|
||||
- some pages don't have tables of contents
|
||||
I didn't make changes here as I'm not sure what the correct style is.
|
||||
-->
|
||||
* [Angular modularity](#angular-modularity "Add structure to the app with NgModule")
|
||||
* [The application root module](#root-module "The startup module that every app requires")
|
||||
* [Bootstrap](#bootstrap "Launch the app in a browser with the root module as the entry point") the root module
|
||||
* [Declarations](#declarations "Declare the components, directives, and pipes that belong to a module")
|
||||
* [Providers](#providers "Extend the app with additional services")
|
||||
* [Imports](#imports "Import components, directives, and pipes for use in component templates")
|
||||
* [Resolve conflicts](#resolve-conflicts "When two directives have the same selector")
|
||||
* [Angular modularity](guide/ngmodule#angular-modularity "Add structure to the app with NgModule")
|
||||
* [The application root module](guide/ngmodule#root-module "The startup module that every app requires")
|
||||
* [Bootstrap](guide/ngmodule#bootstrap "Launch the app in a browser with the root module as the entry point") the root module
|
||||
* [Declarations](guide/ngmodule#declarations "Declare the components, directives, and pipes that belong to a module")
|
||||
* [Providers](guide/ngmodule#providers "Extend the app with additional services")
|
||||
* [Imports](guide/ngmodule#imports "Import components, directives, and pipes for use in component templates")
|
||||
* [Resolve conflicts](guide/ngmodule#resolve-conflicts "When two directives have the same selector")
|
||||
<!-- CF: See my comment in the "Resolve diretive conflicts" section below proposing renaming or reorganizing that section. -->
|
||||
* [Feature modules](#feature-modules "Partition the app into feature modules")
|
||||
* [Lazy loaded modules](#lazy-load "Load modules asynchronously") with the router
|
||||
* [Shared modules](#shared-module "Create modules for commonly used components, directives, and pipes")
|
||||
* [The Core module](#core-module "Create a core module with app-wide singleton services and single-use components")
|
||||
* [Configure core services with _forRoot_](#core-for-root "Configure providers during module import")
|
||||
* [Prevent reimport of the _CoreModule_](#prevent-reimport "because bad things happen if a lazy loaded module imports Core")
|
||||
* [NgModule metadata properties](#ngmodule-properties "A technical summary of the @NgModule metadata properties")
|
||||
* [Feature modules](guide/ngmodule#feature-modules "Partition the app into feature modules")
|
||||
* [Lazy loaded modules](guide/ngmodule#lazy-load "Load modules asynchronously") with the router
|
||||
* [Shared modules](guide/ngmodule#shared-module "Create modules for commonly used components, directives, and pipes")
|
||||
* [The Core module](guide/ngmodule#core-module "Create a core module with app-wide singleton services and single-use components")
|
||||
* [Configure core services with _forRoot_](guide/ngmodule#core-for-root "Configure providers during module import")
|
||||
* [Prevent reimport of the _CoreModule_](guide/ngmodule#prevent-reimport "because bad things happen if a lazy loaded module imports Core")
|
||||
* [NgModule metadata properties](guide/ngmodule#ngmodule-properties "A technical summary of the @NgModule metadata properties")
|
||||
<!-- CF: This link goes to the top of this page. I would expect it to go to an "NgModule metadata properties"
|
||||
section at the end of this page, but that section doesn't exist. -->
|
||||
|
||||
@ -61,7 +61,7 @@ Here's an index to live examples at key moments in the evolution of the sample:
|
||||
|
||||
This page covers NgModule concepts in a tutorial fashion.
|
||||
|
||||
The companion [NgModule FAQs](../cookbook/ngmodule-faq.html "NgModule FAQs") cookbook
|
||||
The companion [NgModule FAQs](cookbook/ngmodule-faq) cookbook
|
||||
offers answers to specific design and implementation questions.
|
||||
Read this page before reading those FAQs.
|
||||
|
||||
@ -119,7 +119,7 @@ Later in this page, you'll read about this process. For now, you'll start with t
|
||||
Every Angular app has a *root module* class.
|
||||
By convention, the *root module* class is called `AppModule` and it exists in a file named `app.module.ts`.
|
||||
|
||||
The `AppModule` from the QuickStart seed on the [Setup](setup.html) page is as minimal as possible:
|
||||
The `AppModule` from the QuickStart seed on the [Setup](guide/setup) page is as minimal as possible:
|
||||
|
||||
{@example 'setup/ts/src/app/app.module.ts'}
|
||||
|
||||
@ -153,7 +153,7 @@ Angular offers a variety of bootstrapping options targeting multiple platforms.
|
||||
This page describes two options, both targeting the browser.
|
||||
|
||||
### Dynamic bootstrapping with the just-in-time (JIT) compiler
|
||||
In the first, _dynamic_ option, the [Angular compiler](../cookbook/ngmodule-faq.html#q-angular-compiler "About the Angular Compiler")
|
||||
In the first, _dynamic_ option, the [Angular compiler](cookbook/ngmodule-faq)
|
||||
compiles the application in the browser and then launches the app.
|
||||
|
||||
|
||||
@ -206,7 +206,7 @@ This is the last time you'll look at `main.ts`.
|
||||
|
||||
## Declare directives and components
|
||||
As the app evolves,
|
||||
the first addition is a `HighlightDirective`, an [attribute directive](attribute-directives.html)
|
||||
the first addition is a `HighlightDirective`, an [attribute directive](guide/attribute-directives)
|
||||
that sets the background color of the attached element.
|
||||
|
||||
{@example 'ngmodule/ts/src/app/highlight.directive.ts'}
|
||||
@ -251,9 +251,9 @@ Import the `TitleComponent` class and add it to the module's `declarations`:
|
||||
|
||||
Modules are a great way to provide services for all of the module's components.
|
||||
|
||||
The [Dependency Injection](dependency-injection.html) page describes
|
||||
The [Dependency Injection](guide/dependency-injection) page describes
|
||||
the Angular hierarchical dependency-injection system and how to configure that system
|
||||
with [providers](dependency-injection.html#providers) at different levels of the
|
||||
with [providers](guide/dependency-injection) at different levels of the
|
||||
application's component tree.
|
||||
|
||||
A module can add providers to the application's root dependency injector, making those services
|
||||
@ -306,7 +306,7 @@ More accurately, `NgIf` is declared in `CommonModule` from `@angular/common`.
|
||||
|
||||
`CommonModule` contributes many of the common directives that applications need, including `ngIf` and `ngFor`.
|
||||
|
||||
`BrowserModule` imports `CommonModule` and [re-exports](../cookbook/ngmodule-faq.html#q-re-export) it.
|
||||
`BrowserModule` imports `CommonModule` and [re-exports](cookbook/ngmodule-faq) it.
|
||||
The net effect is that an importer of `BrowserModule` gets `CommonModule` directives automatically.Many familiar Angular directives don't belong to `CommonModule`.
|
||||
For example, `NgModel` and `RouterLink` belong to Angular's `FormsModule` and `RouterModule` respectively.
|
||||
You must import those modules before you can use their directives.
|
||||
@ -316,16 +316,16 @@ a form component that imports form support from the Angular `FormsModule`.
|
||||
|
||||
### Add the _ContactComponent_
|
||||
|
||||
[Angular forms](forms.html) are a great way to manage user data entry.
|
||||
[Angular forms](guide/forms) are a great way to manage user data entry.
|
||||
|
||||
The `ContactComponent` presents a "contact editor,"
|
||||
implemented with Angular forms in the [template-driven form](forms.html#template-driven) style.
|
||||
implemented with Angular forms in the [template-driven form](guide/forms) style.
|
||||
|
||||
### Angular form styles
|
||||
|
||||
You can write Angular form components in
|
||||
template-driven or
|
||||
[reactive](../cookbook/dynamic-form.html) style.
|
||||
[reactive](cookbook/dynamic-form) style.
|
||||
<!-- CF: this link goes to a page titled "Dynamic Forms". Should the link text be "dynamic" instead of "reactive"? -->
|
||||
|
||||
The following sample imports the `FormsModule` from `@angular/forms` because
|
||||
@ -338,7 +338,7 @@ Add an element with that name to the `AppComponent` template, just below the `<a
|
||||
{@example 'ngmodule/ts/src/app/app.component.1b.ts' region='template'}
|
||||
|
||||
Form components are often complex. The `ContactComponent` has its own `ContactService`
|
||||
and [custom pipe](pipes.html#custom-pipes) (called `Awesome`),
|
||||
and [custom pipe](guide/pipes) (called `Awesome`),
|
||||
and an alternative version of the `HighlightDirective`.
|
||||
|
||||
To make it manageable, place all contact-related material in an `src/app/contact` folder
|
||||
@ -434,7 +434,7 @@ To work around this, create an alias for the contact version using the `as` Java
|
||||
|
||||
This solves the immediate issue of referencing both directive _types_ in the same file but
|
||||
leaves another issue unresolved.
|
||||
You'll learn more about that issue later in this page, in [Resolve directive conflicts](#resolve-conflicts).
|
||||
You'll learn more about that issue later in this page, in [Resolve directive conflicts](guide/ngmodule#resolve-conflicts).
|
||||
### Provide the _ContactService_
|
||||
The `ContactComponent` displays contacts retrieved by the `ContactService`,
|
||||
which Angular injects into its constructor.
|
||||
@ -473,8 +473,8 @@ Now you can inject `ContactService` (like `UserService`) into any component in t
|
||||
To inject `ContactService`, you must first import its _type_.
|
||||
Only Contact components should import the `ContactService` type.
|
||||
|
||||
Read more in the [How do I restrict service scope to a module?](../cookbook/ngmodule-faq.html#q-component-scoped-providers) section
|
||||
of the [NgModule FAQs](../cookbook/ngmodule-faq.html) page.
|
||||
Read more in the [How do I restrict service scope to a module?](cookbook/ngmodule-faq) section
|
||||
of the [NgModule FAQs](cookbook/ngmodule-faq) page.
|
||||
### Run the app
|
||||
Everything is in place to run the application with its contact editor.
|
||||
|
||||
@ -549,7 +549,7 @@ Try the example:
|
||||
This section seems like more of an introduction to the next section, "Feature modules".
|
||||
Consider moving this section to be a child section of "Feature modules", or striking "Resolve" from this title. -->
|
||||
|
||||
An issue arose [earlier](#import-name-conflict) when you declared the contact's `HighlightDirective` because
|
||||
An issue arose [earlier](guide/ngmodule#import-name-conflict) when you declared the contact's `HighlightDirective` because
|
||||
you already had a `HighlightDirective` class at the application level.
|
||||
|
||||
The selectors of the two directives both highlight the attached element with a different color.
|
||||
@ -670,8 +670,8 @@ Before `ContactComponent` can bind with `[(ngModel)]`, its `ContactModule` must
|
||||
~~~
|
||||
|
||||
You also replaced `BrowserModule` by `CommonModule`, for reasons explained in the
|
||||
[Should I import BrowserModule or CommonModule?](../cookbook/ngmodule-faq.html#q-browser-vs-common-module)
|
||||
section of the [NgModule FAQs](../cookbook/ngmodule-faq.html) page.
|
||||
[Should I import BrowserModule or CommonModule?](cookbook/ngmodule-faq)
|
||||
section of the [NgModule FAQs](cookbook/ngmodule-faq) page.
|
||||
|
||||
You _declare_ the contact component, directive, and pipe in the module `declarations`.
|
||||
|
||||
@ -762,14 +762,14 @@ The module does _not_ import `HeroModule` or `CrisisModule`.
|
||||
They'll be fetched and mounted asynchronously when the user navigates to one of their routes.
|
||||
|
||||
The significant change from version 2 is the addition of the *AppRoutingModule* to the module `imports`.
|
||||
The `AppRoutingModule` is a [routing module](../guide/router.html#routing-module)
|
||||
The `AppRoutingModule` is a [routing module](guide/router)
|
||||
that handles the app's routing concerns.
|
||||
|
||||
### App routing
|
||||
|
||||
{@example 'ngmodule/ts/src/app/app-routing.module.ts'}
|
||||
|
||||
The router is the subject of the [Routing & Navigation](router.html) page, so this section skips many of the details and
|
||||
The router is the subject of the [Routing & Navigation](guide/router) page, so this section skips many of the details and
|
||||
concentrates on the intersection of NgModules and routing.
|
||||
|
||||
The `app-routing.module.ts` file defines three routes.
|
||||
@ -839,8 +839,8 @@ _forRoot_ and _forChild_ are conventional names for methods that
|
||||
deliver different `import` values to root and feature modules.
|
||||
Angular doesn't recognize them but Angular developers do.
|
||||
|
||||
[Follow this convention](../cookbook/ngmodule-faq.html#q-for-root) if you write a similar module
|
||||
that has both shared [declarables](../cookbook/ngmodule-faq.html#q-declarable) and services.
|
||||
[Follow this convention](cookbook/ngmodule-faq) if you write a similar module
|
||||
that has both shared [declarables](cookbook/ngmodule-faq) and services.
|
||||
|
||||
|
||||
~~~
|
||||
@ -866,7 +866,7 @@ that has both shared [declarables](../cookbook/ngmodule-faq.html#q-declarable) a
|
||||
Now that you navigate to `ContactComponent` with the router, there's no reason to make it public.
|
||||
Also, `ContactComponent` doesn't need a selector.
|
||||
No template will ever again reference this `ContactComponent`.
|
||||
It's gone from the [AppComponent template](#app-component-template).
|
||||
It's gone from the [AppComponent template](guide/ngmodule#app-component-template).
|
||||
|
||||
|
||||
{@a hero-module}
|
||||
@ -923,15 +923,15 @@ a more interesting and useful example. Its file structure is as follows:
|
||||
</aio-filetree>
|
||||
|
||||
This is the child routing scenario familiar to readers of the
|
||||
[Child routing component](router.html#child-routing-component) section of the
|
||||
[Routing & Navigation](router.html#child-routing-component) page.
|
||||
[Child routing component](guide/router) section of the
|
||||
[Routing & Navigation](guide/router) page.
|
||||
The `HeroComponent` is the feature's top component and routing host.
|
||||
Its template has a `<router-outlet>` that displays either a list of heroes (`HeroList`)
|
||||
or an editor of a selected hero (`HeroDetail`).
|
||||
Both components delegate to the `HeroService` to fetch and save data.
|
||||
|
||||
Yet another `HighlightDirective` colors elements in yet a different shade.
|
||||
In the next section, [Shared modules](#shared-module "Shared modules"), you'll resolve the repetition and inconsistencies.
|
||||
In the next section, [Shared modules](guide/ngmodule#shared-module "Shared modules"), you'll resolve the repetition and inconsistencies.
|
||||
|
||||
The `HeroModule` is a feature module like any other.
|
||||
|
||||
@ -1006,7 +1006,7 @@ and only one provider of it.
|
||||
|
||||
`UserService` is an application-wide singleton.
|
||||
You don't want each module to have its own separate instance.
|
||||
Yet there is [a real danger](../cookbook/ngmodule-faq.html#q-why-it-is-bad) of that happening
|
||||
Yet there is [a real danger](cookbook/ngmodule-faq) of that happening
|
||||
<!-- CF: This link goes to the top of the NgModule FAQs page.
|
||||
It looks like it is supposed to go to a specific question/section within the page. -->
|
||||
if the `SharedModule` provides the `UserService`.
|
||||
@ -1140,7 +1140,7 @@ A module that adds providers to the application can offer a facility for configu
|
||||
|
||||
By convention, the `forRoot` static method both provides and configures services at the same time.
|
||||
It takes a service configuration object and returns a
|
||||
[ModuleWithProviders](../api/core/index/ModuleWithProviders-interface.html), which is
|
||||
[ModuleWithProviders](api/core/index/ModuleWithProviders-interface), which is
|
||||
a simple object with the following properties:
|
||||
* `ngModule`: the `CoreModule` class
|
||||
* `providers`: the configured providers
|
||||
@ -1189,7 +1189,7 @@ Remember to _import_ the result; don't add it to any other `@NgModule` list.
|
||||
## Prevent reimport of the _CoreModule_
|
||||
|
||||
Only the root `AppModule` should import the `CoreModule`.
|
||||
[Bad things happen](../cookbook/ngmodule-faq.html#q-why-it-is-bad) if a lazy-loaded module imports it.
|
||||
[Bad things happen](cookbook/ngmodule-faq) if a lazy-loaded module imports it.
|
||||
<!-- CF: Again, this link goes to the top of the NgModule FAQs page.
|
||||
It looks like it is supposed to go to a specific question/section within the page. -->
|
||||
|
||||
@ -1226,5 +1226,5 @@ You made it! You can examine and download the complete source for this final ver
|
||||
### Frequently asked questions
|
||||
|
||||
Now that you understand NgModules, you may be interested
|
||||
in the companion [NgModule FAQs](../cookbook/ngmodule-faq.html "NgModule FAQs") page
|
||||
in the companion [NgModule FAQs](cookbook/ngmodule-faq) page
|
||||
with its ready answers to specific design and implementation questions.
|
@ -2,7 +2,7 @@
|
||||
Npm Packages
|
||||
|
||||
@intro
|
||||
Recommended npm packages, and how to specify package dependencies
|
||||
Recommended npm packages, and how to specify package dependencies.
|
||||
|
||||
@description
|
||||
Angular applications and Angular itself depend upon features and functionality provided by a variety of third-party packages.
|
||||
@ -16,12 +16,17 @@ Get them now</a> if they're not already installed on your machine.
|
||||
by running the commands `node -v` and `npm -v` in a terminal/console window.
|
||||
Older versions produce errors.
|
||||
|
||||
We recommend [nvm](https://github.com/creationix/nvm) for managing multiple versions of node and npm. You may need [nvm](https://github.com/creationix/nvm) if you already have projects running on your machine that use other versions of node and npm.
|
||||
We recommend a comprehensive starter-set of packages as specified in the `dependencies` and `devDependencies`
|
||||
sections of the <a href="https://docs.npmjs.com/files/package.json" target="_blank">package.json</a> file
|
||||
installed as described during [Setup](setup.html).You can use other packages but we recommend *this particular set* to start with because (a) they work well together and
|
||||
(b) they include everything you'll need to build and run the sample applications in this series.
|
||||
Note: A cookbook or guide page may require an additional library such as *jQuery*.You'll install more than you need for QuickStart.
|
||||
Consider using [nvm](https://github.com/creationix/nvm) for managing multiple
|
||||
versions of node and npm. You may need [nvm](https://github.com/creationix/nvm) if
|
||||
you already have projects running on your machine that use other versions of node and npm.
|
||||
During [Setup](guide/setup), a <a href="https://docs.npmjs.com/files/package.json" target="_blank">package.json</a>
|
||||
file is installed with a comprehensive starter set of
|
||||
packages as specified in the `dependencies` and `devDependencies` sections.
|
||||
|
||||
You can use other packages but the packages in _this particular set_ work well together and include
|
||||
everything you need to build and run the sample applications in this series.
|
||||
|
||||
Note: A cookbook or guide page may require an additional library such as *jQuery*.You'll install more than you need for the QuickStart guide.
|
||||
No worries!
|
||||
You only serve to the client those packages that the application actually requests.
|
||||
|
||||
@ -29,7 +34,7 @@ This page explains what each package does. You can make substitutions later to s
|
||||
|
||||
## *dependencies* and *devDependencies*
|
||||
The `package.json` includes two sets of packages,
|
||||
[dependencies](#dependencies) and [devDependencies](#dev-dependencies).
|
||||
[dependencies](guide/npm-packages#dependencies) and [devDependencies](guide/npm-packages#dev-dependencies).
|
||||
|
||||
The *dependencies* are essential to *running* the application.
|
||||
The *devDependencies* are only necessary to *develop* the application.
|
||||
@ -46,37 +51,40 @@ You can exclude them from production installations by adding `--production` to t
|
||||
## *dependencies*
|
||||
The `dependencies` section of `package.json` contains:
|
||||
|
||||
* ***Features*** - Feature packages give the application framework and utility capabilities.
|
||||
* ***Features***: Feature packages give the application framework and utility capabilities.
|
||||
|
||||
* ***Polyfills*** - Polyfills plug gaps in the browser's JavaScript implementation.
|
||||
* ***Polyfills***: Polyfills plug gaps in the browser's JavaScript implementation.
|
||||
|
||||
* ***Other*** - Other libraries that support the application such as `bootstrap` for HTML widgets and styling.
|
||||
* ***Other***: Other libraries that support the application such as `bootstrap` for HTML widgets and styling.
|
||||
|
||||
### Feature Packages
|
||||
|
||||
***@angular/core*** - Critical runtime parts of the framework needed by every application.
|
||||
***@angular/core***: Critical runtime parts of the framework needed by every application.
|
||||
Includes all metadata decorators, `Component`, `Directive`, dependency injection, and the component lifecycle hooks.
|
||||
|
||||
***@angular/common*** - The commonly needed services, pipes, and directives provided by the Angular team.
|
||||
***@angular/common***: The commonly needed services, pipes, and directives provided by the Angular team.
|
||||
|
||||
***@angular/compiler*** - Angular's *Template Compiler*.
|
||||
***@angular/compiler***: Angular's *Template Compiler*.
|
||||
It understands templates and can convert them to code that makes the application run and render.
|
||||
Typically you don’t interact with the compiler directly; rather, you use it indirectly via `platform-browser-dynamic` or the offline template compiler.
|
||||
|
||||
***@angular/platform-browser*** - Everything DOM and browser related, especially the pieces that help render into DOM.
|
||||
This package also includes the bootstrapStatic method for bootstrapping applications for production builds that pre-compile templates offline.
|
||||
***@angular/platform-browser***: Everything DOM and browser related, especially
|
||||
the pieces that help render into the DOM.
|
||||
This package also includes the `bootstrapStatic()` method
|
||||
for bootstrapping applications for production builds that pre-compile templates offline.
|
||||
|
||||
***@angular/platform-browser-dynamic*** - Includes [Providers](../api/core/index/Provider-type-alias.html) and a [bootstrap](ngmodule.html#bootstrap) method for applications that
|
||||
***@angular/platform-browser-dynamic***: Includes [Providers](api/core/index/Provider-type-alias)
|
||||
and a [bootstrap](guide/ngmodule) method for applications that
|
||||
compile templates on the client. Don’t use offline compilation.
|
||||
Use this package for bootstrapping during development and for bootstrapping plunker samples.
|
||||
|
||||
***@angular/http*** - Angular's http client.
|
||||
***@angular/http***: Angular's HTTP client.
|
||||
|
||||
***@angular/router*** - Component router.
|
||||
***@angular/router***: Component router.
|
||||
|
||||
***@angular/upgrade*** - Set of utilities for upgrading AngularJS applications to Angular.
|
||||
***@angular/upgrade***: Set of utilities for upgrading AngularJS applications to Angular.
|
||||
|
||||
***[system.js](https://github.com/systemjs/systemjs)*** - A dynamic module loader compatible with the
|
||||
***[system.js](https://github.com/systemjs/systemjs)***: A dynamic module loader compatible with the
|
||||
[ES2015 module](http://www.2ality.com/2014/09/es6-modules-final.html) specification.
|
||||
Other viable choices include the well-regarded [webpack](https://webpack.github.io/).
|
||||
|
||||
@ -94,16 +102,16 @@ Install these polyfills using the npm packages that Angular lists in the *peerDe
|
||||
|
||||
You must list these packages in the `dependencies` section of your own `package.json`.
|
||||
|
||||
For background on this requirement, see [Why peerDependencies?](#why-peer-dependencies).***core-js*** - Patches the global context (window) with essential features of ES2015 (ES6).
|
||||
For background on this requirement, see [Why peerDependencies?](guide/npm-packages#why-peer-dependencies).***core-js***: Patches the global context (window) with essential features of ES2015 (ES6).
|
||||
You may substitute an alternative polyfill that provides the same core APIs.
|
||||
When these APIs are implemented by the major browsers, this dependency will become unnecessary.
|
||||
|
||||
***rxjs*** - A polyfill for the [Observables specification](https://github.com/zenparsing/es-observable) currently before the
|
||||
***rxjs***: A polyfill for the [Observables specification](https://github.com/zenparsing/es-observable) currently before the
|
||||
[TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language.
|
||||
You can pick a preferred version of *rxjs* (within a compatible version range)
|
||||
without waiting for Angular updates.
|
||||
|
||||
***zone.js*** - A polyfill for the [Zone specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the
|
||||
***zone.js***: A polyfill for the [Zone specification](https://gist.github.com/mhevery/63fdcdf7c65886051d55) currently before the
|
||||
[TC39](http://www.ecma-international.org/memento/TC39.htm) committee that determines standards for the JavaScript language.
|
||||
You can pick a preferred version of *zone.js* to use (within a compatible version range)
|
||||
without waiting for Angular updates.
|
||||
@ -113,12 +121,12 @@ without waiting for Angular updates.
|
||||
|
||||
### Other helper libraries
|
||||
|
||||
***angular-in-memory-web-api*** - An Angular-supported library that simulates a remote server's web api
|
||||
without requiring an actual server or real http calls.
|
||||
Good for demos, samples, and early stage development (before we even have a server).
|
||||
Read about it in the [Http Client](server-communication.html#appendix-tour-of-heroes-in-memory-server) page.
|
||||
***angular-in-memory-web-api***: An Angular-supported library that simulates a remote server's web api
|
||||
without requiring an actual server or real HTTP calls.
|
||||
Good for demos, samples, and early stage development (before you even have a server).
|
||||
Read about it in the [HTTP Client](guide/server-communication) page.
|
||||
|
||||
***bootstrap*** - [Bootstrap](http://getbootstrap.com/) is a popular HTML and CSS framework for designing responsive web apps.
|
||||
***bootstrap***: [Bootstrap](http://getbootstrap.com/) is a popular HTML and CSS framework for designing responsive web apps.
|
||||
Some of the samples improve their appearance with *bootstrap*.
|
||||
|
||||
|
||||
@ -128,33 +136,32 @@ Some of the samples improve their appearance with *bootstrap*.
|
||||
The packages listed in the *devDependencies* section of the `package.json` help you develop the application.
|
||||
You don't have to deploy them with the production application although there is no harm in doing so.
|
||||
|
||||
***[concurrently](https://www.npmjs.com/package/concurrently)*** -
|
||||
***[concurrently](https://www.npmjs.com/package/concurrently)***:
|
||||
A utility to run multiple *npm* commands concurrently on OS/X, Windows, and Linux operating systems.
|
||||
|
||||
***[lite-server](https://www.npmjs.com/package/lite-server)*** -
|
||||
***[lite-server](https://www.npmjs.com/package/lite-server)***:
|
||||
A light-weight, static file server, by [John Papa](http://johnpapa.net/)
|
||||
with excellent support for Angular apps that use routing.
|
||||
|
||||
***[typescript](https://www.npmjs.com/package/typescript)*** -
|
||||
***[typescript](https://www.npmjs.com/package/typescript)***:
|
||||
the TypeScript language server, including the *tsc* TypeScript compiler.
|
||||
|
||||
***@types/\**** - TypeScript definition files.
|
||||
Learn more about it in the [TypeScript Configuration](typescript-configuration.html#typings) chapter.
|
||||
***@types/\****: TypeScript definition files.
|
||||
Learn more about it in the [TypeScript Configuration](guide/typescript-configuration) guide.
|
||||
|
||||
|
||||
|
||||
{@a why-peer-dependencies}
|
||||
## Why *peerDependencies*?
|
||||
|
||||
There isn't a *peerDependencies* section in the QuickStart `package.json`.
|
||||
There isn't a [*peerDependencies*](https://nodejs.org/en/blog/npm/peer-dependencies/) section in the QuickStart `package.json`.
|
||||
But Angular has a *peerDependencies* section in
|
||||
*its* package.json, which has important consequences for your application.
|
||||
*its* `package.json`, which has important consequences for your application.
|
||||
|
||||
It explains why you load the [polyfill](#polyfills) *dependency* packages in the QuickStart `package.json`,
|
||||
This section explains why you load the [polyfill](guide/npm-packages#polyfills) *dependency*
|
||||
packages in the QuickStart application's `package.json`,
|
||||
and why you'll need those packages in your own applications.
|
||||
|
||||
An explanation of [peer dependencies](https://nodejs.org/en/blog/npm/peer-dependencies/) follows.
|
||||
|
||||
Packages depend on other packages. For example, your application depends on the Angular package.
|
||||
|
||||
Two packages, "A" and "B", could depend on the same third package "C".
|
||||
@ -178,7 +185,7 @@ The difference between a `dependency` and a `peerDependency` is roughly this:
|
||||
The Angular `package.json` specifies several *peer dependency* packages,
|
||||
each pinned to a particular version of a third-party package.
|
||||
|
||||
### We must install Angular's *peerDependencies* ourselves.
|
||||
### You must install Angular's *peerDependencies* yourself.
|
||||
|
||||
When *npm* installs packages listed in *your* `dependencies` section,
|
||||
it also installs the packages listed within *their* packages `dependencies` sections.
|
||||
|
@ -12,23 +12,23 @@ This guide explains reactive forms as you follow the steps to build a "Hero Deta
|
||||
{@a toc}
|
||||
## Contents
|
||||
|
||||
- [Introduction to reactive forms](#intro)
|
||||
- [Setup](#setup)
|
||||
- [Create a data model](#data-model)
|
||||
- [Create a _reactive forms_ component](#create-component)
|
||||
- [Create its template file](#create-template)
|
||||
- [Import the _ReactiveFormsModule_](#import)
|
||||
- [Display the _HeroDetailComponent_](#update)
|
||||
- [Add a FormGroup](#formgroup)
|
||||
- [Taking a look at the form model](#json)
|
||||
- [Introduction to _FormBuilder_](#formbuilder)
|
||||
- [Validators.required](#validators)
|
||||
- [Nested FormGroups](#grouping)
|
||||
- [Inspect _FormControl_ properties](#properties)
|
||||
- [Set form model data using _setValue_ and _patchValue_](#set-data)
|
||||
- [Use _FormArray_ to present an array of _FormGroups_](#form-array)
|
||||
- [Observe control changes](#observe-control)
|
||||
- [Save form data](#save)
|
||||
- [Introduction to reactive forms](guide/reactive-forms#intro)
|
||||
- [Setup](guide/reactive-forms#setup)
|
||||
- [Create a data model](guide/reactive-forms#data-model)
|
||||
- [Create a _reactive forms_ component](guide/reactive-forms#create-component)
|
||||
- [Create its template file](guide/reactive-forms#create-template)
|
||||
- [Import the _ReactiveFormsModule_](guide/reactive-forms#import)
|
||||
- [Display the _HeroDetailComponent_](guide/reactive-forms#update)
|
||||
- [Add a FormGroup](guide/reactive-forms#formgroup)
|
||||
- [Taking a look at the form model](guide/reactive-forms#json)
|
||||
- [Introduction to _FormBuilder_](guide/reactive-forms#formbuilder)
|
||||
- [Validators.required](guide/reactive-forms#validators)
|
||||
- [Nested FormGroups](guide/reactive-forms#grouping)
|
||||
- [Inspect _FormControl_ properties](guide/reactive-forms#properties)
|
||||
- [Set form model data using _setValue_ and _patchValue_](guide/reactive-forms#set-data)
|
||||
- [Use _FormArray_ to present an array of _FormGroups_](guide/reactive-forms#form-array)
|
||||
- [Observe control changes](guide/reactive-forms#observe-control)
|
||||
- [Save form data](guide/reactive-forms#save)
|
||||
|
||||
Try the <live-example plnkr="final" title="Reactive Forms (final) in Plunker">Reactive Forms live-example</live-example>.
|
||||
|
||||
@ -65,7 +65,7 @@ the form controls and pull user-changed values back out. The component can
|
||||
observe changes in form control state and react to those changes.
|
||||
|
||||
One advantage of working with form control objects directly is that value and validity updates
|
||||
are [always synchronous and under your control](#async-vs-sync "Async vs sync").
|
||||
are [always synchronous and under your control](guide/reactive-forms#async-vs-sync "Async vs sync").
|
||||
You won't encounter the timing issues that sometimes plague a template-driven form
|
||||
and reactive forms can be easier to unit test.
|
||||
|
||||
@ -82,7 +82,7 @@ but it does facilitate the reactive programming approach should you choose to us
|
||||
|
||||
### _Template-driven_ forms
|
||||
|
||||
_Template-driven_ forms, introduced in the [Template guide](forms.html), take a completely different approach.
|
||||
_Template-driven_ forms, introduced in the [Template guide](guide/forms), take a completely different approach.
|
||||
|
||||
You place HTML form controls (such as `<input>` and `<select>`) in the component template and
|
||||
bind them to _data model_ properties in the component, using directives
|
||||
@ -96,7 +96,7 @@ Angular updates the mutable _data model_ with user changes as they happen.
|
||||
For this reason, the `ngModel` directive is not part of the ReactiveFormsModule.
|
||||
|
||||
While this means less code in the component class,
|
||||
[template-driven forms are asynchronous](#async-vs-sync "Async vs sync")
|
||||
[template-driven forms are asynchronous](guide/reactive-forms#async-vs-sync "Async vs sync")
|
||||
which may complicate development in more advanced scenarios.
|
||||
|
||||
|
||||
@ -116,7 +116,7 @@ That means you must wait a tick before manipulating any of the controls
|
||||
from within the component class.
|
||||
|
||||
For example, if you inject the form control with a `@ViewChild(NgForm)` query and examine it in the
|
||||
[`ngAfterViewInit` lifecycle hook](lifecycle-hooks.html#afterview "Lifecycle hooks guide: AfterView"),
|
||||
[`ngAfterViewInit` lifecycle hook](guide/lifecycle-hooks),
|
||||
you'll discover that it has no children.
|
||||
You must wait a tick, using `setTimeout`, before you can
|
||||
extract a value from a control, test its validity, or set it to a new value.
|
||||
@ -136,17 +136,17 @@ You may decide to use both in the same application.
|
||||
|
||||
The balance of this _reactive forms_ guide explores the _reactive_ paradigm and
|
||||
concentrates exclusively on reactive forms techniques.
|
||||
For information on _template-driven forms_, see the [_Forms_](forms.html) guide.
|
||||
For information on _template-driven forms_, see the [_Forms_](guide/forms) guide.
|
||||
|
||||
In the next section, you'll set up your project for the reactive form demo.
|
||||
Then you'll learn about the [Angular form classes](#essentials) and how to use them in a reactive form.
|
||||
Then you'll learn about the [Angular form classes](guide/reactive-forms#essentials) and how to use them in a reactive form.
|
||||
|
||||
|
||||
|
||||
{@a setup}
|
||||
## Setup
|
||||
|
||||
Follow the steps in the [_Setup_ guide](../setup.html "Setup guide")
|
||||
Follow the steps in the [_Setup_ guide](setup)
|
||||
for creating a new project folder (perhaps called `reactive-forms`)
|
||||
based on the _QuickStart seed_.
|
||||
|
||||
@ -181,7 +181,7 @@ Now enter the `@Component` decorator that specifies the `HeroDetailComponent` me
|
||||
{@example 'reactive-forms/ts/src/app/hero-detail.component.ts' region='metadata'}
|
||||
|
||||
The `moduleId: module.id` lets you use
|
||||
[component-relative paths](../cookbook/component-relative-paths.html) in file URLs
|
||||
[component-relative paths](cookbook/component-relative-paths) in file URLs
|
||||
such as when specifying the `templateUrl`.
|
||||
|
||||
Next, create an exported `HeroDetailComponent` class with a `FormControl`.
|
||||
@ -202,7 +202,7 @@ This simple control doesn't have data or validators.
|
||||
In real apps, most form controls have both.
|
||||
|
||||
This guide touches only briefly on `Validators`. For an in-depth look at them,
|
||||
read the [Form Validation](../cookbook/form-validation.html) cookbook.
|
||||
read the [Form Validation](cookbook/form-validation) cookbook.
|
||||
|
||||
|
||||
|
||||
@ -256,21 +256,21 @@ Revise the `AppComponent` template so it displays the `HeroDetailComponent`.
|
||||
### Essential form classes
|
||||
It may be helpful to read a brief description of the core form classes.
|
||||
|
||||
* [_AbstractControl_](../api/forms/index/AbstractControl-class.html "API Reference: AbstractControl")
|
||||
* [_AbstractControl_](api/forms/index/AbstractControl-class)
|
||||
is the abstract base class for the three concrete form control classes:
|
||||
`FormControl`, `FormGroup`, and `FormArray`.
|
||||
It provides their common behaviors and properties, some of which are _observable_.
|
||||
|
||||
* [_FormControl_](../api/forms/index/FormControl-class.html "API Reference: FormControl")
|
||||
* [_FormControl_](api/forms/index/FormControl-class)
|
||||
tracks the value and validity status of an _individual_ form control.
|
||||
It corresponds to an HTML form control such as an input box or selector.
|
||||
|
||||
* [_FormGroup_](../api/forms/index/FormGroup-class.html "API Reference: FormGroup")
|
||||
* [_FormGroup_](api/forms/index/FormGroup-class)
|
||||
tracks the value and validity state of a _group_ of `AbstractControl` instances.
|
||||
The group's properties include its child controls.
|
||||
The top-level form in your component is a `FormGroup`.
|
||||
|
||||
* [_FormArray_](../api/forms/index/FormArray-class.html "API Reference: FormArray")
|
||||
* [_FormArray_](api/forms/index/FormArray-class)
|
||||
tracks the value and validity state of a numerically indexed _array_ of `AbstractControl` instances.
|
||||
|
||||
You'll learn more about these classes as you work through this guide.
|
||||
@ -440,7 +440,7 @@ Using `Validators.required` is optional for the rest of the guide.
|
||||
It remains in each of the following examples with the same configuration.
|
||||
|
||||
For more on validating Angular forms, see the
|
||||
[Form Validation](../cookbook/form-validation.html) guide.
|
||||
[Form Validation](cookbook/form-validation) guide.
|
||||
### More FormControls
|
||||
A hero has more than a name.
|
||||
A hero has an address, a super power and sometimes a sidekick too.
|
||||
@ -487,9 +487,9 @@ You tie these controls to the template HTML elements in the same way,
|
||||
specifiying the `FormControl` name with the `formControlName` directive.
|
||||
|
||||
See the API reference for more information about
|
||||
[radio buttons](../api/forms/index/RadioControlValueAccessor-directive.html "API: RadioControlValueAccessor"),
|
||||
[selects](../api/forms/index/SelectControlValueAccessor-directive.html "API: SelectControlValueAccessor"), and
|
||||
[checkboxes](../api/forms/index/CheckboxControlValueAccessor-directive.html "API: CheckboxControlValueAccessor").
|
||||
[radio buttons](api/forms/index/RadioControlValueAccessor-directive),
|
||||
[selects](api/forms/index/SelectControlValueAccessor-directive), and
|
||||
[checkboxes](api/forms/index/CheckboxControlValueAccessor-directive).
|
||||
|
||||
|
||||
|
||||
@ -654,12 +654,12 @@ such as one of the following:
|
||||
</table>
|
||||
|
||||
Learn about other `FormControl` properties in the
|
||||
[_AbstractControl_](../api/forms/index/AbstractControl-class.html) API reference.
|
||||
[_AbstractControl_](api/forms/index/AbstractControl-class) API reference.
|
||||
|
||||
One common reason for inspecting `FormControl` properties is to
|
||||
make sure the user entered valid values.
|
||||
Read more about validating Angular forms in the
|
||||
[Form Validation](../cookbook/form-validation.html) guide.
|
||||
[Form Validation](cookbook/form-validation) guide.
|
||||
|
||||
|
||||
|
||||
@ -704,7 +704,7 @@ There are two significant differences between these models:
|
||||
1. The `Hero` has an `id`. The form model does not because you generally don't show primary keys to users.
|
||||
|
||||
1. The `Hero` has an array of addresses. This form model presents only one address,
|
||||
a choice [revisited below](#form-array "Form arrays").
|
||||
a choice [revisited below](guide/reactive-forms#form-array "Form arrays").
|
||||
|
||||
Nonetheless, the two models are pretty close in shape and you'll see in a moment how this alignment facilitates copying the _data model_ properties
|
||||
to the _form model_ with the `patchValue` and `setValue` methods.
|
||||
@ -767,7 +767,7 @@ values and does not throw helpful errors.
|
||||
Now you know _how_ to set the _form model_ values. But _when_ do you set them?
|
||||
The answer depends upon when the component gets the _data model_ values.
|
||||
|
||||
The `HeroDetailComponent` in this reactive forms sample is nested within a _master/detail_ `HeroListComponent` ([discussed below](#hero-list)).
|
||||
The `HeroDetailComponent` in this reactive forms sample is nested within a _master/detail_ `HeroListComponent` ([discussed below](guide/reactive-forms#hero-list)).
|
||||
The `HeroListComponent` displays hero names to the user.
|
||||
When the user clicks on a hero, the list component passes the selected hero into the `HeroDetailComponent`
|
||||
by binding to its `hero` input property.
|
||||
@ -777,7 +777,7 @@ by binding to its `hero` input property.
|
||||
|
||||
In this approach, the value of `hero` in the `HeroDetailComponent` changes
|
||||
every time the user selects a new hero.
|
||||
You should call _setValue_ in the [ngOnChanges](lifecyle-hooks.html#onchanges)
|
||||
You should call _setValue_ in the [ngOnChanges](guide/lifecyle-hooks)
|
||||
hook, which Angular calls whenever the input `hero` property changes
|
||||
as the following steps demonstrate.
|
||||
|
||||
@ -839,11 +839,11 @@ A "Refresh" button clears the hero list and the current selected hero before ref
|
||||
|
||||
The remaining `HeroListComponent` and `HeroService` implementation details are not relevant to understanding reactive forms.
|
||||
The techniques involved are covered elsewhere in the documentation, including the _Tour of Heroes_
|
||||
[here](../tutorial/toh-pt3.html "ToH: Multiple Components") and [here](../tutorial/toh-pt4.html "ToH: Services").
|
||||
[here](tutorial/toh-pt3) and [here](tutorial/toh-pt4).
|
||||
|
||||
If you're coding along with the steps in this reactive forms tutorial,
|
||||
create the pertinent files based on the
|
||||
[source code displayed below](#source-code "Reactive Forms source code").
|
||||
[source code displayed below](guide/reactive-forms#source-code "Reactive Forms source code").
|
||||
Notice that `hero-list.component.ts` imports `Observable` and `finally` while `hero.service.ts` imports `Observable`, `of`,
|
||||
and `delay` from `rxjs`.
|
||||
Then return here to learn about _form array_ properties.
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
Security
|
||||
|
||||
@intro
|
||||
Developing for content security in Angular applications
|
||||
Developing for content security in Angular applications.
|
||||
|
||||
@description
|
||||
This page describes Angular's built-in
|
||||
@ -18,7 +18,7 @@ You can run the <live-example></live-example> in Plunker and download the code f
|
||||
Reporting vulnerabilities
|
||||
</h2>
|
||||
|
||||
To report vulnerabilities in Angular itself, email us at [security@angular.io](mailto:security@angular.io).
|
||||
To report vulnerabilities in Angular itself, email us at [security@angular.io](guide/mailto:security@angular).
|
||||
|
||||
For more information about how Google handles security issues, see [Google's security
|
||||
philosophy](https://www.google.com/about/appsecurity/).
|
||||
@ -39,7 +39,7 @@ important security fixes and enhancements. Instead, share your Angular improveme
|
||||
community and make a pull request.
|
||||
|
||||
* **Avoid Angular APIs marked in the documentation as “_Security Risk_.”**
|
||||
For more information, see the [Trusting safe values](#bypass-security-apis) section of this page.
|
||||
For more information, see the [Trusting safe values](guide/security#bypass-security-apis) section of this page.
|
||||
|
||||
|
||||
<h2 id='xss'>
|
||||
@ -68,7 +68,7 @@ _Angular templates are the same as executable code_: HTML, attributes, and bindi
|
||||
prevent values that an attacker can control from ever making it into the source code of a
|
||||
template. Never generate template source code by concatenating user input and templates.
|
||||
To prevent these vulnerabilities, use
|
||||
the [offline template compiler](#offline-template-compiler), also known as _template injection_.
|
||||
the [offline template compiler](guide/security#offline-template-compiler), also known as _template injection_.
|
||||
|
||||
### Sanitization and security contexts
|
||||
|
||||
@ -124,7 +124,7 @@ and greatly improves application performance. Use the offline template compiler
|
||||
deployments; don't dynamically generate templates. Angular trusts template code, so generating
|
||||
templates, in particular templates containing user data, circumvents Angular's built-in protections.
|
||||
For information about dynamically constructing forms in a safe way, see the
|
||||
[Dynamic Forms](../cookbook/dynamic-form.html) cookbook page.
|
||||
[Dynamic Forms](cookbook/dynamic-form) cookbook page.
|
||||
|
||||
### Server-side XSS protection
|
||||
|
||||
@ -142,5 +142,5 @@ carries a high risk of introducing template-injection vulnerabilities.
|
||||
|
||||
Angular applications must follow the same security principles as regular web applications, and
|
||||
must be audited as such. Angular-specific APIs that should be audited in a security review,
|
||||
such as the [_bypassSecurityTrust_](#bypass-security-apis) methods, are marked in the documentation
|
||||
such as the [_bypassSecurityTrust_](guide/security#bypass-security-apis) methods, are marked in the documentation
|
||||
as security sensitive.
|
@ -13,27 +13,50 @@ it isn't covered in this page.Modern browsers support two HTTP-based APIs:
|
||||
[Fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API).
|
||||
|
||||
The !{_Angular_http_library} simplifies application programming with the **XHR** and **JSONP** APIs.
|
||||
This page covers:
|
||||
|
||||
- [The Tour of Heroes *HTTP* client demo](#http-client).
|
||||
- [Fetch data with http.get](#fetch-data).
|
||||
<li if-docs="ts"> [RxJS library](#rxjs).</li>
|
||||
<li if-docs="ts"> [Enable RxJS operators](#enable-rxjs-operators).</li>
|
||||
- [Process the response object](#extract-data).
|
||||
- [Always handle errors](#error-handling).
|
||||
- [Send data to the server](#update).
|
||||
<li if-docs="ts"> [Fall back to promises](#promises).</li>
|
||||
- [Cross-Origin Requests: Wikipedia example](#cors).
|
||||
<ul if-docs="ts">
|
||||
<li> [Search parameters](#search-parameters).</li>
|
||||
<li> [More fun with observables](#more-observables).</li>
|
||||
# Contents
|
||||
* [Demos](guide/server-communication#demos)
|
||||
* [Providing HTTP Services](guide/server-communication#http-providers)
|
||||
* [The Tour of Heroes *HTTP* client demo](guide/server-communication#http-client)
|
||||
- [The `HeroListComponent` class](guide/server-communication#HeroListComponent)
|
||||
* [Fetch data with `http.get()`](guide/server-communication#fetch-data)
|
||||
<li if-docs="ts"> [RxJS library](guide/server-communication#rxjs-library)
|
||||
<ul>
|
||||
<li> [Enable RxJS operators](guide/server-communication#enable-rxjs-operators)</li>
|
||||
</ul>
|
||||
- [Guarding against Cross-Site Request Forgery](#xsrf).
|
||||
- [Override default request headers (and other request options)](#override-default-request-options).
|
||||
- [Appendix: Tour of Heroes _in-memory web api_](#in-mem-web-api).
|
||||
</li>
|
||||
* [Process the response object](guide/server-communication#extract-data)
|
||||
- [Parse to `JSON`](guide/server-communication#parse-to-json)
|
||||
- [Do not return the response object](guide/server-communication#no-return-response-object)
|
||||
- [Always handle errors](guide/server-communication#error-handling)
|
||||
- [`HeroListComponent` error handling](guide/server-communication#hero-list-component)
|
||||
* [Send data to the server](guide/server-communication#update)
|
||||
- [Headers](guide/server-communication#headers)
|
||||
- [JSON results](guide/server-communication#json-results)
|
||||
|
||||
<ul><li if-docs="ts"> [Fall back to promises](guide/server-communication#promises)</ul>
|
||||
|
||||
* [Cross-Origin Requests: Wikipedia example](guide/server-communication#cors)
|
||||
<ul if-docs="ts">
|
||||
<li> [Search Wikipedia](guide/server-communication#search-wikipedia)</li>
|
||||
<li> [Search parameters](guide/server-communication#search-parameters)</li>
|
||||
<li> [The WikiComponent](guide/server-communication#wikicomponent)</li>
|
||||
</ul>
|
||||
* [A wasteful app](guide/server-communication#wasteful-app)
|
||||
<li if-docs="ts"> [More fun with Observables](guide/server-communication#more-observables)
|
||||
<ul>
|
||||
<li> [Create a stream of search terms](guide/server-communication#create-stream)</li>
|
||||
<li> [Listen for search terms](guide/server-communication#listen-for-search-terms)</li>
|
||||
</ul>
|
||||
</li>
|
||||
* [Guarding against Cross-Site Request Forgery](guide/server-communication#xsrf)
|
||||
* [Override default request headers (and other request options)](guide/server-communication#override-default-request-options)
|
||||
* [Appendix: Tour of Heroes _in-memory web api_](guide/server-communication#in-mem-web-api)
|
||||
|
||||
A <live-example>live example</live-example> illustrates these topics.
|
||||
|
||||
|
||||
{@a demos}
|
||||
|
||||
# Demos
|
||||
|
||||
This page describes server communication with the help of the following demos:
|
||||
@ -50,7 +73,7 @@ The !{_Angular_Http} client communicates with the server using a familiar HTTP r
|
||||
The `!{_Http}` client is one of a family of services in the !{_Angular_http_library}.
|
||||
Before you can use the `!{_Http}` client, you need to register it as a service provider with the dependency injection system.
|
||||
|
||||
Read about providers in the [Dependency Injection](dependency-injection.html) page.
|
||||
Read about providers in the [Dependency Injection](guide/dependency-injection) page.
|
||||
Register providers by importing other NgModules to the root NgModule in `app.module.ts`.
|
||||
|
||||
|
||||
@ -58,12 +81,12 @@ Register providers by importing other NgModules to the root NgModule in `app.mod
|
||||
|
||||
|
||||
The `HttpModule` is necessary for making HTTP calls.
|
||||
Though the JsonpModule isn't necessary for plain HTTP,
|
||||
Though the `JsonpModule` isn't necessary for plain HTTP,
|
||||
there is a JSONP demo later in this page.
|
||||
Loading its module now saves time.
|
||||
## The Tour of Heroes HTTP client demo
|
||||
|
||||
The first demo is a mini-version of the [tutorial](../tutorial)'s "Tour of Heroes" (ToH) application.
|
||||
The first demo is a mini-version of the [tutorial](tutorial)'s "Tour of Heroes" (ToH) application.
|
||||
This version gets some heroes from the server, displays them in a list, lets the user add new heroes, and saves them to the server.
|
||||
The app uses the !{_Angular_Http} client to communicate via `XMLHttpRequest (XHR)`.
|
||||
|
||||
@ -79,7 +102,7 @@ This demo has a single component, the `HeroListComponent`. Here's its template:
|
||||
It presents the list of heroes with an `ngFor`.
|
||||
Below the list is an input box and an *Add Hero* button where you can enter the names of new heroes
|
||||
and add them to the database.
|
||||
A [template reference variable](template-syntax.html#ref-vars), `newHeroName`, accesses the
|
||||
A [template reference variable](guide/template-syntax), `newHeroName`, accesses the
|
||||
value of the input box in the `(click)` event binding.
|
||||
When the user clicks the button, that value passes to the component's `addHero` method and then
|
||||
the event binding clears it to make it ready for a new hero name.
|
||||
@ -96,7 +119,7 @@ Here's the component class:
|
||||
|
||||
{@example 'server-communication/ts/src/app/toh/hero-list.component.ts' region='component'}
|
||||
|
||||
Angular [injects](dependency-injection.html) a `HeroService` into the constructor
|
||||
Angular [injects](guide/dependency-injection) a `HeroService` into the constructor
|
||||
and the component calls that service to fetch and save data.
|
||||
|
||||
The component **does not talk directly to the !{_Angular_Http} client**.
|
||||
@ -107,7 +130,7 @@ This is a golden rule: **always delegate data access to a supporting service cla
|
||||
|
||||
Although _at runtime_ the component requests heroes immediately after creation,
|
||||
you **don't** call the service's `get` method in the component's constructor.
|
||||
Instead, call it inside the `ngOnInit` [lifecycle hook](lifecycle-hooks.html)
|
||||
Instead, call it inside the `ngOnInit` [lifecycle hook](guide/lifecycle-hooks)
|
||||
and rely on Angular to call `ngOnInit` when it instantiates this component.
|
||||
This is a *best practice*.
|
||||
Components are easier to test and debug when their constructors are simple, and all real work
|
||||
@ -116,7 +139,7 @@ Components are easier to test and debug when their constructors are simple, and
|
||||
|
||||
{@a HeroService}
|
||||
|
||||
## Fetch data with http.get
|
||||
## Fetch data with _http.get()_
|
||||
|
||||
In many of the previous samples the app faked the interaction with the server by
|
||||
returning mock heroes in a service like this one:
|
||||
@ -128,7 +151,7 @@ You can revise that `HeroService` to get the heroes from the server using the !{
|
||||
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='v1'}
|
||||
|
||||
Notice that the !{_Angular_Http} client service is
|
||||
[injected](dependency-injection.html) into the `HeroService` constructor.
|
||||
[injected](guide/dependency-injection) into the `HeroService` constructor.
|
||||
|
||||
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='ctor'}
|
||||
|
||||
@ -138,7 +161,7 @@ Look closely at how to call `!{_priv}http.get`:
|
||||
|
||||
You pass the resource URL to `get` and it calls the server which returns heroes.
|
||||
|
||||
The server returns heroes once you've set up the [in-memory web api](#in-mem-web-api)
|
||||
The server returns heroes once you've set up the [in-memory web api](guide/server-communication#in-mem-web-api)
|
||||
described in the appendix below.
|
||||
Alternatively, you can temporarily target a JSON file by changing the endpoint URL:
|
||||
|
||||
@ -149,13 +172,15 @@ Alternatively, you can temporarily target a JSON file by changing the endpoint U
|
||||
|
||||
{@a extract-data}
|
||||
## Process the response object
|
||||
Remember that the `getHeroes()` method used an `!{_priv}extractData` helper method to map the `!{_priv}http.get` response object to heroes:
|
||||
Remember that the `getHeroes()` method used an `!{_priv}extractData()` helper method to map the `!{_priv}http.get` response object to heroes:
|
||||
|
||||
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='extract-data'}
|
||||
|
||||
The `response` object doesn't hold the data in a form the app can use directly.
|
||||
You must parse the response data into a JSON object.
|
||||
|
||||
|
||||
{@a parse-to-json}
|
||||
### Parse to JSON
|
||||
Don't expect the decoded JSON to be the heroes !{_array} directly.
|
||||
This server always wraps JSON results in an object with a `data`
|
||||
@ -169,8 +194,12 @@ This is conventional web API behavior, driven by
|
||||
Make no assumptions about the server API.
|
||||
Not all servers return an object with a `data` property.
|
||||
|
||||
|
||||
~~~
|
||||
|
||||
|
||||
|
||||
{@a no-return-response-object}
|
||||
### Do not return the response object
|
||||
The `getHeroes()` method _could_ have returned the HTTP response but this wouldn't
|
||||
be a best practice.
|
||||
@ -225,7 +254,7 @@ just the name of a new hero and returns an `Observable` of `Hero`. It begins lik
|
||||
|
||||
To implement it, you must know the server's API for creating heroes.
|
||||
|
||||
[This sample's data server](#server) follows typical REST guidelines.
|
||||
[This sample's data server](guide/server-communication#in-mem-web-api) follows typical REST guidelines.
|
||||
It expects a [`POST`](http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5) request
|
||||
at the same endpoint as `GET` heroes.
|
||||
It expects the new hero data to arrive in the body of the request,
|
||||
@ -240,17 +269,23 @@ The server generates the `id` and returns the entire `JSON` representation
|
||||
of the new hero including its generated id. The hero arrives tucked inside a response object
|
||||
with its own `data` property.
|
||||
|
||||
Now that you know how the API works, implement `addHero()`as follows:
|
||||
Now that you know how the API works, implement `addHero()` as follows:
|
||||
|
||||
|
||||
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero'}
|
||||
|
||||
|
||||
|
||||
{@a headers}
|
||||
### Headers
|
||||
|
||||
In the `headers` object, the `Content-Type` specifies that the body represents JSON.
|
||||
|
||||
|
||||
{@a json-results}
|
||||
### JSON results
|
||||
|
||||
As with `getHeroes()`, use the `!{_priv}extractData()` helper to [extract the data](#extract-data)
|
||||
As with `getHeroes()`, use the `!{_priv}extractData()` helper to [extract the data](guide/server-communication#extract-data)
|
||||
from the response.
|
||||
|
||||
|
||||
@ -270,10 +305,13 @@ This is called the [same-origin policy](https://en.wikipedia.org/wiki/Same-origi
|
||||
|
||||
Modern browsers do allow `XHR` requests to servers from a different origin if the server supports the
|
||||
[CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) protocol.
|
||||
If the server requires user credentials, you'll enable them in the [request headers](#headers).
|
||||
If the server requires user credentials, enable them in the [request headers](guide/server-communication#headers).
|
||||
Some servers do not support CORS but do support an older, read-only alternative called [JSONP](https://en.wikipedia.org/wiki/JSONP).
|
||||
Wikipedia is one such server.
|
||||
This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.### Search wikipedia
|
||||
This [Stack Overflow answer](http://stackoverflow.com/questions/2067472/what-is-jsonp-all-about/2067584#2067584) covers many details of JSONP.
|
||||
|
||||
{@a search-wikipedia}
|
||||
### Search Wikipedia
|
||||
|
||||
Here is a simple search that shows suggestions from Wikipedia as the user
|
||||
types in a text box:
|
||||
@ -289,7 +327,7 @@ types in a text box:
|
||||
## Guarding against Cross-Site Request Forgery
|
||||
|
||||
In a cross-site request forgery (CSRF or XSRF), an attacker tricks the user into visiting
|
||||
a different web page with malignant code that secretly sends a malicious request to your application's web server,
|
||||
a different web page with malignant code that secretly sends a malicious request to your application's web server.
|
||||
|
||||
The server and client application must work together to thwart this attack.
|
||||
Angular's `Http` client does its part by applying a default `CookieXSRFStrategy` automatically to all requests.
|
||||
@ -299,7 +337,7 @@ generated authentication token in a cookie named `XSRF-TOKEN`.
|
||||
The HTTP client adds an `X-XSRF-TOKEN` header with that token value to subsequent requests.
|
||||
The server receives both the cookie and the header, compares them, and processes the request only if the cookie and header match.
|
||||
|
||||
See the [XSRF topic on the Security page](security.html#xsrf) for more information about XSRF and Angular's `XSRFStrategy` counter measures.
|
||||
See the [XSRF topic on the Security page](guide/security) for more information about XSRF and Angular's `XSRFStrategy` counter measures.
|
||||
|
||||
|
||||
{@a override-default-request-options}
|
||||
@ -311,7 +349,7 @@ Request options (such as headers) are merged into the
|
||||
before the request is processed.
|
||||
The `HttpModule` provides these default options via the `RequestOptions` token.
|
||||
|
||||
You can override these defaults to suit your application needs.
|
||||
You can override these defaults to suit your application needs
|
||||
by creating a custom sub-class of `RequestOptions`
|
||||
that sets the default options for the application.
|
||||
|
||||
@ -326,13 +364,13 @@ Then it registers the provider in the root `AppModule`.
|
||||
{@example 'server-communication/ts/src/app/app.module.ts' region='provide-default-request-options'}
|
||||
|
||||
|
||||
Remember to include this provider during setup when unit testing the app's HTTP services.After this change, the `header` option setting in `HeroService.addHero` is no longer necessary,
|
||||
Remember to include this provider during setup when unit testing the app's HTTP services.After this change, the `header` option setting in `HeroService.addHero()` is no longer necessary,
|
||||
|
||||
|
||||
{@example 'server-communication/ts/src/app/toh/hero.service.ts' region='addhero'}
|
||||
|
||||
You can confirm that `DefaultRequestOptions` is working by examing HTTP requests in the browser developer tools' network tab.
|
||||
If you're short-circuiting the server call with something like the [_in-memory web api_](#in-mem-web-api),
|
||||
If you're short-circuiting the server call with something like the [_in-memory web api_](guide/server-communication#in-mem-web-api),
|
||||
try commenting-out the `addHero` header option,
|
||||
set a breakpoint on the POST call, and step through the request processing
|
||||
to verify the header is there.
|
||||
|
@ -51,7 +51,7 @@ inside a Web Worker to improve your app's responsiveness by using multiple threa
|
||||
means that you could run your app inside Electron.js or Windows Universal to deliver it to the desktop.
|
||||
## Use the *Title* service
|
||||
Fortunately, Angular bridges the gap by providing a `Title` service as part of the *Browser platform*.
|
||||
The [Title](../api/platform-browser/index/Title-class.html) service is a simple class that provides an API
|
||||
The [Title](api/platform-browser/index/Title-class) service is a simple class that provides an API
|
||||
for getting and setting the current HTML document title:
|
||||
|
||||
* `getTitle() : string` — Gets the title of the current HTML document.
|
||||
@ -100,4 +100,4 @@ That's exactly what we're doing.
|
||||
The `Title` service is part of the Angular *browser platform*.
|
||||
If we bootstrap our application into a different platform,
|
||||
we'll have to provide a different `Title` service that understands the concept of a "document title" for that specific platform.
|
||||
Ideally the application itself neither knows nor cares about the runtime environment.[Back to top](#top)
|
||||
Ideally the application itself neither knows nor cares about the runtime environment.[Back to top](guide/set-document-title#top)
|
@ -2,7 +2,7 @@
|
||||
Setup for local development
|
||||
|
||||
@intro
|
||||
Install the Angular QuickStart seed for faster, more efficient development on your machine
|
||||
Install the Angular QuickStart seed for faster, more efficient development on your machine.
|
||||
|
||||
@description
|
||||
|
||||
@ -13,16 +13,16 @@ Install the Angular QuickStart seed for faster, more efficient development on yo
|
||||
<span if-docs="ts">
|
||||
The <live-example name=quickstart>QuickStart live-coding</live-example> example is an Angular _playground_.
|
||||
It's not where you'd develop a real application.
|
||||
You [should develop locally](#why-locally "Why develop locally") on your own machine ... and that's also how we think you should learn Angular.
|
||||
You [should develop locally](guide/setup#why-locally "Why develop locally") on your own machine ... and that's also how we think you should learn Angular.
|
||||
</span>
|
||||
|
||||
Setting up a new project on your machine is quick and easy with the **QuickStart seed**,
|
||||
maintained [on github](!{_qsRepo} "Install the github QuickStart repo").
|
||||
Make sure you have [!{_prereq} installed](#install-prerequisites "What if you don't have !{_prereq}?").
|
||||
maintained [on github](guide/!{_qsRepo} "Install the github QuickStart repo").
|
||||
Make sure you have [!{_prereq} installed](guide/setup#install-prerequisites "What if you don't have !{_prereq}?").
|
||||
Then ...
|
||||
1. Create a project folder (you can call it `quickstart` and rename it later).
|
||||
1. [Clone](#clone "Clone it from github") or [download](#download "download it from github") the **QuickStart seed** into your project folder.
|
||||
1. !{_Install} [!{_npm}](#install-prerequisites "What if you don't have !{_prereq}?") packages.
|
||||
1. [Clone](guide/setup#clone "Clone it from github") or [download](guide/setup#download "download it from github") the **QuickStart seed** into your project folder.
|
||||
1. !{_Install} [!{_npm}](guide/setup#install-prerequisites "What if you don't have !{_prereq}?") packages.
|
||||
1. Run `!{_npm} !{_start}` to launch the sample application.
|
||||
|
||||
|
||||
@ -180,7 +180,7 @@ The following are all in `src/`
|
||||
|
||||
|
||||
<td>
|
||||
Defines `AppModule`, the [root module](appmodule.html "AppModule: the root module") that tells Angular how to assemble the application.
|
||||
Defines `AppModule`, the [root module](guide/appmodule) that tells Angular how to assemble the application.
|
||||
Right now it declares only the `AppComponent`.
|
||||
Soon there will be more components to declare.
|
||||
</td>
|
||||
@ -197,12 +197,12 @@ The following are all in `src/`
|
||||
|
||||
|
||||
<td>
|
||||
Compiles the application with the [JIT compiler](../glossary.html#jit) and
|
||||
[bootstraps](appmodule.html#main "bootstrap the application")
|
||||
Compiles the application with the [JIT compiler](glossary) and
|
||||
[bootstraps](guide/appmodule)
|
||||
the application's main module (`AppModule`) to run in the browser.
|
||||
The JIT compiler is a reasonable choice during the development of most projects and
|
||||
it's the only viable choice for a sample running in a _live-coding_ environment like Plunker.
|
||||
You'll learn about alternative compiling and [deployment](deployment.html) options later in the documentation.
|
||||
You'll learn about alternative compiling and [deployment](guide/deployment) options later in the documentation.
|
||||
|
||||
</td>
|
||||
|
||||
@ -215,7 +215,7 @@ The following are all in `src/`
|
||||
|
||||
### Next Step
|
||||
|
||||
If you're new to Angular, we recommend staying on the [learning path](learning-angular.html "Angular learning path").
|
||||
If you're new to Angular, we recommend staying on the [learning path](guide/learning-angular).
|
||||
<br></br><br></br>
|
||||
|
||||
{@a install-prerequisites}
|
||||
|
@ -17,18 +17,18 @@ how you can write your own structural directives to do the same thing.
|
||||
|
||||
### Table of contents
|
||||
|
||||
- [What are structural directives?](#definition)
|
||||
- [*NgIf* case study](#ngIf)
|
||||
- [Group sibling elements with <ng-container>](#ng-container)
|
||||
- [The asterisk (\*) prefix](#asterisk)
|
||||
- [Inside *NgFor*](#ngfor)
|
||||
- [microsyntax](#microsyntax)
|
||||
- [template input variables](#template-input-variable)
|
||||
- [one structural directive per element](#one-per-element)
|
||||
- [Inside the *NgSwitch* directives](#ngSwitch)
|
||||
- [Prefer the (\*) prefix](#prefer-asterisk)
|
||||
- [The <template> element](#template)
|
||||
- [Write a structural directive](#unless)
|
||||
- [What are structural directives?](guide/structural-directives#definition)
|
||||
- [*NgIf* case study](guide/structural-directives#ngIf)
|
||||
- [The asterisk (*) prefix](guide/structural-directives#asterisk)
|
||||
- [Inside *NgFor*](guide/structural-directives#ngFor)
|
||||
- [microsyntax](guide/structural-directives#microsyntax)
|
||||
- [template input variables](guide/structural-directives#template-input-variable)
|
||||
- [one structural directive per element](guide/structural-directives#one-per-element)
|
||||
- [Inside the *NgSwitch* directives](guide/structural-directives#ngSwitch)
|
||||
- [Prefer the (*) prefix](guide/structural-directives#prefer-asterisk)
|
||||
- [The <template> element](guide/structural-directives#template)
|
||||
- [Group sibling elements with <ng-container>](guide/structural-directives#ng-container)
|
||||
- [Write a structural directive](guide/structural-directives#unless)
|
||||
|
||||
Try the <live-example></live-example>.
|
||||
|
||||
@ -45,28 +45,22 @@ As with other directives, you apply a structural directive to a _host element_.
|
||||
The directive then does whatever it's supposed to do with that host element and its descendents.
|
||||
|
||||
Structural directives are easy to recognize.
|
||||
An asterisk (\*) precedes the directive attribute name as in this example.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif'}
|
||||
|
||||
An asterisk (*) precedes the directive attribute name as in this example.
|
||||
No brackets. No parentheses. Just `*ngIf` set to a string.
|
||||
|
||||
You'll learn in this guide that the [asterisk (\*) is a convenience notation](#asterisk)
|
||||
and the string is a [_microsyntax_](#microsyntax) rather than the usual [template expression](template-syntax.html#template-expressions).
|
||||
Angular "de-sugars" this notation into a marked-up `<template>` that surrounds the
|
||||
You'll learn in this guide that the [asterisk (*) is a convenience notation](guide/structural-directives#asterisk)
|
||||
and the string is a [_microsyntax_](guide/structural-directives#microsyntax) rather than the usual
|
||||
[template expression](guide/template-syntax).
|
||||
Angular desugars this notation into a marked-up `<template>` that surrounds the
|
||||
host element and its descendents.
|
||||
Each structural directive does something different with that template.
|
||||
|
||||
Three of the common, built-in structural directives—[NgIf](template-syntax.html#ngIf),
|
||||
[NgFor](template-syntax.html#ngFor), and [NgSwitch...](template-syntax.html#ngSwitch)—are
|
||||
described in the [_Template Syntax_](template-syntax.html) guide and seen in samples throughout the Angular documentation.
|
||||
Three of the common, built-in structural directives—[NgIf](guide/template-syntax),
|
||||
[NgFor](guide/template-syntax), and [NgSwitch...](guide/template-syntax)—are
|
||||
described in the [_Template Syntax_](guide/template-syntax) guide and seen in samples throughout the Angular documentation.
|
||||
Here's an example of them in a template:
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='built-in'}
|
||||
|
||||
This guide won't repeat how to _use_ them. But it does explain _how they work_
|
||||
and how to [write your own](#unless) structural directive.
|
||||
and how to [write your own](guide/structural-directives#unless) structural directive.
|
||||
|
||||
|
||||
~~~ {.callout.is-helpful}
|
||||
@ -91,30 +85,27 @@ you apply the directive to an element in the HTML template.
|
||||
~~~
|
||||
|
||||
|
||||
There are two other kinds of Angular directives, described extensively elsewhere: (1) components and (2) attribute directives.
|
||||
There are two other kinds of Angular directives, described extensively elsewhere:
|
||||
(1) components and (2) attribute directives.
|
||||
|
||||
A *component* manages a region of HTML in the manner of a native HTML element.
|
||||
Technically it's a directive with a template.
|
||||
|
||||
An [*attribute* directive](attribute-directives.html) changes the appearance or behavior
|
||||
An [*attribute* directive](guide/attribute-directives) changes the appearance or behavior
|
||||
of an element, component, or another directive.
|
||||
For example, the built-in [`NgStyle`](template-syntax.html#ngStyle) directive
|
||||
For example, the built-in [`NgStyle`](guide/template-syntax) directive
|
||||
changes several element styles at the same time.
|
||||
|
||||
You can apply many _attribute_ directives to one host element.
|
||||
You can [only apply one](#one-per-element) _structural_ directive to a host element.
|
||||
You can [only apply one](guide/structural-directives#one-per-element) _structural_ directive to a host element.
|
||||
|
||||
|
||||
{@a ngIf}
|
||||
|
||||
## NgIf Case Study
|
||||
## NgIf case study
|
||||
|
||||
`NgIf` is the simplest structural directive and the easiest to understand.
|
||||
It takes a boolean value and makes an entire chunk of the DOM appear or disappear.
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif-true'}
|
||||
|
||||
It takes a boolean expression and makes an entire chunk of the DOM appear or disappear.
|
||||
The `ngIf` directive doesn't hide elements with CSS. It adds and removes them physically from the DOM.
|
||||
Confirm that fact using browser developer tools to inspect the DOM.
|
||||
|
||||
@ -123,7 +114,7 @@ Confirm that fact using browser developer tools to inspect the DOM.
|
||||
</figure>
|
||||
|
||||
The top paragraph is in the DOM. The bottom, disused paragraph is not;
|
||||
in its place is a comment about "template bindings" (more about that [later](#asterisk)).
|
||||
in its place is a comment about "template bindings" (more about that [later](guide/structural-directives#asterisk)).
|
||||
|
||||
When the condition is false, `NgIf` removes its host element from the DOM,
|
||||
detaches it from DOM events (the attachments that it made),
|
||||
@ -133,9 +124,6 @@ The component and DOM nodes can be garbage-collected and free up memory.
|
||||
### Why *remove* rather than *hide*?
|
||||
|
||||
A directive could hide the unwanted paragraph instead by setting its `display` style to `none`.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='display-none'}
|
||||
|
||||
While invisible, the element remains in the DOM.
|
||||
|
||||
<figure class='image-display'>
|
||||
@ -166,128 +154,24 @@ Before applying a structural directive, you might want to pause for a moment
|
||||
to consider the consequences of adding and removing elements and of creating and destroying components.
|
||||
|
||||
|
||||
{@a ngcontainer}
|
||||
|
||||
|
||||
{@a ng-container}
|
||||
|
||||
## Group sibling elements with <ng-container>
|
||||
|
||||
There's often a _root_ element that can and should host the structural directive.
|
||||
The list element (`<li>`) is a typical host element of an `NgFor` repeater.
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngfor-li'}
|
||||
|
||||
When there isn't a host element, you can usually wrap the content in a native HTML container element,
|
||||
such as a `<div>`, and attach the directive to that wrapper.
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif'}
|
||||
|
||||
Introducing another container element—typically a `<span>` or `<div>`—to
|
||||
group the elements under a single _root_ is usually harmless.
|
||||
_Usually_ ... but not _always_.
|
||||
|
||||
The grouping element may break the template appearance because CSS styles
|
||||
neither expect nor accommodate the new layout.
|
||||
For example, suppose you have the following paragraph layout.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif-span'}
|
||||
|
||||
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.css' region='p-span'}
|
||||
|
||||
The constructed paragraph renders strangely.
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"> </img>
|
||||
</figure>
|
||||
|
||||
The `p span` style, intended for use elsewhere, was inadvertently applied here.
|
||||
|
||||
Another problem: some HTML elements require all immediate children to be of a specific type.
|
||||
For example, the `<select>` tag requires `<option>` children.
|
||||
You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
|
||||
|
||||
When you try this,
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='select-span'}
|
||||
|
||||
the drop down is empty.
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work"> </img>
|
||||
</figure>
|
||||
|
||||
The browser won't display an `<option>` within a `<span>`.
|
||||
|
||||
### <ng-container> to the rescue
|
||||
|
||||
The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout
|
||||
because Angular _doesn't put it in the DOM_.
|
||||
|
||||
Here's the conditional paragraph again, this time using `<ng-container>`.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif-ngcontainer'}
|
||||
|
||||
It renders properly.
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"> </img>
|
||||
</figure>
|
||||
|
||||
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='select-ngcontainer'}
|
||||
|
||||
The drop down works properly.
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"> </img>
|
||||
</figure>
|
||||
|
||||
The `<ng-container>` is a syntax element recognized by the Angular parser.
|
||||
It's not a directive, component, class, or interface.
|
||||
It's more like the curly braces in a JavaScript `if`-block:
|
||||
|
||||
<code-example language="javascript">
|
||||
if (someCondition) {
|
||||
statement1;
|
||||
statement2;
|
||||
statement3;
|
||||
}
|
||||
</code-example>
|
||||
|
||||
Without those braces JavaScript could only execute the first statement
|
||||
when you intend to conditionally execute all of them as a single block.
|
||||
The `<ng-container>` satisfies a similar need in Angular templates.
|
||||
|
||||
|
||||
{@a asterisk}
|
||||
|
||||
## The asterisk (\*) prefix
|
||||
## The asterisk (*) prefix
|
||||
|
||||
Surely you noticed the asterisk (\*) prefix to the directive name
|
||||
Surely you noticed the asterisk (*) prefix to the directive name
|
||||
and wondered why it is necessary and what it does.
|
||||
|
||||
Here is `*ngIf` displaying the hero's name if `hero` exists.
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='asterisk'}
|
||||
|
||||
The asterisk is "syntactic sugar" for something a bit more complicated.
|
||||
Internally, Angular "de-sugars" it in two stages.
|
||||
Internally, Angular desugars it in two stages.
|
||||
First, it translates the `*ngIf="..."` into a template _attribute_, `template="ngIf ..."`, like this.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif-template-attr'}
|
||||
|
||||
Then it translates the template _attribute_ into a template _element_, wrapped around the host element, like this.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngif-template'}
|
||||
|
||||
* The `*ngIf` directive moved to the `<template>` tag where it became a property binding,`[ngIf]`.
|
||||
* The rest of the `<div>`, including its class attribute, moved inside the `<template>` tag.
|
||||
* The `*ngIf` directive moved to the `<template>` element where it became a property binding,`[ngIf]`.
|
||||
* The rest of the `<div>`, including its class attribute, moved inside the `<template>` element.
|
||||
|
||||
None of these forms are actually rendered.
|
||||
Only the finished product ends up in the DOM.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/hero-div-in-dom.png' alt="hero div in DOM"> </img>
|
||||
</figure>
|
||||
@ -295,25 +179,22 @@ Only the finished product ends up in the DOM.
|
||||
Angular consumed the `<template>` content during its actual rendering and
|
||||
replaced the `<template>` with a diagnostic comment.
|
||||
|
||||
The [`NgFor`](#ngfor) and [`NgSwitch...`](#ngswitch) directives follow the same pattern.
|
||||
The [`NgFor`](guide/structural-directives#ngFor) and [`NgSwitch...`](guide/structural-directives#ngSwitch) directives follow the same pattern.
|
||||
|
||||
|
||||
{@a ngfor}
|
||||
{@a ngFor}
|
||||
|
||||
## Inside _*ngFor_
|
||||
|
||||
Angular transforms the `*ngFor` in similar fashion from asterisk (\*) syntax through
|
||||
Angular transforms the `*ngFor` in similar fashion from asterisk (*) syntax through
|
||||
template _attribute_ to template _element_.
|
||||
|
||||
Here's a full-featured application of `NgFor`, written all three ways:
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='inside-ngfor'}
|
||||
|
||||
This is manifestly more complicated than `ngIf` and rightly so.
|
||||
The `NgFor` directive has more features, both required and optional, than the `NgIf` shown in this guide.
|
||||
At minimum `NgFor` needs a looping variable (`let hero`) and a list (`heroes`).
|
||||
|
||||
You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](microsyntax).
|
||||
You enable these features in the string assigned to `ngFor`, which you write in Angular's [microsyntax](guide/structural-directives#microsyntax).
|
||||
|
||||
|
||||
~~~ {.alert.is-helpful}
|
||||
@ -328,11 +209,12 @@ In this example, the `[ngClass]="odd"` stays on the `<div>`.
|
||||
|
||||
|
||||
{@a microsyntax}
|
||||
### microsyntax
|
||||
### Microsyntax
|
||||
|
||||
The Angular microsyntax lets you configure a directive in a compact, friendly string.
|
||||
The microsyntax parser translates that string into attributes on the `<template>`:
|
||||
|
||||
* The `let` keyword declares a [_template input variable_](#template-input-variable)
|
||||
* The `let` keyword declares a [_template input variable_](guide/structural-directives#template-input-variable)
|
||||
that you reference within the template. The input variables in this example are `hero`, `i`, and `odd`.
|
||||
The parser translates `let hero`, `let i`, and `let odd` into variables named,
|
||||
`let-hero`, `let-i`, and `let-odd`.
|
||||
@ -353,7 +235,7 @@ It's intended source is implicit.
|
||||
Angular sets `let-hero` to the value of the context's `$implicit` property
|
||||
which `NgFor` has initialized with the hero for the current iteration.
|
||||
|
||||
* The [API guide](../api/common/index/NgFor-directive.html "API: NgFor")
|
||||
* The [API guide](api/common/index/NgFor-directive)
|
||||
describes additional `NgFor` directive properties and context properties.
|
||||
|
||||
These microsyntax mechanisms are available to you when you write your own structural directives.
|
||||
@ -368,18 +250,18 @@ Studying the source code for `NgIf` and `NgFor` is a great way to learn more.
|
||||
### Template input variable
|
||||
|
||||
A _template input variable_ is a variable whose value you can reference _within_ a single instance of the template.
|
||||
There are several such variables in this example: `hero`, `li`, and `odd`.
|
||||
There are several such variables in this example: `hero`, `i`, and `odd`.
|
||||
All are preceded by the keyword `let`.
|
||||
|
||||
A _template input variable_ is **_not_** the same as a
|
||||
[template _reference_ variable](template-syntax.html#ref-vars),
|
||||
[template _reference_ variable](guide/template-syntax),
|
||||
neither _semantically_ nor _syntactically_.
|
||||
|
||||
You declare a template _input_ variable declaration with the `let` keyword (`let hero`).
|
||||
You declare a template _input_ variable using the `let` keyword (`let hero`).
|
||||
The variable's scope is limited to a _single instance_ of the repeated template.
|
||||
You can use the same variable name again in the definition of other structural directives.
|
||||
|
||||
You declare a template _reference_ variable declaration by prefixing the variable name with `#` (`#var`).
|
||||
You declare a template _reference_ variable by prefixing the variable name with `#` (`#var`).
|
||||
A _reference_ variable refers to its attached element, component or directive.
|
||||
It can be accessed _anywhere_ in the _entire template_.
|
||||
|
||||
@ -401,19 +283,16 @@ If so (and it seems like it should be so), how should Angular generalize the abi
|
||||
|
||||
There are no easy answers to these questions. Prohibiting multiple structural directives makes them moot.
|
||||
There's an easy solution for this use case: put the `*ngIf` on a container element that wraps the `*ngFor` element.
|
||||
One or both elements can be an [`ng-container`](#ngcontainer) so you don't have to introduce extra levels of HTML.
|
||||
One or both elements can be an [`ng-container`](guide/structural-directives#ngcontainer) so you don't have to introduce extra levels of HTML.
|
||||
|
||||
|
||||
{@a ngswitch}
|
||||
{@a ngSwitch}
|
||||
|
||||
## Inside the _NgSwitch_ directives
|
||||
## Inside _NgSwitch_ directives
|
||||
|
||||
The Angular _NgSwitch_ is actually a set of cooperating directives: `NgSwitch`, `NgSwitchCase`, and `NgSwitchDefault`.
|
||||
|
||||
Here's an example.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngswitch'}
|
||||
|
||||
The switch value assigned to `NgSwitch` (`hero.emotion`) determines which
|
||||
(if any) of the switch cases are displayed.
|
||||
|
||||
@ -422,7 +301,7 @@ It's an _attribute_ directive that controls the behavior of the other two switch
|
||||
That's why you write `[ngSwitch]`, never `*ngSwitch`.
|
||||
|
||||
`NgSwitchCase` and `NgSwitchDefault` _are_ structural directives.
|
||||
You attach them to elements using the asterisk (\*) prefix notation.
|
||||
You attach them to elements using the asterisk (*) prefix notation.
|
||||
An `NgSwitchCase` displays its host element when its value matches the switch value.
|
||||
The `NgSwitchDefault` displays its host element when no sibling `NgSwitchCase` matches the switch value.
|
||||
|
||||
@ -430,26 +309,20 @@ The element to which you apply a directive is its _host_ element.
|
||||
The `<happy-hero>` is the host element for the happy `*ngSwitchCase`.
|
||||
The `<unknown-hero>` is the host element for the `*ngSwitchDefault`.
|
||||
As with other structural directives, the `NgSwitchCase` and `NgSwitchDefault`
|
||||
can be "de-sugared" into the template _attribute_ form.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngswitch-template-attr'}
|
||||
|
||||
That, in turn, can be "de-sugared" into the `<template>` element form.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='ngswitch-template'}
|
||||
|
||||
can be desugared into the template _attribute_ form.
|
||||
That, in turn, can be desugared into the `<template>` element form.
|
||||
|
||||
|
||||
{@a prefer-asterisk}
|
||||
## Prefer the asterisk (\*) syntax.
|
||||
## Prefer the asterisk (*) syntax.
|
||||
|
||||
The asterisk (\*) syntax is more clear than the other "de-sugared" forms.
|
||||
Use [<ng-container>](#ng-container) when there's no single element
|
||||
The asterisk (*) syntax is more clear than the other desugared forms.
|
||||
Use [<ng-container>](guide/structural-directives#ng-container) when there's no single element
|
||||
to host the directive.
|
||||
|
||||
While there's rarely a good reason to apply a structural directive in template _attribute_ or _element_ form,
|
||||
it's still important to know that Angular creates a `<template>` and to understand how it works.
|
||||
You'll refer to the `<template>` when you [write your own structural directive](#unless).
|
||||
You'll refer to the `<template>` when you [write your own structural directive](guide/structural-directives#unless).
|
||||
|
||||
|
||||
{@a template}
|
||||
@ -461,37 +334,116 @@ is a formula for rendering HTML.
|
||||
It is never displayed directly.
|
||||
In fact, before rendering the view, Angular _replaces_ the `<template>` and its contents with a comment.
|
||||
|
||||
If there is no structural directive, if you merely wrap some elements in a `<template>` and do nothing with it,
|
||||
If there is no structural directive and you merely wrap some elements in a `<template>`,
|
||||
those elements disappear.
|
||||
That's the fate of the middle "hip" in the phrase "Hip! Hip! Hooray!".
|
||||
That's the fate of the middle "Hip!" in the phrase "Hip! Hip! Hooray!".
|
||||
Angular erases the middle "Hip!", leaving the cheer a bit less enthusiastic.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='template-tag'}
|
||||
|
||||
Angular erases the middle "hip", leaving the cheer a bit less enthusiastic.
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/template-rendering.png' width="350" alt="template tag rendering"> </img>
|
||||
</figure>
|
||||
|
||||
A structural directive puts a `<template>` to work
|
||||
as you'll see when you write your own structural directive.
|
||||
as you'll see when you [write your own structural directive](guide/structural-directives#unless).
|
||||
|
||||
|
||||
{@a ngcontainer}
|
||||
|
||||
|
||||
{@a ng-container}
|
||||
|
||||
## Group sibling elements with <ng-container>
|
||||
|
||||
There's often a _root_ element that can and should host the structural directive.
|
||||
The list element (`<li>`) is a typical host element of an `NgFor` repeater.
|
||||
When there isn't a host element, you can usually wrap the content in a native HTML container element,
|
||||
such as a `<div>`, and attach the directive to that wrapper.
|
||||
Introducing another container element—typically a `<span>` or `<div>`—to
|
||||
group the elements under a single _root_ is usually harmless.
|
||||
_Usually_ ... but not _always_.
|
||||
|
||||
The grouping element may break the template appearance because CSS styles
|
||||
neither expect nor accommodate the new layout.
|
||||
For example, suppose you have the following paragraph layout.
|
||||
You also have a CSS style rule that happens to apply to a `<span>` within a `<p>`aragraph.
|
||||
The constructed paragraph renders strangely.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/bad-paragraph.png' alt="spanned paragraph with bad style"> </img>
|
||||
</figure>
|
||||
|
||||
The `p span` style, intended for use elsewhere, was inadvertently applied here.
|
||||
|
||||
Another problem: some HTML elements require all immediate children to be of a specific type.
|
||||
For example, the `<select>` element requires `<option>` children.
|
||||
You can't wrap the _options_ in a conditional `<div>` or a `<span>`.
|
||||
|
||||
When you try this,
|
||||
the drop down is empty.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/bad-select.png' alt="spanned options don't work"> </img>
|
||||
</figure>
|
||||
|
||||
The browser won't display an `<option>` within a `<span>`.
|
||||
|
||||
### <ng-container> to the rescue
|
||||
|
||||
The Angular `<ng-container>` is a grouping element that doesn't interfere with styles or layout
|
||||
because Angular _doesn't put it in the DOM_.
|
||||
|
||||
Here's the conditional paragraph again, this time using `<ng-container>`.
|
||||
It renders properly.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/good-paragraph.png' alt="ngcontainer paragraph with proper style"> </img>
|
||||
</figure>
|
||||
|
||||
Now conditionally exclude a _select_ `<option>` with `<ng-container>`.
|
||||
The drop down works properly.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/select-ngcontainer-anim.gif' alt="ngcontainer options work properly"> </img>
|
||||
</figure>
|
||||
|
||||
The `<ng-container>` is a syntax element recognized by the Angular parser.
|
||||
It's not a directive, component, class, or interface.
|
||||
It's more like the curly braces in a JavaScript `if`-block:
|
||||
|
||||
<code-example language="javascript">
|
||||
if (someCondition) {
|
||||
statement1;
|
||||
statement2;
|
||||
statement3;
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
Without those braces, JavaScript would only execute the first statement
|
||||
when you intend to conditionally execute all of them as a single block.
|
||||
The `<ng-container>` satisfies a similar need in Angular templates.
|
||||
|
||||
|
||||
{@a unless}
|
||||
|
||||
## Write a structural directive
|
||||
In this section, you write a `UnlessDirective` structural directive
|
||||
|
||||
In this section, you write an `UnlessDirective` structural directive
|
||||
that does the opposite of `NgIf`.
|
||||
`NgIf` displays the template content when the condition is `true`.
|
||||
`UnlessDirective` displays the content when the condition is ***false***.
|
||||
Creating a directive is similar to creating a component.
|
||||
|
||||
* Import the `Directive` decorator (instead of the `Component` decorator).
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='myUnless-1'}
|
||||
* Import the `Input`, `TemplateRef`, and `ViewContainerRef` symbols; you'll need them for _any_ structural directive.
|
||||
|
||||
* Apply the decorator to the directive class.
|
||||
|
||||
* Set the CSS *attribute selector* that identifies the directive when applied to an element in a template.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/unless.directive.ts' region='skeleton'}
|
||||
|
||||
The directive's _selector_ is typically the directive's **attribute name** in square brackets.`[myUnless]`.
|
||||
Here's how you might begin:
|
||||
The directive's _selector_ is typically the directive's **attribute name** in square brackets, `[myUnless]`.
|
||||
The brackets define a CSS
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_selectors" target="_blank" title="MDN: Attribute selectors">attribute selector</a>.
|
||||
|
||||
@ -499,36 +451,29 @@ The directive _attribute name_ should be spelled in _lowerCamelCase_ and begin w
|
||||
Don't use `ng`. That prefix belongs to Angular.
|
||||
Pick something short that fits you or your company.
|
||||
In this example, the prefix is `my`.
|
||||
The directive _class_ name ends in `Directive` per the [style guide](style-guide.html#02-03 "Angular Style Guide").
|
||||
The directive _class_ name ends in `Directive` per the [style guide](guide/style-guide).
|
||||
Angular's own directives do not.
|
||||
|
||||
### _TemplateRef_ and _ViewContainerRef_
|
||||
|
||||
A simple structural directive like this one creates an
|
||||
[_embedded view_](../api/core/index/EmbeddedViewRef-class.html "API: EmbeddedViewRef")
|
||||
[_embedded view_](api/core/index/EmbeddedViewRef-class)
|
||||
from the Angular-generated `<template>` and inserts that view in a
|
||||
[_view container_](../api/core/index/ViewContainerRef-class.html "API: ViewContainerRef")
|
||||
[_view container_](api/core/index/ViewContainerRef-class)
|
||||
adjacent to the directive's original `<p>` host element.
|
||||
|
||||
You'll acquire the `<template>` contents with a
|
||||
[`TemplateRef`](../api/core/index/TemplateRef-class.html "API: TemplateRef")
|
||||
[`TemplateRef`](api/core/index/TemplateRef-class)
|
||||
and access the _view container_ through a
|
||||
[`ViewContainerRef`](../api/core/index/ViewContainerRef-class.html "API: ViewContainerRef").
|
||||
[`ViewContainerRef`](api/core/index/ViewContainerRef-class).
|
||||
|
||||
You inject both in the directive constructor as private variables of the class.
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/unless.directive.ts' region='ctor'}
|
||||
|
||||
### The _myUnless_ property
|
||||
|
||||
The directive consumer expects to bind a true/false condition to `[myUnless]`.
|
||||
That means the directive needs a `myUnless` property, decorated with `@Input`
|
||||
Read about `@Input` in the [_Template Syntax_](template-syntax.html#inputs-outputs) guide.
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/unless.directive.ts' region='set'}
|
||||
|
||||
Read about `@Input` in the [_Template Syntax_](guide/template-syntax) guide.
|
||||
Angular sets the `myUnless` property whenever the value of the condition changes.
|
||||
Because the `myUnless` property does work, it needs a setter.
|
||||
|
||||
@ -541,18 +486,12 @@ clear the container which also destroys the view.
|
||||
Nobody reads the `myUnless` property so it doesn't need a getter.
|
||||
|
||||
The completed directive code looks like this:
|
||||
|
||||
|
||||
{@example 'structural-directives/ts/src/app/unless.directive.ts' region='no-docs'}
|
||||
|
||||
Add this directive to the `!{_declsVsDirectives}` !{_array} of the !{_AppModuleVsAppComp}.
|
||||
|
||||
Then create some HTML to try it.
|
||||
|
||||
{@example 'structural-directives/ts/src/app/app.component.html' region='myUnless'}
|
||||
|
||||
When the `condition` is falsy, the top (A) paragraph appears and the bottom (B) paragraph disappears.
|
||||
When the `condition` is truthy, the top (A) paragraph is removed and the bottom (B) paragraph appears.
|
||||
|
||||
<figure class='image-display'>
|
||||
<img src='assets/images/devguide/structural-directives/unless-anim.gif' alt="UnlessDirective in action"> </img>
|
||||
</figure>
|
||||
@ -562,9 +501,10 @@ When the `condition` is truthy, the top (A) paragraph is removed and the bottom
|
||||
{@a summary}
|
||||
|
||||
## Summary
|
||||
|
||||
You can both try and download the source code for this guide in the <live-example></live-example>.
|
||||
|
||||
Here is the source from the `app/` folder.
|
||||
Here is the source from the `src/app/` folder.
|
||||
|
||||
<md-tab-group>
|
||||
|
||||
@ -606,9 +546,10 @@ Here is the source from the `app/` folder.
|
||||
</md-tab-group>
|
||||
|
||||
You learned
|
||||
|
||||
* that structural directives manipulate HTML layout.
|
||||
* to use [`<ng-container>`](#ngcontainer) as a grouping element when there is no suitable host element.
|
||||
* that the angular "de-sugars" [asterisk (\*) syntax](#asterisk) into a `<template>`.
|
||||
* to use [`<ng-container>`](guide/structural-directives#ngcontainer) as a grouping element when there is no suitable host element.
|
||||
* that the Angular desugars [asterisk (*) syntax](guide/structural-directives#asterisk) into a `<template>`.
|
||||
* how that works for the `NgIf`, `NgFor` and `NgSwitch` built-in directives.
|
||||
* about the [_microsyntax_](#microsyntax) that expands into a [`<template>`](#template).
|
||||
* to write a [custom structural directive](#unless), `UnlessDirective`.
|
||||
* about the [_microsyntax_](guide/structural-directives#microsyntax) that expands into a [`<template>`](guide/structural-directives#template).
|
||||
* to write a [custom structural directive](guide/structural-directives#unless), `UnlessDirective`.
|
@ -60,16 +60,16 @@ The guideline will use the shortcut `hero.component.ts|html|css|spec` to represe
|
||||
{@a toc}
|
||||
## Table of contents
|
||||
|
||||
1. [Single responsibility](#single-responsibility)
|
||||
1. [Naming](#naming)
|
||||
1. [Coding conventions](#coding-conventions)
|
||||
1. [App structure and Angular modules](#application-structure-and-angular-modules)
|
||||
1. [Components](#components)
|
||||
1. [Directives](#directives)
|
||||
1. [Services](#services)
|
||||
1. [Data services](#data-services)
|
||||
1. [Lifecycle hooks](#lifecycle-hooks)
|
||||
1. [Appendix](#appendix)
|
||||
1. [Single responsibility](guide/style-guide#single-responsibility)
|
||||
1. [Naming](guide/style-guide#naming)
|
||||
1. [Coding conventions](guide/style-guide#coding-conventions)
|
||||
1. [App structure and Angular modules](guide/style-guide#application-structure-and-angular-modules)
|
||||
1. [Components](guide/style-guide#components)
|
||||
1. [Directives](guide/style-guide#directives)
|
||||
1. [Services](guide/style-guide#services)
|
||||
1. [Data services](guide/style-guide#data-services)
|
||||
1. [Lifecycle hooks](guide/style-guide#lifecycle-hooks)
|
||||
1. [Appendix](guide/style-guide#appendix)
|
||||
|
||||
## Single responsibility
|
||||
|
||||
@ -3225,7 +3225,7 @@ A typical *lazy loaded folder* contains a *routing component*, its child compone
|
||||
|
||||
|
||||
<div class='s-why' class='s-why-last'>
|
||||
**Why?** Angular allows for an [alternative syntax](template-syntax.html#binding-syntax) `on-*`. If the event itself was prefixed with `on` this would result in an `on-onEvent` binding expression.
|
||||
**Why?** Angular allows for an [alternative syntax](guide/template-syntax) `on-*`. If the event itself was prefixed with `on` this would result in an `on-onEvent` binding expression.
|
||||
|
||||
</div>
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,7 @@
|
||||
TypeScript to JavaScript
|
||||
|
||||
@intro
|
||||
Convert Angular TypeScript examples into ES6 and ES5 JavaScript
|
||||
Convert Angular TypeScript examples into ES6 and ES5 JavaScript.
|
||||
|
||||
@description
|
||||
Anything you can do with Angular in _TypeScript_, you can also do
|
||||
@ -19,16 +19,16 @@ can read and write Angular apps in their preferred dialect.
|
||||
{@a toc}
|
||||
## Table of contents
|
||||
|
||||
[_TypeScript_ to _ES6_ to _ES5_](#from-ts)<br>
|
||||
[Modularity: imports and exports](#modularity)<br>
|
||||
[Classes and Class Metadata](#class-metadata)<br>
|
||||
[_ES5_ DSL](#dsl)<br>
|
||||
[Interfaces](#interfaces)<br>
|
||||
[Input and Output Metadata](#io-decorators)<br>
|
||||
[Dependency Injection](#dependency-injection)<br>
|
||||
[Host Binding](#host-binding)<br>
|
||||
[View and Child Decorators](#view-child-decorators)<br>
|
||||
[AOT compilation in _TypeScript_ Only](#aot)<br>
|
||||
[_TypeScript_ to _ES6_ to _ES5_](guide/ts-to-js#from-ts)<br>
|
||||
[Modularity: imports and exports](guide/ts-to-js#modularity)<br>
|
||||
[Classes and Class Metadata](guide/ts-to-js#class-metadata)<br>
|
||||
[_ES5_ DSL](guide/ts-to-js#dsl)<br>
|
||||
[Interfaces](guide/ts-to-js#interfaces)<br>
|
||||
[Input and Output Metadata](guide/ts-to-js#io-decorators)<br>
|
||||
[Dependency Injection](guide/ts-to-js#dependency-injection)<br>
|
||||
[Host Binding](guide/ts-to-js#host-binding)<br>
|
||||
[View and Child Decorators](guide/ts-to-js#view-child-decorators)<br>
|
||||
[AOT compilation in _TypeScript_ Only](guide/ts-to-js#aot)<br>
|
||||
|
||||
**Run and compare the live <live-example name="cb-ts-to-js">_TypeScript_</live-example> and <live-example name="cb-ts-to-js" lang="js">JavaScript</live-example>
|
||||
code shown in this cookbook.**
|
||||
@ -40,7 +40,7 @@ code shown in this cookbook.**
|
||||
|
||||
_TypeScript_
|
||||
<a href="https://www.typescriptlang.org" target="_blank" title='"TypeScript is a typed, superset of JavaScript"'>is a typed superset of _ES6 JavaScript_</a>.
|
||||
_ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers.
|
||||
_ES6 JavaScript_ is a superset of _ES5 JavaScript_. _ES5_ is the kind of JavaScript that runs natively in all modern browsers.
|
||||
The transformation of _TypeScript_ code all the way down to _ES5_ code can be seen as "shedding" features.
|
||||
|
||||
The downgrade progression is
|
||||
@ -81,7 +81,7 @@ To use decorators and annotations with Babel, install the
|
||||
|
||||
In both _TypeScript_ and _ES6_, you import Angular classes, functions, and other members with _ES6_ `import` statements.
|
||||
|
||||
In _ES5_, you access the Angular entities of the [the Angular packages](../glossary.html#scoped-package)
|
||||
In _ES5_, you access the Angular entities of the [the Angular packages](glossary)
|
||||
through the global `ng` object.
|
||||
Anything you can import from `@angular` is a nested member of this `ng` object:
|
||||
|
||||
@ -260,7 +260,7 @@ Use the constructor function pattern instead, adding methods to the prototype.
|
||||
When writing in _TypeScript_ or _ES6-with-decorators_,
|
||||
provide configuration and metadata by adorning a class with one or more *decorators*.
|
||||
For example, you supply metadata to a component class by preceding its definition with a
|
||||
[`@Component`](../api/core/index/Component-decorator.html) decorator function whose
|
||||
[`@Component`](api/core/index/Component-decorator) decorator function whose
|
||||
argument is an object literal with metadata properties.
|
||||
|
||||
In _plain ES6_, you provide metadata by attaching an `annotations` array to the _class_.
|
||||
@ -477,7 +477,7 @@ Just implement the methods and ignore interfaces when translating code samples f
|
||||
### Input and Output Decorators
|
||||
|
||||
In _TypeScript_ and _ES6-with-decorators_, you often add metadata to class _properties_ with _property decorators_.
|
||||
For example, you apply [`@Input` and `@Output` property decorators](../guide/template-syntax.html#inputs-outputs)
|
||||
For example, you apply [`@Input` and `@Output` property decorators](guide/template-syntax)
|
||||
to public class properties that will be the target of data binding expressions in parent components.
|
||||
|
||||
There is no equivalent of a property decorator in _ES5_ or _plain ES6_.
|
||||
@ -529,7 +529,7 @@ you specify the special binding name in the argument to the property decorator.
|
||||
In _ES5_ and _plain ES6_ code, convey this pairing with the `propertyName: bindingName` syntax in the class metadata.
|
||||
|
||||
## Dependency Injection
|
||||
Angular relies heavily on [Dependency Injection](../guide/dependency-injection.html) to provide services to the objects it creates.
|
||||
Angular relies heavily on [Dependency Injection](guide/dependency-injection) to provide services to the objects it creates.
|
||||
When Angular creates a new component, directive, pipe or another service,
|
||||
it sets the class constructor parameters to instances of services provided by an _Injector_.
|
||||
|
||||
@ -642,12 +642,12 @@ You can qualify injection behavior with injection decorators from `@angular/core
|
||||
|
||||
In _TypeScript_ and _ES6-with-decorators_,
|
||||
you precede the constructor parameters with injection qualifiers such as:
|
||||
* [`@Optional`](../api/core/index/Optional-decorator.html) sets the parameter to `null` if the service is missing
|
||||
* [`@Attribute`](../api/core/index/Attribute-interface.html) to inject a host element attribute value
|
||||
* [`@ContentChild`](../api/core/index/ContentChild-decorator.html) to inject a content child
|
||||
* [`@ViewChild`](../api/core/index/ViewChild-decorator.html) to inject a view child
|
||||
* [`@Host`](../api/core/index/Host-decorator.html) to inject a service in this component or its host
|
||||
* [`@SkipSelf`](../api/core/index/SkipSelf-decorator.html) to inject a service provided in an ancestor of this component
|
||||
* [`@Optional`](api/core/index/Optional-decorator) sets the parameter to `null` if the service is missing
|
||||
* [`@Attribute`](api/core/index/Attribute-interface) to inject a host element attribute value
|
||||
* [`@ContentChild`](api/core/index/ContentChild-decorator) to inject a content child
|
||||
* [`@ViewChild`](api/core/index/ViewChild-decorator) to inject a view child
|
||||
* [`@Host`](api/core/index/Host-decorator) to inject a service in this component or its host
|
||||
* [`@SkipSelf`](api/core/index/SkipSelf-decorator) to inject a service provided in an ancestor of this component
|
||||
|
||||
In _plain ES6_ and _ES5_, create an instance of the equivalent injection qualifier in a nested array within the `parameters` array.
|
||||
For example, you'd write `new Optional()` in _plain ES6_ and `new ng.core.Optional()` in _ES5_.
|
||||
@ -702,9 +702,9 @@ element whose tag matches the component selector.
|
||||
|
||||
In _TypeScript_ and _ES6-with-decorators_, you can use host property decorators to bind a host
|
||||
element to a component or directive.
|
||||
The [`@HostBinding`](../api/core/index/HostBinding-interface.html) decorator
|
||||
The [`@HostBinding`](api/core/index/HostBinding-interface) decorator
|
||||
binds host element properties to component data properties.
|
||||
The [`@HostListener`](../api/core/index/HostListener-interface.html) decorator binds
|
||||
The [`@HostListener`](api/core/index/HostListener-interface) decorator binds
|
||||
host element events to component event handlers.
|
||||
|
||||
In _plain ES6_ or _ES5_, add a `host` attribute to the component metadata to achieve the
|
||||
@ -778,8 +778,8 @@ Several _property_ decorators query a component's nested view and content compon
|
||||
_View_ children are associated with element tags that appear _within_ the component's template.
|
||||
|
||||
_Content_ children are associated with elements that appear _between_ the component's element tags;
|
||||
they are projected into an `<ng-content>` slot in the component's template. The [`@ViewChild`](../api/core/index/ViewChild-decorator.html) and
|
||||
[`@ViewChildren`](../api/core/index/ViewChildren-decorator.html) property decorators
|
||||
they are projected into an `<ng-content>` slot in the component's template. The [`@ViewChild`](api/core/index/ViewChild-decorator) and
|
||||
[`@ViewChildren`](api/core/index/ViewChildren-decorator) property decorators
|
||||
allow a component to query instances of other components that are used in
|
||||
its view.
|
||||
|
||||
@ -813,13 +813,13 @@ The `queries` property value is a hash map.
|
||||
|
||||
</md-tab-group>
|
||||
|
||||
The [`@ContentChild`](../api/core/index/ContentChild-decorator.html) and
|
||||
[`@ContentChildren`](../api/core/index/ContentChildren-decorator.html) property decorators
|
||||
The [`@ContentChild`](api/core/index/ContentChild-decorator) and
|
||||
[`@ContentChildren`](api/core/index/ContentChildren-decorator) property decorators
|
||||
allow a component to query instances of other components that have been projected
|
||||
into its view from elsewhere.
|
||||
|
||||
They can be added in the same way as [`@ViewChild`](../api/core/index/ViewChild-decorator.html) and
|
||||
[`@ViewChildren`](../api/core/index/ViewChildren-decorator.html).
|
||||
They can be added in the same way as [`@ViewChild`](api/core/index/ViewChild-decorator) and
|
||||
[`@ViewChildren`](api/core/index/ViewChildren-decorator).
|
||||
|
||||
<md-tab-group>
|
||||
|
||||
@ -862,7 +862,7 @@ instead of the `@ViewChild` and `@ContentChild` property decorators.
|
||||
## AOT Compilation in _TypeScript_ only
|
||||
|
||||
Angular offers two modes of template compilation, JIT (_Just-in-Time_) and
|
||||
[AOT (_Ahead-of-Time_)](aot-compiler.html).
|
||||
[AOT (_Ahead-of-Time_)](guide/aot-compiler).
|
||||
Currently the AOT compiler only works with _TypeScript_ applications because, in part, it generates
|
||||
_TypeScript_ files as an intermediate result.
|
||||
**AOT is not an option for pure JavaScript applications** at this time.
|
@ -30,32 +30,32 @@ business, because the work can be done collaboratively and spread over
|
||||
a period of time. The `upgrade` module in Angular has been designed to
|
||||
make incremental upgrading seamless.
|
||||
|
||||
1. [Preparation](#preparation)
|
||||
1. [Follow the Angular Style Guide](#follow-the-angular-style-guide)
|
||||
2. [Using a Module Loader](#using-a-module-loader)
|
||||
3. [Migrating to TypeScript](#migrating-to-typescript)
|
||||
4. [Using Component Directives](#using-component-directives)
|
||||
2. [Upgrading with The Upgrade Module](#upgrading-with-the-upgrade-module)
|
||||
1. [How The Upgrade Module Works](#how-the-upgrade-module-works)
|
||||
2. [Bootstrapping hybrid](#bootstrapping-hybrid-applications)
|
||||
3. [Using Angular Components from AngularJS Code](#using-angular-components-from-angularjs-code)
|
||||
4. [Using AngularJS Component Directives from Angular Code](#using-angularjs-component-directives-from-angular-code)
|
||||
5. [Projecting AngularJS Content into Angular Components](#projecting-angularjs-content-into-angular-components)
|
||||
6. [Transcluding Angular Content into AngularJS Component Directives](#transcluding-angular-content-into-angularjs-component-directives)
|
||||
7. [Making AngularJS Dependencies Injectable to Angular](#making-angularjs-dependencies-injectable-to-angular)
|
||||
8. [Making Angular Dependencies Injectable to AngularJS](#making-angular-dependencies-injectable-to-angularjs)
|
||||
9. [Using Ahead-of-time compilation with hybrid apps](#using-ahead-of-time-compilation-with-hybrid-apps)
|
||||
10. [Dividing routes between Angular and AngularJS](#dividing-routes-between-angular-and-angularjs)
|
||||
3. [PhoneCat Upgrade Tutorial](#phonecat-upgrade-tutorial)
|
||||
1. [Switching to TypeScript](#switching-to-typescript)
|
||||
2. [Installing Angular](#installing-angular)
|
||||
3. [Bootstrapping a hybrid PhoneCat](#bootstrapping-a-hybrid-phonecat)
|
||||
4. [Upgrading the Phone service](#upgrading-the-phone-service)
|
||||
5. [Upgrading Components](#upgrading-components)
|
||||
6. [AoT compile the hybrid app](#aot-compile-the-hybrid-app)
|
||||
7. [Adding The Angular Router And Bootstrap](#adding-the-angular-router-and-bootstrap)
|
||||
8. [Say Goodbye to AngularJS](#say-goodbye-to-angularjs)
|
||||
3. [Appendix: Upgrading PhoneCat Tests](#appendix-upgrading-phonecat-tests)
|
||||
1. [Preparation](guide/upgrade#preparation)
|
||||
1. [Follow the Angular Style Guide](guide/upgrade#follow-the-angular-style-guide)
|
||||
2. [Using a Module Loader](guide/upgrade#using-a-module-loader)
|
||||
3. [Migrating to TypeScript](guide/upgrade#migrating-to-typescript)
|
||||
4. [Using Component Directives](guide/upgrade#using-component-directives)
|
||||
2. [Upgrading with The Upgrade Module](guide/upgrade#upgrading-with-the-upgrade-module)
|
||||
1. [How The Upgrade Module Works](guide/upgrade#how-the-upgrade-module-works)
|
||||
2. [Bootstrapping hybrid](guide/upgrade#bootstrapping-hybrid-applications)
|
||||
3. [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code)
|
||||
4. [Using AngularJS Component Directives from Angular Code](guide/upgrade#using-angularjs-component-directives-from-angular-code)
|
||||
5. [Projecting AngularJS Content into Angular Components](guide/upgrade#projecting-angularjs-content-into-angular-components)
|
||||
6. [Transcluding Angular Content into AngularJS Component Directives](guide/upgrade#transcluding-angular-content-into-angularjs-component-directives)
|
||||
7. [Making AngularJS Dependencies Injectable to Angular](guide/upgrade#making-angularjs-dependencies-injectable-to-angular)
|
||||
8. [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs)
|
||||
9. [Using Ahead-of-time compilation with hybrid apps](guide/upgrade#using-ahead-of-time-compilation-with-hybrid-apps)
|
||||
10. [Dividing routes between Angular and AngularJS](guide/upgrade#dividing-routes-between-angular-and-angularjs)
|
||||
3. [PhoneCat Upgrade Tutorial](guide/upgrade#phonecat-upgrade-tutorial)
|
||||
1. [Switching to TypeScript](guide/upgrade#switching-to-typescript)
|
||||
2. [Installing Angular](guide/upgrade#installing-angular)
|
||||
3. [Bootstrapping a hybrid PhoneCat](guide/upgrade#bootstrapping-a-hybrid-phonecat)
|
||||
4. [Upgrading the Phone service](guide/upgrade#upgrading-the-phone-service)
|
||||
5. [Upgrading Components](guide/upgrade#upgrading-components)
|
||||
6. [AoT compile the hybrid app](guide/upgrade#aot-compile-the-hybrid-app)
|
||||
7. [Adding The Angular Router And Bootstrap](guide/upgrade#adding-the-angular-router-and-bootstrap)
|
||||
8. [Say Goodbye to AngularJS](guide/upgrade#say-goodbye-to-angularjs)
|
||||
3. [Appendix: Upgrading PhoneCat Tests](guide/upgrade#appendix-upgrading-phonecat-tests)
|
||||
|
||||
## Preparation
|
||||
|
||||
@ -207,7 +207,7 @@ using the component API:
|
||||
|
||||
Controller lifecycle hook methods `$onInit()`, `$onDestroy()`, and `$onChanges()`
|
||||
are another convenient feature that AngularJS 1.5 introduces. They all have nearly
|
||||
exact [equivalents in Angular](lifecycle-hooks.html), so organizing component lifecycle
|
||||
exact [equivalents in Angular](guide/lifecycle-hooks), so organizing component lifecycle
|
||||
logic around them will ease the eventual Angular upgrade process.
|
||||
|
||||
## Upgrading with The Upgrade Module
|
||||
@ -266,7 +266,7 @@ frameworks in how it actually works.
|
||||
|
||||
|
||||
<td>
|
||||
Tokens [can have different types](../guide/dependency-injection.html).
|
||||
Tokens [can have different types](guide/dependency-injection).
|
||||
They are often classes. They may also be strings.
|
||||
</td>
|
||||
|
||||
@ -283,7 +283,7 @@ frameworks in how it actually works.
|
||||
|
||||
|
||||
<td>
|
||||
There is a [tree hierarchy of injectors](../guide/hierarchical-dependency-injection.html),
|
||||
There is a [tree hierarchy of injectors](guide/hierarchical-dependency-injection),
|
||||
with a root injector and an additional injector for each component.
|
||||
|
||||
</td>
|
||||
@ -379,7 +379,7 @@ bindings get updated.
|
||||
In Angular things are different. While change detection still
|
||||
occurs after every event, no one needs to call `scope.$apply()` for
|
||||
that to happen. This is because all Angular code runs inside something
|
||||
called the [Angular zone](../api/core/index/NgZone-class.html). Angular always
|
||||
called the [Angular zone](api/core/index/NgZone-class). Angular always
|
||||
knows when the code finishes, so it also knows when it should kick off
|
||||
change detection. The code itself doesn't have to call `scope.$apply()`
|
||||
or anything like it.
|
||||
@ -408,7 +408,7 @@ When we downgrade an Angular component and then use it from AngularJS,
|
||||
the component's inputs will be watched using AngularJS change detection.
|
||||
When those inputs change, the corresponding properties in the component
|
||||
are set. We can also hook into the changes by implementing the
|
||||
[OnChanges](../api/core/index/OnChanges-class.html) interface in the component,
|
||||
[OnChanges](api/core/index/OnChanges-class) interface in the component,
|
||||
just like we could if it hadn't been downgraded.
|
||||
|
||||
Correspondingly, when we upgrade an AngularJS component and use it from Angular,
|
||||
@ -432,7 +432,7 @@ That means that we need at least one module each from both AngularJS and Angular
|
||||
We will import `UpgradeModule` inside our Angular module, and then use it for
|
||||
bootstrapping our AngularJS module. Let's see how.
|
||||
|
||||
Learn more about Angular modules at the [NgModule guide](ngmodule.html).
|
||||
Learn more about Angular modules at the [NgModule guide](guide/ngmodule).
|
||||
### Bootstrapping hybrid applications
|
||||
|
||||
The first step to upgrading an application using the `UpgradeModule` is
|
||||
@ -460,7 +460,7 @@ will result in the same thing:
|
||||
{@example 'upgrade-module/ts/src/app/ajs-bootstrap/app.module.ts' region='bootstrap'}
|
||||
|
||||
Now introduce Angular to the project. Inspired by instructions in
|
||||
[the Setup](setup.html), you can selectively copy in material from the
|
||||
[the Setup](guide/setup), you can selectively copy in material from the
|
||||
<a href="https://github.com/angular/quickstart" target="_blank">QuickStart github repository</a>.
|
||||
|
||||
Next, create an `app.module.ts` file and add the following `NgModule` class:
|
||||
@ -615,7 +615,7 @@ Angular.
|
||||
|
||||
Not all kinds of AngularJS directives can be upgraded. The directive
|
||||
really has to be a *component directive*, with the characteristics
|
||||
[described in the preparation guide above](#using-component-directives).
|
||||
[described in the preparation guide above](guide/upgrade#using-component-directives).
|
||||
Our safest bet for ensuring compatibility is using the
|
||||
[component API](https://docs.angularjs.org/api/ng/type/angular.Module)
|
||||
introduced in AngularJS 1.5.
|
||||
@ -860,7 +860,7 @@ code. For example, we might have a service called `HeroesService` in AngularJS:
|
||||
|
||||
{@example 'upgrade-module/ts/src/app/ajs-to-a-providers/heroes.service.ts'}
|
||||
|
||||
We can upgrade the service using a Angular [Factory provider](./dependency-injection.html#factory-providers)
|
||||
We can upgrade the service using a Angular [Factory provider](guide/dependency-injection)
|
||||
that requests the service from the AngularJS `$injector`.
|
||||
|
||||
We recommend declaring the Factory Provider in a separate `ajs-upgraded-providers.ts` file
|
||||
@ -929,7 +929,7 @@ After this, the service is injectable anywhere in our AngularJS code:
|
||||
We can take advantage of Ahead-of-time (AoT) compilation on hybrid apps just like on any other
|
||||
Angular application.
|
||||
The setup for an hybrid app is mostly the same as described in
|
||||
[the Ahead-of-time Compilation chapter](../cookbook/aot-compiler.html)
|
||||
[the Ahead-of-time Compilation chapter](cookbook/aot-compiler)
|
||||
save for differences in `index.html` and `main-aot.ts`
|
||||
|
||||
Our `index.html` will likely have script tags loading AngularJS files, so the `index.html` we
|
||||
@ -986,7 +986,7 @@ Next we declare both AngularJS and Angular routes as normal:
|
||||
In our `app.module.ts` we need to add `AppComponent` to the declarations and boostrap array.
|
||||
|
||||
Next we configure the router itself.
|
||||
We want to use [hash navigation](./router.html#-hashlocationstrategy-) in Angular
|
||||
We want to use [hash navigation](guide/router) in Angular
|
||||
because that's what we're also using in AngularJS.
|
||||
|
||||
Lastly, and most importantly, we want to use a custom `UrlHandlingStrategy` that will tell
|
||||
@ -1008,7 +1008,7 @@ which is where many of us began our Angular adventures. Now we'll see how to
|
||||
bring that application to the brave new world of Angular.
|
||||
|
||||
During the process we'll learn how to apply the steps outlined in the
|
||||
[preparation guide](#preparation) in practice: We'll align the application
|
||||
[preparation guide](guide/upgrade#preparation) in practice: We'll align the application
|
||||
with Angular and also take TypeScript into use.
|
||||
|
||||
To follow along with the tutorial, clone the
|
||||
@ -1204,7 +1204,7 @@ In terms of project structure, this is where our work begins:
|
||||
This is actually a pretty good starting point. The code uses the AngularJS 1.5
|
||||
component API and the organization follows the
|
||||
[AngularJS Style Guide](https://github.com/johnpapa/angular-styleguide/blob/master/a1/README.md),
|
||||
which is an important [preparation step](#following-the-angular-style-guide) before
|
||||
which is an important [preparation step](guide/upgrade#following-the-angular-style-guide) before
|
||||
a successful upgrade.
|
||||
|
||||
* Each component, service, and filter is in its own source file, as per the
|
||||
@ -1246,7 +1246,7 @@ Jasmine unit test framework.
|
||||
|
||||
We should also configure the TypeScript compiler so that it can understand our
|
||||
project. We'll add a `tsconfig.json` file to the project directory, just like we do
|
||||
in the documentation [setup](setup.html). It instructs the TypeScript compiler how
|
||||
in the documentation [setup](guide/setup). It instructs the TypeScript compiler how
|
||||
to interpret our source files.
|
||||
We are telling the TypeScript compiler to turn our TypeScript files to ES5 code
|
||||
bundled into CommonJS modules.
|
||||
@ -1362,7 +1362,7 @@ We'll instead turn it directly into an Angular service.
|
||||
|
||||
Having completed our preparation work, let's get going with the Angular
|
||||
upgrade of PhoneCat. We'll do this incrementally with the help of the
|
||||
[upgrade module](#upgrading-with-the-upgrade-module) that comes with Angular.
|
||||
[upgrade module](guide/upgrade#upgrading-with-the-upgrade-module) that comes with Angular.
|
||||
By the time we're done, we'll be able to remove AngularJS from the project
|
||||
completely, but the key is to do this piece by piece without breaking the application.
|
||||
|
||||
@ -1374,7 +1374,7 @@ The project also contains some animations, which we are not yet upgrading in thi
|
||||
~~~
|
||||
|
||||
Let's install Angular into the project, along with the SystemJS module loader.
|
||||
Take a look at the results of the [Setup](setup.html) instructions
|
||||
Take a look at the results of the [Setup](guide/setup) instructions
|
||||
and get the following configurations from there:
|
||||
|
||||
* Add Angular and the other new dependencies to `package.json`
|
||||
@ -1411,7 +1411,7 @@ to load the actual application:
|
||||
{@example 'upgrade-phonecat-2-hybrid/ts/index.html' region='angular'}
|
||||
|
||||
We also need to make a couple of adjustments
|
||||
to the `systemjs.config.js` file installed during [setup](setup.html).
|
||||
to the `systemjs.config.js` file installed during [setup](guide/setup).
|
||||
|
||||
We want to point the browser to the project root when loading things through SystemJS,
|
||||
instead of using the `<base>` URL.
|
||||
@ -1443,7 +1443,7 @@ What we'll do next is bootstrap the application as a *hybrid application*
|
||||
that supports both AngularJS and Angular components. Once we've done that
|
||||
we can start converting the individual pieces to Angular.
|
||||
|
||||
To [bootstrap a hybrid application](#bootstrapping-hybrid-applications),
|
||||
To [bootstrap a hybrid application](guide/upgrade#bootstrapping-hybrid-applications),
|
||||
we first need to import `UpgradeModule` in our `AppModule`, and override it's bootstrap method:
|
||||
|
||||
|
||||
@ -1514,7 +1514,7 @@ service in `phone.service.ts` with a TypeScript class decorated as `@Injectable`
|
||||
|
||||
The `@Injectable` decorator will attach some dependency injection metadata
|
||||
to the class, letting Angular know about its dependencies. As described
|
||||
by our [Dependency Injection Guide](./dependency-injection.html),
|
||||
by our [Dependency Injection Guide](guide/dependency-injection),
|
||||
this is a marker decorator we need to use for classes that have no other
|
||||
Angular decorators but still need to have their dependencies injected.
|
||||
|
||||
@ -1614,7 +1614,7 @@ with Angular's two-way `[(ngModel)]` binding syntax:
|
||||
{@example 'upgrade-phonecat-2-hybrid/ts/app/phone-list/phone-list.template.html' region='controls'}
|
||||
|
||||
Replace the list's `ng-repeat` with an `*ngFor` as
|
||||
[described in the Template Syntax page](../guide/template-syntax.html#directives).
|
||||
[described in the Template Syntax page](guide/template-syntax).
|
||||
Replace the image tag's `ng-src` with a binding to the native `src` property.
|
||||
|
||||
|
||||
@ -1662,7 +1662,7 @@ which was injected into `PhoneDetails` when it was still an AngularJS controller
|
||||
We intend to inject it into the new `PhoneDetailsComponent`.
|
||||
|
||||
Unfortunately, AngularJS dependencies are not automatically available to Angular components.
|
||||
We must use a [Factory provider](#making-angularjs-dependencies-injectable-to-angular)
|
||||
We must use a [Factory provider](guide/upgrade#making-angularjs-dependencies-injectable-to-angular)
|
||||
to make `$routeParams` an Angular provider.
|
||||
Do that in a new file called `ajs-upgraded-providers.ts` and import it in `app.module.ts`:
|
||||
|
||||
@ -1684,7 +1684,7 @@ There are several notable changes here:
|
||||
* Just like we did in the phone list, we've replaced `ng-src` with property
|
||||
bindings for the standard `src` property.
|
||||
* We're using the property binding syntax around `ng-class`. Though Angular
|
||||
does have [a very similar `ngClass`](../guide/template-syntax.html#directives)
|
||||
does have [a very similar `ngClass`](guide/template-syntax)
|
||||
as AngularJS does, its value is not magically evaluated as an expression.
|
||||
In Angular we always specify in the template when an attribute's value is
|
||||
a property expression, as opposed to a literal string.
|
||||
@ -1727,7 +1727,7 @@ remove the filter <script> tag from `index.html`:
|
||||
## AoT compile the hybrid app
|
||||
|
||||
To use AoT with our hybrid app we have to first set it up like any other Angular application,
|
||||
as shown in [the Ahead-of-time Compilation chapter](../cookbook/aot-compiler.html).
|
||||
as shown in [the Ahead-of-time Compilation chapter](cookbook/aot-compiler).
|
||||
|
||||
Then we have to change `main-aot.ts` bootstrap also bootstrap the AngularJS app
|
||||
via `UpgradeModule`:
|
||||
@ -1764,7 +1764,7 @@ while keeping `/phones/:phoneId` in the AngularJS router.
|
||||
|
||||
#### Add the Angular router
|
||||
|
||||
Angular has an [all-new router](router.html).
|
||||
Angular has an [all-new router](guide/router).
|
||||
|
||||
Like all routers, it needs a place in the UI to display routed views.
|
||||
For Angular that's the `<router-outlet>` and it belongs in a *root component*
|
||||
@ -1792,7 +1792,7 @@ It replaces the old AngularJS `ng-view` directive:
|
||||
#### Create the _Routing Module_
|
||||
A router needs configuration whether it's the AngularJS or Angular or any other router.
|
||||
|
||||
The details of Angular router configuration are best left to the [Routing documentation](router.html)
|
||||
The details of Angular router configuration are best left to the [Routing documentation](guide/router)
|
||||
which recommends that you create a `NgModule` dedicated to router configuration
|
||||
(called a _Routing Module_).
|
||||
|
||||
@ -1838,7 +1838,7 @@ and let that directive construct the appropriate URL to the `PhoneDetailComponen
|
||||
{@example 'upgrade-phonecat-3-router/ts/app/phone-list/phone-list.template.html' region='list'}
|
||||
|
||||
|
||||
See the [Routing](router.html) page for details.
|
||||
See the [Routing](guide/router) page for details.
|
||||
We are now running both routers at the same time!
|
||||
Angular is handling the initial `/` url, redirecting to `/phones`.
|
||||
Meanwhile when we click a link to the phone detail, AngularJS takes over.
|
||||
@ -1881,7 +1881,7 @@ to the Angular way.
|
||||
{@example 'upgrade-phonecat-4-final/ts/app/main.ts'}
|
||||
|
||||
If you haven't already, remove all references to the `UpgradeModule` from `app.module.ts`,
|
||||
as well as any [Factory provider](#making-angularjs-dependencies-injectable-to-angular)
|
||||
as well as any [Factory provider](guide/upgrade#making-angularjs-dependencies-injectable-to-angular)
|
||||
for AngularJS services, and the `app/ajs-upgraded-providers.ts` file.
|
||||
|
||||
Also remove any `downgradeInjectable()` or `downgradeComponent()` you find,
|
||||
|
@ -13,13 +13,13 @@ event binding syntax.
|
||||
Run the <live-example></live-example>.
|
||||
## Binding to user input events
|
||||
|
||||
You can use [Angular event bindings](./template-syntax.html#event-binding)
|
||||
You can use [Angular event bindings](guide/template-syntax)
|
||||
to respond to any [DOM event](https://developer.mozilla.org/en-US/docs/Web/Events).
|
||||
Many DOM events are triggered by user input. Binding to these events provides a way to
|
||||
get input from the user.
|
||||
|
||||
To bind to a DOM event, surround the DOM event name in parentheses and assign a quoted
|
||||
[template statement](./template-syntax.html#template-statements) to it.
|
||||
[template statement](guide/template-syntax) to it.
|
||||
|
||||
The following example shows an event binding that implements a click handler:
|
||||
|
||||
@ -63,7 +63,7 @@ In this case, `target` refers to the [`<input>` element](https://developer.mozil
|
||||
|
||||
After each call, the `onKey()` method appends the contents of the input box value to the list
|
||||
in the component's `values` property, followed by a separator character (|).
|
||||
The [interpolation](./template-syntax.html#interpolation)
|
||||
The [interpolation](guide/template-syntax)
|
||||
displays the accumulating input box changes from the `values` property.
|
||||
|
||||
Suppose the user enters the letters "abc", and then backspaces to remove them one by one.
|
||||
@ -114,7 +114,7 @@ The next section shows how to use template reference variables to address this p
|
||||
|
||||
## Get user input from a template reference variable
|
||||
There's another way to get the user data: use Angular
|
||||
[**template reference variables**](./template-syntax.html#ref-vars).
|
||||
[**template reference variables**](guide/template-syntax).
|
||||
These variables provide direct access to an element from within the template.
|
||||
To declare a template reference variable, precede an identifier with a hash (or pound) character (#).
|
||||
|
||||
@ -184,7 +184,7 @@ To fix this issue, listen to both the _Enter_ key and the _blur_ event.
|
||||
|
||||
|
||||
## Put it all together
|
||||
The previous page showed how to [display data](./displaying-data.html).
|
||||
The previous page showed how to [display data](guide/displaying-data).
|
||||
This page demonstrated event binding techniques.
|
||||
|
||||
Now, put it all together in a micro-app
|
||||
|
@ -2,7 +2,7 @@
|
||||
Visual Studio 2015 QuickStart
|
||||
|
||||
@intro
|
||||
Use Visual Studio 2015 with the QuickStart files
|
||||
Use Visual Studio 2015 with the QuickStart files.
|
||||
|
||||
@description
|
||||
<a id="top"></a>Some developers prefer Visual Studio as their Integrated Development Environment (IDE).
|
||||
@ -21,15 +21,15 @@ then consider the _experimental_
|
||||
Note that the resulting code does not map to the docs. Adjust accordingly.
|
||||
The steps are as follows:
|
||||
|
||||
- [Prerequisite](#prereq1): Install Node.js
|
||||
- [Prerequisite](#prereq2): Install Visual Studio 2015 Update 3
|
||||
- [Prerequisite](#prereq3): Configure External Web tools
|
||||
- [Prerequisite](#prereq4): Install TypeScript 2 for Visual Studio 2015
|
||||
- [Step 1](#download): Download the QuickStart files
|
||||
- [Step 2](#create-project): Create the Visual Studio ASP.NET project
|
||||
- [Step 3](#copy): Copy the QuickStart files into the ASP.NET project folder
|
||||
- [Step 4](#restore): Restore required packages
|
||||
- [Step 5](#build-and-run): Build and run the app
|
||||
- [Prerequisite](guide/visual-studio-2015#prereq1): Install Node.js
|
||||
- [Prerequisite](guide/visual-studio-2015#prereq2): Install Visual Studio 2015 Update 3
|
||||
- [Prerequisite](guide/visual-studio-2015#prereq3): Configure External Web tools
|
||||
- [Prerequisite](guide/visual-studio-2015#prereq4): Install TypeScript 2 for Visual Studio 2015
|
||||
- [Step 1](guide/visual-studio-2015#download): Download the QuickStart files
|
||||
- [Step 2](guide/visual-studio-2015#create-project): Create the Visual Studio ASP.NET project
|
||||
- [Step 3](guide/visual-studio-2015#copy): Copy the QuickStart files into the ASP.NET project folder
|
||||
- [Step 4](guide/visual-studio-2015#restore): Restore required packages
|
||||
- [Step 5](guide/visual-studio-2015#build-and-run): Build and run the app
|
||||
|
||||
|
||||
<h2 id='prereq1'>
|
@ -2,7 +2,7 @@
|
||||
Webpack: an introduction
|
||||
|
||||
@intro
|
||||
Create Angular applications with a Webpack based tooling
|
||||
Create Angular applications with a Webpack based tooling.
|
||||
|
||||
@description
|
||||
|
||||
@ -22,22 +22,22 @@ This guide offers a taste of Webpack and explains how to use it with Angular app
|
||||
<a id="top"></a>
|
||||
## Table of contents
|
||||
|
||||
[What is Webpack?](#what-is-webpack)
|
||||
[What is Webpack?](guide/webpack#what-is-webpack)
|
||||
|
||||
* [Entries and outputs](#entries-outputs)
|
||||
* [Loaders](#loaders)
|
||||
* [Plugins](#plugins)
|
||||
* [Entries and outputs](guide/webpack#entries-outputs)
|
||||
* [Loaders](guide/webpack#loaders)
|
||||
* [Plugins](guide/webpack#plugins)
|
||||
|
||||
[Configuring Webpack](#configure-webpack)
|
||||
[Configuring Webpack](guide/webpack#configure-webpack)
|
||||
|
||||
* [Common configuration](#common-configuration)
|
||||
* [Development configuration](#development-configuration)
|
||||
* [Production configuration](#production-configuration)
|
||||
* [Test configuration](#test-configuration)
|
||||
* [Common configuration](guide/webpack#common-configuration)
|
||||
* [Development configuration](guide/webpack#development-configuration)
|
||||
* [Production configuration](guide/webpack#production-configuration)
|
||||
* [Test configuration](guide/webpack#test-configuration)
|
||||
|
||||
[Trying it out](#try)
|
||||
[Trying it out](guide/webpack#try)
|
||||
|
||||
[Conclusions](#conclusions)
|
||||
[Conclusions](guide/webpack#conclusions)
|
||||
|
||||
You can also <a href="/resources/zips/webpack/webpack.zip">download the final result.</a>
|
||||
|
||||
@ -95,7 +95,7 @@ and emits *two* bundle files, one called `app.js` containing only the applicatio
|
||||
another called `vendor.js` with all the vendor dependencies.
|
||||
|
||||
The `[name]` in the output name is a *placeholder* that a Webpack plugin replaces with the entry names,
|
||||
`app` and `vendor`. Plugins are [covered later](#commons-chunk-plugin) in the guide.
|
||||
`app` and `vendor`. Plugins are [covered later](guide/webpack#commons-chunk-plugin) in the guide.
|
||||
To tell Webpack what belongs in the vendor bundle,
|
||||
add a `vendor.ts` file that only imports the application's third-party modules:
|
||||
|
||||
@ -191,8 +191,8 @@ Add these files:
|
||||
|
||||
|
||||
Many of these files should be familiar from other Angular documentation guides,
|
||||
especially the [_Typescript configuration_](../guide/typescript-configuration.html) and
|
||||
[_npm packages_](../guide/npm-packages.html) guides.
|
||||
especially the [_Typescript configuration_](guide/typescript-configuration) and
|
||||
[_npm packages_](guide/npm-packages) guides.
|
||||
|
||||
Webpack, the plugins, and the loaders are also installed as packages.
|
||||
They are listed in the updated `packages.json`.
|
||||
@ -209,7 +209,7 @@ Open a terminal window and (re)install the *npm* packages
|
||||
### Polyfills
|
||||
|
||||
You'll need polyfills to run an Angular application in most browsers as explained
|
||||
in the [_Browser Support_](browser-support.html) guide.
|
||||
in the [_Browser Support_](guide/browser-support) guide.
|
||||
|
||||
Polyfills should be bundled separately from the application and vendor bundles.
|
||||
Add a `polyfills.ts` like this one to the `src/` folder.
|
||||
@ -253,10 +253,10 @@ Webpack is a NodeJS-based tool that reads configuration from a JavaScript _commo
|
||||
The configuration imports dependencies with `require` statements
|
||||
and exports several objects as properties of a `module.exports` object.
|
||||
|
||||
* [`entries`](#common-entries) - the entry-point files that define the bundles.
|
||||
* [`resolve`](#common-resolve) - how to resolve file names when they lack extensions.
|
||||
* [`module.rules`](#common-rules) - `module` is an object with `rules` for deciding how files are loaded.
|
||||
* [`plugins`](#common-plugins) - creates instances of the plugins.
|
||||
* [`entries`](guide/webpack#common-entries) - the entry-point files that define the bundles.
|
||||
* [`resolve`](guide/webpack#common-resolve) - how to resolve file names when they lack extensions.
|
||||
* [`module.rules`](guide/webpack#common-rules) - `module` is an object with `rules` for deciding how files are loaded.
|
||||
* [`plugins`](guide/webpack#common-plugins) - creates instances of the plugins.
|
||||
|
||||
|
||||
{@a common-entries}
|
||||
@ -370,7 +370,7 @@ The development build relies on the Webpack development server, configured near
|
||||
|
||||
Although you tell Webpack to put output bundles in the `dist` folder,
|
||||
the dev server keeps all bundles in memory; it doesn't write them to disk.
|
||||
You won't find any files in the `dist` folder (at least not any generated from `this development build`).
|
||||
You won't find any files in the `dist` folder (at least not any generated from *this development build*).
|
||||
|
||||
|
||||
The `HtmlWebpackPlugin` (added in `webpack.common.js`) use the *publicPath* and the *filename* settings to generate
|
||||
@ -567,4 +567,4 @@ for a small Angular application.
|
||||
|
||||
_You could always do more_. Search the web for expert advice and expand your Webpack knowledge.
|
||||
|
||||
[Back to top](#top)
|
||||
[Back to top](guide/webpack#top)
|
@ -1,9 +1,5 @@
|
||||
{
|
||||
"TopBar": [
|
||||
{
|
||||
"url": "/",
|
||||
"title": "Home"
|
||||
},
|
||||
{
|
||||
"url": "api",
|
||||
"title": "API"
|
@ -2,13 +2,13 @@
|
||||
The Hero Editor
|
||||
|
||||
@intro
|
||||
We build a simple hero editor
|
||||
We build a simple hero editor.
|
||||
|
||||
@description
|
||||
## Setup to develop locally
|
||||
Real application development takes place in a local development environment like your machine.
|
||||
|
||||
Follow the [setup](../guide/setup.html) instructions for creating a new project
|
||||
Follow the [setup](guide/setup) instructions for creating a new project
|
||||
named <ngio-ex path="angular-tour-of-heroes"></ngio-ex>
|
||||
after which the file structure should look like this:
|
||||
|
||||
@ -109,7 +109,7 @@ The browser should refresh and display our title and hero.
|
||||
|
||||
The double curly braces tell our app to read the `title` and `hero` properties from the component and render them.
|
||||
This is the "interpolation" form of one-way data binding.
|
||||
Learn more about interpolation in the [Displaying Data chapter](../guide/displaying-data.html).### Hero object
|
||||
Learn more about interpolation in the [Displaying Data chapter](guide/displaying-data).### Hero object
|
||||
|
||||
At the moment, our hero is just a name. Our hero needs more properties.
|
||||
Let's convert the `hero` from a literal string to a class.
|
||||
@ -189,8 +189,8 @@ Now we have included the forms package which includes `ngModel`.
|
||||
|
||||
|
||||
Learn more about the `FormsModule` and `ngModel` in the
|
||||
[Forms](../guide/forms.html#ngModel) and
|
||||
[Template Syntax](../guide/template-syntax.html#ngModel) chapters.
|
||||
[Forms](guide/forms) and
|
||||
[Template Syntax](guide/template-syntax) chapters.
|
||||
Let’s update the template to use the **`ngModel`** built-in directive for two-way binding.
|
||||
|
||||
Replace the `<input>` with the following HTML
|
||||
@ -226,4 +226,4 @@ Our Tour of Heroes only displays one hero and we really want to display a list o
|
||||
We also want to allow the user to select a hero and display their details.
|
||||
We’ll learn more about how to retrieve lists, bind them to the
|
||||
template, and allow a user to select a hero in the
|
||||
[next tutorial chapter](./toh-pt2.html).
|
||||
[next tutorial chapter](tutorial/toh-pt2).
|
@ -2,7 +2,7 @@
|
||||
Master/Detail
|
||||
|
||||
@intro
|
||||
We build a master/detail page with a list of heroes
|
||||
We build a master/detail page with a list of heroes.
|
||||
|
||||
@description
|
||||
Our story needs more heroes.
|
||||
@ -17,7 +17,7 @@ so we’ll need a way to do that.
|
||||
|
||||
## Where We Left Off
|
||||
Before we continue with Part 2 of the Tour of Heroes,
|
||||
let’s verify we have the following structure after [Part 1](./toh-pt1.html).
|
||||
let’s verify we have the following structure after [Part 1](tutorial/toh-pt1).
|
||||
If not, we’ll need to go back to Part 1 and figure out what we missed.
|
||||
|
||||
<aio-filetree>
|
||||
@ -160,8 +160,8 @@ The `let` keyword before "hero" identifies `hero` as a template input variable.
|
||||
We can reference this variable within the template to access a hero’s properties.
|
||||
|
||||
Learn more about `ngFor` and template input variables in the
|
||||
[Displaying Data](../guide/displaying-data.html#ngFor) and
|
||||
[Template Syntax](../guide/template-syntax.html#ngFor) chapters.
|
||||
[Displaying Data](guide/displaying-data) and
|
||||
[Template Syntax](guide/template-syntax) chapters.
|
||||
Now we insert some content between the `<li>` tags
|
||||
that uses the `hero` template variable to display the hero’s properties.
|
||||
|
||||
@ -219,8 +219,8 @@ The expression to the right of the equal sign calls the `AppComponent` method,
|
||||
passing the template input variable `hero` as an argument.
|
||||
That’s the same `hero` variable we defined previously in the `ngFor`.
|
||||
Learn more about Event Binding in the
|
||||
[User Input](../guide/user-input.html) and
|
||||
[Templating Syntax](../guide/template-syntax.html#event-binding) chapters.### Add the click handler
|
||||
[User Input](guide/user-input) and
|
||||
[Templating Syntax](guide/template-syntax) chapters.### Add the click handler
|
||||
Our event binding refers to an `onSelect` method that doesn’t exist yet.
|
||||
We’ll add that method to our component now.
|
||||
|
||||
@ -291,8 +291,8 @@ structure of portions of the DOM.
|
||||
In other words, they give structure to the way Angular displays content in the DOM.
|
||||
|
||||
Learn more about `ngIf`, `ngFor` and other structural directives in the
|
||||
[Structural Directives](../guide/structural-directives.html) and
|
||||
[Template Syntax](../guide/template-syntax.html#directives) chapters.
|
||||
[Structural Directives](guide/structural-directives) and
|
||||
[Template Syntax](guide/template-syntax) chapters.
|
||||
The browser refreshes and we see the list of heroes but not the selected hero detail.
|
||||
The `ngIf` keeps it out of the DOM as long as the `selectedHero` is undefined.
|
||||
When we click on a hero in the list, the selected hero displays in the hero details.
|
||||
@ -323,7 +323,7 @@ from the data source (the expression `hero === selectedHero`) to a property of `
|
||||
{@example 'toh-2/ts-snippets/app.component.snippets.pt2.ts' region='class-selected-2'}
|
||||
|
||||
|
||||
Learn more about [property bindings](../guide/template-syntax.html#property-binding)
|
||||
Learn more about [property bindings](guide/template-syntax)
|
||||
in the Template Syntax chapter.
|
||||
The browser reloads our app.
|
||||
We select the hero Magneta and the selection is clearly identified by the background color.
|
||||
@ -353,4 +353,4 @@ Run the <live-example></live-example> for this part.
|
||||
Our Tour of Heroes has grown, but it’s far from complete.
|
||||
We can't put the entire app into a single component.
|
||||
We need to break it up into sub-components and teach them to work together
|
||||
as we learn in the [next chapter](toh-pt3.html).
|
||||
as we learn in the [next chapter](tutorial/toh-pt3).
|
@ -2,7 +2,7 @@
|
||||
Multiple Components
|
||||
|
||||
@intro
|
||||
We refactor the master/detail view into separate components
|
||||
We refactor the master/detail view into separate components.
|
||||
|
||||
@description
|
||||
Our app is growing.
|
||||
@ -117,8 +117,8 @@ Notice that we have an `AppComponent` in a file named `app.component.ts` and ou
|
||||
|
||||
All of our component names end in "Component". All of our component file names end in ".component".
|
||||
|
||||
We spell our file names in lower **[dash case](../guide/glossary.html#dash-case)**
|
||||
(AKA **[kebab-case](../guide/glossary.html#kebab-case)**) so we don't worry about
|
||||
We spell our file names in lower **[dash case](guide/glossary)**
|
||||
(AKA **[kebab-case](guide/glossary)**) so we don't worry about
|
||||
case sensitivity on the server or in source control.
|
||||
|
||||
<!-- TODO
|
||||
@ -182,7 +182,7 @@ Notice that the `hero` property is the ***target*** of a property binding &mdash
|
||||
|
||||
Angular insists that we declare a ***target*** property to be an ***input*** property.
|
||||
If we don't, Angular rejects the binding and throws an error.
|
||||
We explain input properties in more detail [here](../guide/attribute-directives.html#why-input)
|
||||
We explain input properties in more detail [here](guide/attribute-directives)
|
||||
where we also explain why *target* properties require this special treatment and
|
||||
*source* properties do not.There are a couple of ways we can declare that `hero` is an *input*.
|
||||
We'll do it the way we *prefer*, by annotating the `hero` property with the `@Input` decorator that we imported earlier.
|
||||
@ -191,7 +191,7 @@ We'll do it the way we *prefer*, by annotating the `hero` property with the `@In
|
||||
|
||||
|
||||
Learn more about the `@Input()` decorator in the
|
||||
[Attribute Directives](../guide/attribute-directives.html#input) chapter.
|
||||
[Attribute Directives](guide/attribute-directives) chapter.
|
||||
|
||||
## Refresh the AppModule
|
||||
We return to the `AppModule`, the application's root module, and teach it to use the `HeroDetailComponent`.
|
||||
@ -362,4 +362,4 @@ That's not sustainable.
|
||||
We should refactor data access to a separate service
|
||||
and share it among the components that need data.
|
||||
|
||||
We’ll learn to create services in the [next tutorial](toh-pt4.html) chapter.
|
||||
We’ll learn to create services in the [next tutorial](tutorial/toh-pt4) chapter.
|
@ -2,7 +2,7 @@
|
||||
Services
|
||||
|
||||
@intro
|
||||
We create a reusable service to manage our hero data calls
|
||||
We create a reusable service to manage our hero data calls.
|
||||
|
||||
@description
|
||||
The Tour of Heroes is evolving and we anticipate adding more components in the near future.
|
||||
@ -127,7 +127,7 @@ share that service with all components that need heroes.
|
||||
### Create the HeroService
|
||||
Create a file in the `app` folder called `hero.service.ts`.
|
||||
We've adopted a convention in which we spell the name of a service in lowercase followed by `.service`.
|
||||
If the service name were multi-word, we'd spell the base filename in lower [dash-case](../guide/glossary.html#dash-case).
|
||||
If the service name were multi-word, we'd spell the base filename in lower [dash-case](guide/glossary).
|
||||
The `SpecialSuperHeroService` would be defined in the `special-super-hero.service.ts` file.We name the class `HeroService` and export it for others to import.
|
||||
|
||||
|
||||
@ -234,7 +234,7 @@ Here's the constructor:
|
||||
The constructor itself does nothing. The parameter simultaneously
|
||||
defines a private `heroService` property and identifies it as a `HeroService` injection site.Now Angular will know to supply an instance of the `HeroService` when it creates a new `AppComponent`.
|
||||
|
||||
Learn more about Dependency Injection in the [Dependency Injection](../guide/dependency-injection.html) chapter.The *injector* does not know yet how to create a `HeroService`.
|
||||
Learn more about Dependency Injection in the [Dependency Injection](guide/dependency-injection) chapter.The *injector* does not know yet how to create a `HeroService`.
|
||||
If we ran our code now, Angular would fail with an error:
|
||||
<code-example format="nocode">
|
||||
EXCEPTION: No provider for HeroService! (AppComponent -> HeroService)
|
||||
@ -272,7 +272,7 @@ Angular offers a number of interfaces for tapping into critical moments in the c
|
||||
at creation, after each change, and at its eventual destruction.
|
||||
|
||||
Each interface has a single method. When the component implements that method, Angular calls it at the appropriate time.
|
||||
Learn more about lifecycle hooks in the [Lifecycle Hooks](../guide/lifecycle-hooks.html) chapter.Here's the essential outline for the `OnInit` interface:
|
||||
Learn more about lifecycle hooks in the [Lifecycle Hooks](guide/lifecycle-hooks) chapter.Here's the essential outline for the `OnInit` interface:
|
||||
|
||||
{@example 'toh-4/ts/src/app/app.component.1.ts' region='on-init'}
|
||||
|
||||
@ -332,7 +332,7 @@ in the callback is more succinct than the equivalent function expression and gra
|
||||
|
||||
Our app should still be running, still showing a list of heroes, and still
|
||||
responding to a name selection with a detail view.
|
||||
Checkout the "[Take it slow](#slow)" appendix to see what the app might be like with a poor connection.### Review the App Structure
|
||||
Checkout the "[Take it slow](tutorial/toh-pt4#slow)" appendix to see what the app might be like with a poor connection.### Review the App Structure
|
||||
Let’s verify that we have the following structure after all of our good refactoring in this chapter:
|
||||
|
||||
<aio-filetree>
|
||||
@ -456,7 +456,7 @@ Our Tour of Heroes has become more reusable using shared components and services
|
||||
We want to create a dashboard, add menu links that route between the views, and format data in a template.
|
||||
As our app evolves, we’ll learn how to design it to make it easier to grow and maintain.
|
||||
|
||||
We learn about Angular Component Router and navigation among the views in the [next tutorial](toh-pt5.html) chapter.
|
||||
We learn about Angular Component Router and navigation among the views in the [next tutorial](tutorial/toh-pt5) chapter.
|
||||
|
||||
<a id="slow"></a>### Appendix: Take it slow
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
Routing
|
||||
|
||||
@intro
|
||||
We add the Angular Router and learn to navigate among the views
|
||||
We add the Angular Router and learn to navigate among the views.
|
||||
|
||||
@description
|
||||
We received new requirements for our Tour of Heroes application:
|
||||
@ -20,7 +20,7 @@ When we’re done, users will be able to navigate the app like this:
|
||||
|
||||
We'll add Angular’s *Router* to our app to satisfy these requirements.
|
||||
|
||||
The [Routing and Navigation](../guide/router.html) chapter covers the router
|
||||
The [Routing and Navigation](guide/router) chapter covers the router
|
||||
in more detail than we will in this tutorial.
|
||||
Run the <live-example></live-example> for this part.
|
||||
|
||||
@ -107,7 +107,7 @@ at the top of the `<head>` section.
|
||||
base href is essential
|
||||
</header>
|
||||
|
||||
See the *base href* section of the [router](../guide/router.html#base-href)
|
||||
See the *base href* section of the [router](guide/router)
|
||||
guide to learn why this matters, and what to add if the `base`
|
||||
element is missing.
|
||||
|
||||
@ -131,7 +131,7 @@ This *route definition* has the following parts:
|
||||
it *must* begin with a capital letter to avoid confusion with the *path* (`Heroes`).</li>
|
||||
- **component**: the component that the router should create when navigating to this route (`HeroesComponent`).
|
||||
|
||||
Learn more about defining routes with `!{_RoutesVsAtRouteConfig}` in the [Routing](../guide/router.html) chapter.
|
||||
Learn more about defining routes with `!{_RoutesVsAtRouteConfig}` in the [Routing](guide/router) chapter.
|
||||
### Router Outlet
|
||||
|
||||
If we paste the path, `/heroes`, into the browser address bar,
|
||||
@ -322,7 +322,7 @@ using the `Location` service we injected previously.
|
||||
|
||||
Going back too far could take us out of the application.
|
||||
That's acceptable in a demo. We'd guard against it in a real application,
|
||||
perhaps with the [!{_CanDeactivateGuard}](../api/!{_CanDeactivateGuardUri}.html).
|
||||
perhaps with the [!{_CanDeactivateGuard}](api/!{_CanDeactivateGuardUri}).
|
||||
Then we wire this method with an event binding to a *Back* button that we
|
||||
add to the bottom of the component template.
|
||||
Modifying the template to add this button spurs us to take one more
|
||||
@ -352,7 +352,7 @@ with `<a>` tags. The opening `<a>` tag looks like this:
|
||||
Notice the `[routerLink]` binding.
|
||||
|
||||
Top level navigation in the [`AppComponent`
|
||||
template](#router-links) has router links set to fixed !{_pathVsName}s of the
|
||||
template](tutorial/toh-pt5#router-links) has router links set to fixed !{_pathVsName}s of the
|
||||
destination routes, "/dashboard" and "/heroes".
|
||||
|
||||
This time, we're binding to an expression containing a **link parameters !{_array}**.
|
||||
@ -405,7 +405,7 @@ that we slipped into the interpolation binding. Look for it right after the pipe
|
||||
Pipes are a good way to format strings, currency amounts, dates and other display data.
|
||||
Angular ships with several pipes and we can write our own.
|
||||
|
||||
Learn about pipes in the [Pipes](../guide/pipes.html) chapter.
|
||||
Learn about pipes in the [Pipes](guide/pipes) chapter.
|
||||
### Move content out of the component file
|
||||
|
||||
We are not done. We still have to update the component class to support navigation to the
|
||||
@ -490,10 +490,10 @@ It's pretty easy to package it all up and re-use the component somewhere else.
|
||||
We can also create styles at the *application level* outside of any component.
|
||||
|
||||
Our designers provided some basic styles to apply to elements across the entire app.
|
||||
These correspond to the full set of master styles that we installed earlier during [setup](../guide/setup.html).
|
||||
These correspond to the full set of master styles that we installed earlier during [setup](guide/setup).
|
||||
Here is an excerpt:
|
||||
Create the file <span ngio-ex>styles.css</span>, if it doesn't exist already.
|
||||
Ensure that it contains the [master styles given here](!{styles_css}).
|
||||
Ensure that it contains the [master styles given here](tutorial/!{styles_css}).
|
||||
|
||||
If necessary, also edit <span ngio-ex>index.html</span> to refer to this stylesheet.
|
||||
Look at the app now. Our dashboard, heroes, and navigation links are styling!
|
||||
|
@ -2,7 +2,7 @@
|
||||
HTTP
|
||||
|
||||
@intro
|
||||
We convert our service and components to use Angular's HTTP service
|
||||
We convert our service and components to use Angular's HTTP service.
|
||||
|
||||
@description
|
||||
Our stakeholders appreciate our progress.
|
||||
@ -15,7 +15,7 @@ Run the <live-example></live-example> for this part.
|
||||
|
||||
## Where We Left Off
|
||||
|
||||
In the [previous chapter](toh-pt5.html), we learned to navigate between the dashboard and the fixed heroes list, editing a selected hero along the way.
|
||||
In the [previous chapter](tutorial/toh-pt5), we learned to navigate between the dashboard and the fixed heroes list, editing a selected hero along the way.
|
||||
That's our starting point for this chapter.
|
||||
The application runs and updates automatically as we continue to build the Tour of Heroes.
|
||||
|
||||
@ -289,5 +289,5 @@ Here are the files we _added or changed_ in this chapter.
|
||||
|
||||
### Next Step
|
||||
|
||||
Return to the [learning path](../guide/learning-angular.html#architecture) where
|
||||
Return to the [learning path](guide/learning-angular) where
|
||||
you can read about the concepts and practices you discovered in this tutorial.
|
@ -1,3 +1,4 @@
|
||||
import { browser, element, by, promise } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
describe('site App', function() {
|
||||
@ -9,10 +10,66 @@ describe('site App', function() {
|
||||
});
|
||||
|
||||
it('should show features text after clicking "Features"', () => {
|
||||
page.featureLink.click().then(() => {
|
||||
expect(page.getDocViewerText()).toContain('Progressive web apps');
|
||||
page.getLink('features').click().then(() => {
|
||||
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
|
||||
});
|
||||
});
|
||||
|
||||
it('should convert a doc with a code-example');
|
||||
it('should show the tutorial index page at `/tutorial/`', () => {
|
||||
// check that we can navigate directly to the tutorial page
|
||||
page.navigateTo('tutorial/');
|
||||
expect(page.getDocViewerText()).toMatch(/Tutorial: Tour of Heroes/i);
|
||||
|
||||
// navigate to a different page
|
||||
page.getLink('features').click();
|
||||
|
||||
// check that we can navigate to the tutorial page via a link in the navigation
|
||||
const heading = page.getNavHeading(/tutorial/i);
|
||||
expect(heading.getText()).toMatch(/tutorial/i);
|
||||
heading.click();
|
||||
page.getLink('tutorial/').click();
|
||||
expect(page.getDocViewerText()).toMatch(/Tutorial: Tour of Heroes/i);
|
||||
});
|
||||
|
||||
it('should render `{@example}` dgeni tags as `<code-example>` elements with HTML escaped content', () => {
|
||||
page.navigateTo('guide/component-styles');
|
||||
const codeExample = element.all(by.css('code-example')).first();
|
||||
expect(page.getInnerHtml(codeExample))
|
||||
.toContain('@Component({\n selector: \'hero-app\',\n template: `\n <h1>Tour of Heroes</h1>');
|
||||
});
|
||||
|
||||
describe('api-docs', () => {
|
||||
it('should show a link to github', () => {
|
||||
page.navigateTo('api/common/NgClass');
|
||||
expect(page.ghLink.getAttribute('href'))
|
||||
.toMatch(/https:\/\/github.com\/angular\/angular\/tree\/.+\/packages\/common\/src\/directives\/ng_class\.ts/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('google analytics', () => {
|
||||
beforeEach(done => page.gaReady.then(done));
|
||||
|
||||
it('should call ga', done => {
|
||||
page.ga()
|
||||
.then(calls => {
|
||||
expect(calls.length).toBeGreaterThan(2, 'ga calls');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call ga with initial URL', done => {
|
||||
let path: string;
|
||||
|
||||
page.locationPath()
|
||||
.then(p => path = p)
|
||||
.then(() => page.ga().then(calls => {
|
||||
expect(calls.length).toBeGreaterThan(2, 'ga calls');
|
||||
expect(calls[1]).toEqual(['set', 'page', path]);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
// Todo: add test to confirm tracking URL when navigate.
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -1,18 +1,63 @@
|
||||
import { browser, element, by } from 'protractor';
|
||||
import { browser, element, by, promise, ElementFinder } from 'protractor';
|
||||
|
||||
const githubRegex = /https:\/\/github.com\/angular\/angular\//;
|
||||
|
||||
export class SitePage {
|
||||
|
||||
links = element.all(by.css('md-toolbar a'));
|
||||
docViewer = element(by.css('aio-doc-viewer'));
|
||||
codeExample = element.all(by.css('aio-doc-viewer pre > code'));
|
||||
featureLink = element(by.css('md-toolbar a[href="features"]'));
|
||||
ghLink = this.docViewer
|
||||
.all(by.css('a'))
|
||||
.filter((a: ElementFinder) => a.getAttribute('href').then(href => githubRegex.test(href)))
|
||||
.first();
|
||||
gaReady: promise.Promise<any>;
|
||||
getNavHeading(pattern: RegExp) {
|
||||
return element.all(by.css('aio-nav-item a'))
|
||||
.filter(element => element.getText().then(text => pattern.test(text)))
|
||||
.first();
|
||||
}
|
||||
getLink(path) { return element(by.css(`a[href="${path}"]`)); }
|
||||
ga() { return browser.executeScript('return window["gaCalls"]') as promise.Promise<any[][]>; }
|
||||
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
|
||||
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
navigateTo(pageUrl = '') {
|
||||
return browser.get('/' + pageUrl).then(_ => this.replaceGa(_));
|
||||
}
|
||||
|
||||
getDocViewerText() {
|
||||
return this.docViewer.getText();
|
||||
}
|
||||
|
||||
getInnerHtml(element) {
|
||||
// `getInnerHtml` was removed from webDriver and this is the workaround.
|
||||
// See https://github.com/angular/protractor/blob/master/CHANGELOG.md#breaking-changes
|
||||
return browser.executeScript('return arguments[0].innerHTML;', element);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the ambient Google Analytics tracker with homebrew spy
|
||||
* don't send commands to GA during e2e testing!
|
||||
* @param _ - forward's anything passed in
|
||||
*/
|
||||
private replaceGa(_: any) {
|
||||
|
||||
this.gaReady = browser.driver.executeScript(() => {
|
||||
// Give ga() a "ready" callback:
|
||||
// https://developers.google.com/analytics/devguides/collection/analyticsjs/command-queue-reference
|
||||
window['ga'](() => {
|
||||
window['gaCalls'] = [];
|
||||
window['ga'] = function() { window['gaCalls'].push(arguments); };
|
||||
});
|
||||
|
||||
})
|
||||
.then(() => {
|
||||
// wait for GaService to start using window.ga after analytics lib loads.
|
||||
const d = promise.defer();
|
||||
setTimeout(() => d.fulfill(), 1000); // GaService.initializeDelay
|
||||
return d.promise;
|
||||
});
|
||||
|
||||
return _;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,12 @@
|
||||
},
|
||||
"hosting": {
|
||||
"public": "dist",
|
||||
"cleanUrls": true
|
||||
"cleanUrls": true,
|
||||
"rewrites": [
|
||||
{
|
||||
"source": "**/!(*.*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
24
aio/ngsw-manifest.json
Normal file
24
aio/ngsw-manifest.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"static.ignore": [
|
||||
"\\.js\\.map$",
|
||||
"^\/assets\/images\/(bios|cookbooks|devguide|examples)\/",
|
||||
"^\/content\/docs\/.*\/"
|
||||
],
|
||||
"routing": {
|
||||
"index": "/index.html",
|
||||
"routes": {
|
||||
"/": {
|
||||
"prefix": false
|
||||
},
|
||||
"/about": {
|
||||
"prefix": true
|
||||
},
|
||||
"/tutorial": {
|
||||
"prefix": true
|
||||
},
|
||||
"/guide": {
|
||||
"prefix": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,17 +10,19 @@
|
||||
"ng": "yarn check-env && ng",
|
||||
"start": "yarn check-env && ng serve",
|
||||
"build": "yarn check-env && yarn docs && ng build -prod -sm",
|
||||
"test": "yarn check-env && ng test",
|
||||
"test": "yarn check-env && ng test --sourcemap=false",
|
||||
"lint": "yarn check-env && ng lint",
|
||||
"pree2e": "webdriver-manager update --standalone false --gecko false",
|
||||
"pree2e": "yarn ~~update-webdriver",
|
||||
"e2e": "yarn check-env && ng e2e --no-webdriver-update",
|
||||
"deploy-preview": "scripts/deploy-preview.sh",
|
||||
"deploy-staging": "firebase use staging --token \"$FIREBASE_TOKEN\" && yarn ~~deploy",
|
||||
"pre~~deploy": "yarn build",
|
||||
"~~deploy": "firebase deploy --message \"Commit: $TRAVIS_COMMIT\" --non-interactive --token \"$FIREBASE_TOKEN\"",
|
||||
"check-env": "node ../tools/check-environment.js",
|
||||
"predocs": "rimraf src/content",
|
||||
"docs": "dgeni ./transforms/angular.io-package",
|
||||
"docs-test": "node ../dist/tools/cjs-jasmine/index-tools ../../transforms/**/*.spec.js"
|
||||
"docs-test": "node ../dist/tools/cjs-jasmine/index-tools ../../transforms/**/*.spec.js",
|
||||
"~~update-webdriver": "webdriver-manager update --standalone false --gecko false",
|
||||
"pre~~deploy": "yarn build",
|
||||
"~~deploy": "firebase deploy --message \"Commit: $TRAVIS_COMMIT\" --non-interactive --token \"$FIREBASE_TOKEN\""
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
@ -35,20 +37,22 @@
|
||||
"@angular/platform-browser-dynamic": "next",
|
||||
"@angular/platform-server": "next",
|
||||
"@angular/router": "next",
|
||||
"@angular/service-worker": "^1.0.0-beta.7",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "^5.1.0",
|
||||
"rho": "https://github.com/petebacondarwin/rho#heading-fix",
|
||||
"rxjs": "^5.2.0",
|
||||
"ts-helpers": "^1.1.1",
|
||||
"zone.js": "^0.7.6"
|
||||
"zone.js": "^0.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "^1.0.0-rc.0",
|
||||
"@angular/cli": "^1.0.0-rc.4",
|
||||
"@angular/compiler-cli": "next",
|
||||
"@types/jasmine": "2.5.38",
|
||||
"@types/node": "~6.0.60",
|
||||
"canonical-path": "^0.0.2",
|
||||
"codelyzer": "~2.0.0-beta.4",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "^0.16.8",
|
||||
"dgeni-packages": "0.17.0",
|
||||
"entities": "^1.1.1",
|
||||
"firebase-tools": "^3.2.1",
|
||||
"gulp": "^3.9.1",
|
||||
@ -62,7 +66,7 @@
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"lodash": "^4.17.4",
|
||||
"protractor": "~5.1.0",
|
||||
"rho": "^0.3.0",
|
||||
"rimraf": "^2.6.1",
|
||||
"ts-node": "~2.0.0",
|
||||
"tslint": "~4.4.2",
|
||||
"typescript": "2.1.6"
|
||||
|
@ -11,7 +11,7 @@ UPLOAD_URL=$AIO_BUILDS_HOST/create-build/$TRAVIS_PULL_REQUEST/$TRAVIS_PULL_REQUE
|
||||
|
||||
cd "`dirname $0`/.."
|
||||
|
||||
yarn run build -- --prod
|
||||
yarn run build
|
||||
tar --create --gzip --directory "$INPUT_DIR" --file "$OUTPUT_FILE" .
|
||||
|
||||
exec 3>&1
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user