Compare commits
785 Commits
Author | SHA1 | Date | |
---|---|---|---|
345940bbc1 | |||
c49507b289 | |||
c730142508 | |||
27aa00b15f | |||
36a00a255b | |||
0e3249c89b | |||
920019ab70 | |||
82c8b44db7 | |||
bb3a307d5a | |||
dcb0ddaf5e | |||
4c1edd52c5 | |||
d512e27979 | |||
0619d82e0b | |||
a4038d5b94 | |||
e3d5e1fab7 | |||
035036308a | |||
0d29259d9b | |||
26b0f3dc96 | |||
5c9306b0fe | |||
3befb0e4b9 | |||
97bb88f10b | |||
6c7467a58b | |||
c579a85c12 | |||
400fdd08fd | |||
c1fe6c9c81 | |||
c58a0bea91 | |||
88a934b93c | |||
cde5cced69 | |||
472bedd3ea | |||
d8a06d03bd | |||
32020f9fb3 | |||
d574b14934 | |||
00c5d89e7d | |||
d2c8aefe64 | |||
ba796bbdd3 | |||
a0bb2ba7b7 | |||
f9fa3b5b6c | |||
f89d438116 | |||
1abe791d46 | |||
1502ae78b6 | |||
bad6e719de | |||
8c7129f3d2 | |||
dbff6f71e1 | |||
fcd934ccf6 | |||
45a8f340d9 | |||
5856513405 | |||
01fa3ee5c3 | |||
b8d69ffdf3 | |||
7ef60ec2a9 | |||
83f905c0e1 | |||
72ba3a3918 | |||
df10597da4 | |||
5db2e794a9 | |||
486aa06747 | |||
01ce1b32df | |||
c78b0b2c51 | |||
9ade1c3ea3 | |||
315a4cfcd4 | |||
11c04027ab | |||
eabe3b4c39 | |||
d471454675 | |||
bf57776b59 | |||
a32579ae5b | |||
780601d27a | |||
c909e731d7 | |||
9b8eb42354 | |||
0757174e8e | |||
3a43cdefe8 | |||
38c48beddd | |||
ad5749fb04 | |||
f6a838e9ee | |||
a6d1f4aaf1 | |||
eca8d11ee2 | |||
a195b7dbe4 | |||
083d7ec902 | |||
9d2d0cae6d | |||
c2f4a9bf71 | |||
231095fe8a | |||
28a532483a | |||
83853a215d | |||
8248307a99 | |||
67bd88b19a | |||
9f698b4de0 | |||
742f3d6787 | |||
323651bd38 | |||
9d397eb5a1 | |||
6114cd2bd4 | |||
d494f7bd5e | |||
ec6a7ab721 | |||
ad6d2b4619 | |||
c093390010 | |||
acd69f2be2 | |||
5d2f341653 | |||
420d1c35f5 | |||
08647267bb | |||
215d50d2f6 | |||
bf2cb6fa48 | |||
e97a2d4123 | |||
585e3f6adc | |||
7f77ce1a48 | |||
a1616ce181 | |||
1c22dff714 | |||
8d1d6e8f70 | |||
e7f4aba5a3 | |||
fdbe9f5d9f | |||
8bead6bfdd | |||
52dda73dbb | |||
31b3888a2f | |||
6f938470c2 | |||
776c4afc03 | |||
536dd647c6 | |||
51d581ab27 | |||
75294e7dad | |||
04bada7a9d | |||
2349143477 | |||
e9bff5fe9f | |||
411cb0cb92 | |||
53e1fb3554 | |||
2cb3b66640 | |||
5af3144330 | |||
e4043cbb3a | |||
fff424a35f | |||
b5d1c8b05a | |||
d713e33cc4 | |||
3d327d25f0 | |||
077283bf0f | |||
9ec25ea036 | |||
878cfe669c | |||
5f0be3cb2e | |||
9e28e14c08 | |||
954d002884 | |||
0a48591e53 | |||
d37c723951 | |||
9078ca557e | |||
2be1ef6ba0 | |||
47c02efccb | |||
d7ecfb432a | |||
59abf4a33f | |||
d6e715e726 | |||
fcfcd1037c | |||
f3ccd29e7b | |||
5c0bdae809 | |||
838902556b | |||
c6872c02d8 | |||
819982ea20 | |||
f9daa136c3 | |||
6a0d2ed6c8 | |||
2c1f35e794 | |||
5345e8da45 | |||
e35269dd87 | |||
60a03b7ef7 | |||
305b5a3887 | |||
bc549361d3 | |||
084b627f2e | |||
6755d00601 | |||
cba1da3e44 | |||
7be8bb1489 | |||
c7c0c1f626 | |||
3aa4629f92 | |||
2d86dbb090 | |||
91767ff0f9 | |||
078b004ecc | |||
930d204d83 | |||
8d82cdfc77 | |||
cb6996b5c3 | |||
a4f7740332 | |||
ba0faa2f77 | |||
3e68029522 | |||
b4e26b5828 | |||
15cf7fcac2 | |||
24ff0eb13b | |||
cf86f72eb7 | |||
61486f14f1 | |||
d16a7f3ecc | |||
82761ec50e | |||
235bfa77a9 | |||
299ae1bb1c | |||
80f7522dab | |||
028921e369 | |||
a4e11bb524 | |||
a4131752d2 | |||
060dcfbba1 | |||
4be7008f80 | |||
4a0d05515e | |||
83ab99c746 | |||
270da1f69f | |||
6b0e46e36c | |||
3642707145 | |||
0ea76edfd8 | |||
d493a83b2b | |||
f1721d5cef | |||
5b3fd6aa82 | |||
6f829180f7 | |||
27b95ba64a | |||
ef405b1e90 | |||
441073bad5 | |||
cfb37b8994 | |||
a1001f2ea0 | |||
6ca3fb19fd | |||
267f844811 | |||
59e7203a40 | |||
7db177d181 | |||
3f4232a23d | |||
2a634648c2 | |||
97e13991c5 | |||
d42a912343 | |||
03fef736d6 | |||
78b8d0e1ef | |||
aedac71491 | |||
89b44d1900 | |||
2fc5ae561b | |||
cb719ee16e | |||
ed46d9ebd7 | |||
1380c5642b | |||
e3d447229d | |||
327980bf49 | |||
4c30aa8343 | |||
e7c878027a | |||
886f90516d | |||
1faf9bbeb0 | |||
325ad7eb53 | |||
362fba4bde | |||
211d33ad59 | |||
383f04b96d | |||
4e96cdc23f | |||
772c5b8f64 | |||
8f1d0c317e | |||
deac994517 | |||
b754ab298a | |||
3a4e1b8f86 | |||
a73d2a7688 | |||
009f394237 | |||
5ac5ac1dec | |||
cc1e0bbdc0 | |||
ef9f8df9ed | |||
87f951c5c1 | |||
afd58b3739 | |||
c2c14c2a1f | |||
e19930e70d | |||
e7c017cfe3 | |||
8a3493af47 | |||
318e9372c9 | |||
22d80a6780 | |||
593e07bd21 | |||
3dfc7703c2 | |||
2ca2c8fe0f | |||
8004eb0eec | |||
89995075e6 | |||
c2fa65640c | |||
2d065a32ec | |||
fcb68cc139 | |||
01d1c1a9ab | |||
1754beb59c | |||
844208f463 | |||
1689f9378b | |||
011fdfa94f | |||
6466fb20c2 | |||
3f9b3d7666 | |||
8524688a87 | |||
7f58c579f3 | |||
3d7c85b2aa | |||
55c2433171 | |||
03afa36334 | |||
05f1df3224 | |||
901db0e730 | |||
564ca9e8ec | |||
20d377c11b | |||
2c5e873da0 | |||
42a9e5ad7d | |||
cda2530df5 | |||
aaa89bb715 | |||
ea971f7098 | |||
ddaa124162 | |||
7a30153aa1 | |||
7acd33007d | |||
8a56c99f87 | |||
77c0ef38be | |||
5f64e52ed4 | |||
b2a3325258 | |||
95f429c7e2 | |||
f16ca1ce46 | |||
508c555ce2 | |||
f97d8a9cbd | |||
24ec5235a0 | |||
bbd556ee9f | |||
466df41c8f | |||
4e1b5e43fa | |||
b0e362f75b | |||
91092f668e | |||
d268d2ad85 | |||
2486db7c2b | |||
0672a0e547 | |||
9846b19986 | |||
8b79075497 | |||
2956f21d82 | |||
c8ee390d23 | |||
d7440c452a | |||
a2b8dc1cfb | |||
7ebf35c5f8 | |||
dbcaf22805 | |||
0066a1ae70 | |||
491da99abe | |||
13ba84731f | |||
b58bd2bb91 | |||
a32cbed89c | |||
c42f35eb2f | |||
2418c6aca1 | |||
90205079d7 | |||
57a868627f | |||
2cacab92e6 | |||
0520ecb7d0 | |||
047642556c | |||
9edea0bb75 | |||
ce39755937 | |||
7d3f5043e1 | |||
14c0ec97d8 | |||
45f4a47286 | |||
141fcb95a4 | |||
dbf1f6b233 | |||
5d12c19ce9 | |||
352b9c78e0 | |||
019835c721 | |||
255c2b2ff0 | |||
0731fc47e4 | |||
7ffdbc05d2 | |||
4f3ac1d983 | |||
d7c70caa4c | |||
2cddeabce0 | |||
7addc9f1a9 | |||
cb6ddfc215 | |||
681741cd13 | |||
8fe532e593 | |||
198fc6f108 | |||
8d8e419664 | |||
1e208e8c87 | |||
286fbf42c6 | |||
40025f55ad | |||
7eb1a58b26 | |||
28f3c1c96a | |||
0c8adbc4ec | |||
ed1b4a8f19 | |||
bd30c37040 | |||
eb34aa551a | |||
1142c378fd | |||
82a3bd5e8b | |||
5f3fba43fa | |||
eca2246fbc | |||
df71c7c617 | |||
c184de175d | |||
8b65946eb8 | |||
de0c71c467 | |||
39e066add1 | |||
178e750f16 | |||
14be55c9fa | |||
fdd61152c5 | |||
7cc332efe6 | |||
1eba1aaf15 | |||
4e628a887a | |||
b6bc0aeb61 | |||
77d8afb7da | |||
42d1091d6a | |||
d0280a0335 | |||
97d6d909e7 | |||
f7815cf96d | |||
9e43e4900e | |||
8ce38cac0d | |||
a10c126692 | |||
b682bd1916 | |||
26eacd4fcb | |||
fc4741f638 | |||
3f3e9b7555 | |||
53a8459d5f | |||
0ec0ff3bce | |||
f930e75a80 | |||
854bd7d0c8 | |||
e17fe90aaa | |||
0577bf0e3e | |||
4c92cf43cf | |||
c6ecdc9a81 | |||
20cc3ab37e | |||
420b9be1c1 | |||
3c6c00d1d4 | |||
6fa36e387f | |||
9bde1be5a4 | |||
040253c426 | |||
c98a4d6ddd | |||
d5293d2aa3 | |||
d578ab8f3c | |||
9dbb30f884 | |||
fafa50d97f | |||
e010f2ca54 | |||
c9e0db55f7 | |||
ecffc3557f | |||
b861e9c0ac | |||
4213e8d5f0 | |||
bab90a7709 | |||
da79e0433f | |||
1b8752e595 | |||
b3cf5b246f | |||
0a695e181e | |||
a5d1d1161b | |||
a25c3c5c3b | |||
d1a8afbbd0 | |||
f4fcb0c54c | |||
e92b81351b | |||
53805f07ba | |||
e73daa3736 | |||
eef01160f4 | |||
333abf16e4 | |||
388dc93cee | |||
fbd281c26e | |||
420c179b39 | |||
35d61c11fd | |||
e30e1325f3 | |||
fc6c3ae97d | |||
63a28d6805 | |||
b95a336f12 | |||
b4df19c029 | |||
dbd0f8e699 | |||
b7fb92a048 | |||
2ff4b357d7 | |||
1c26f40cd4 | |||
2de34770c4 | |||
0fc6ed3c4a | |||
63455b839a | |||
70b25a3d4f | |||
2200c8a87b | |||
306f46ce92 | |||
1b2d6ea11b | |||
03198de73a | |||
bb0e57eccb | |||
a468f11c7c | |||
9d2241b901 | |||
88a235de3a | |||
7bd9e09c78 | |||
42c58d9fe8 | |||
b3713a112f | |||
e037840b88 | |||
ec6b9cc17d | |||
db4c59dad9 | |||
58ea040570 | |||
6046e86506 | |||
297dab845d | |||
2cb5f59acc | |||
0f3831b105 | |||
9d9d46f52b | |||
d9c4840a9c | |||
12fcc7cafe | |||
95a407d9dd | |||
e9300c979e | |||
df274b478e | |||
f8941a5b6b | |||
38f805cd06 | |||
98931bf9b5 | |||
9b85b533ff | |||
d805526659 | |||
ee435761fd | |||
35c6ed2675 | |||
72f534f7f8 | |||
3cb9b43851 | |||
bf0c520f2e | |||
ee35e223a7 | |||
dc9f4b994e | |||
49be32c931 | |||
1786586747 | |||
45c09416ed | |||
4779c4b94a | |||
793cb328de | |||
966598cda7 | |||
772ccf0d9f | |||
ff6e93163f | |||
dff5129661 | |||
e367593a26 | |||
16039d837e | |||
4665c35453 | |||
4c63241b34 | |||
9aa778e843 | |||
62ba0acfb5 | |||
52d2e46700 | |||
c89421bfa8 | |||
cebd4fada7 | |||
3d82aa781d | |||
89c589085d | |||
a6a7e1bb99 | |||
70dd27ffd8 | |||
7bc5bcde34 | |||
aa0dd0d5ff | |||
e0aa39929b | |||
4172707346 | |||
7c8c41353b | |||
1d9375d115 | |||
f27deea003 | |||
00e6cb1d62 | |||
cae2a893f2 | |||
b34fb04cbf | |||
2365bb89d7 | |||
7b5a0ba8c3 | |||
ad8c4cdd75 | |||
f5ff2068a4 | |||
90a2796a7e | |||
76190c71fc | |||
dd049caf0a | |||
bcd31cb857 | |||
e6161ca307 | |||
d6f6cd0cb1 | |||
b7f2a033df | |||
e37f2663c2 | |||
5e579c4dc9 | |||
f0ec4ae941 | |||
b80b43ddae | |||
f0c570bd41 | |||
e5d2853070 | |||
152293d3cf | |||
ceb61d10c1 | |||
dd806b0d99 | |||
113c75dd27 | |||
b7acf07a70 | |||
519f2baff0 | |||
3962908d36 | |||
afd2cbc3c4 | |||
4275b3412f | |||
1b3ee17888 | |||
8306da5ce1 | |||
75dff8a93a | |||
fc1b557d98 | |||
47f98673c7 | |||
781f5611d1 | |||
deea6da0e0 | |||
43622d32e4 | |||
5db8d93fe5 | |||
535c249f3a | |||
942b986ef3 | |||
acf6075ca9 | |||
28995dba19 | |||
f186c32245 | |||
d7c77cb175 | |||
51b09244ff | |||
458dc10ddc | |||
4341743b4a | |||
b5e92b9a5d | |||
28ff02b116 | |||
f4ef8a92f8 | |||
d7e9d8746a | |||
351759906b | |||
0c2ed4c3e5 | |||
37bfb14956 | |||
81d23b33ef | |||
bb150c2704 | |||
46ff673192 | |||
7842f73e8b | |||
e5f9e3d018 | |||
aebd5f9af6 | |||
4fb034aeec | |||
4da901f8c0 | |||
39b043646b | |||
4480ba3e29 | |||
5e79799b89 | |||
1fa1dd5aaa | |||
8be0972836 | |||
78840e59a7 | |||
c332d4d916 | |||
bb944eecd6 | |||
443f5eee85 | |||
7e5e60b757 | |||
33df4b74da | |||
3c14e9612f | |||
cabf997933 | |||
2ed7146393 | |||
bfda7ca5bd | |||
305b059809 | |||
297b7da166 | |||
6aac91743b | |||
f06d5f02b3 | |||
4a9f0bebc3 | |||
88b0985bad | |||
1e3b8a1354 | |||
5a09753e07 | |||
02bfa9e0e0 | |||
306ff3cd54 | |||
ec5c108b60 | |||
4d9da9b0a1 | |||
3ce7c87cbd | |||
061107c5f6 | |||
9f039d40c1 | |||
b1f1d3ffd2 | |||
03ff380e13 | |||
c6e5fc4fbe | |||
074266b896 | |||
c4dc642f99 | |||
e041ac6f0d | |||
66effde9f3 | |||
663b768780 | |||
0147de9044 | |||
b010849102 | |||
698b0288be | |||
5e80e7e216 | |||
d50cb30443 | |||
29122c518b | |||
58e175318c | |||
f88e63519a | |||
0cc53fb398 | |||
713bce19f4 | |||
d972821889 | |||
2d16b4711e | |||
9181b73e5a | |||
5737e65700 | |||
ca677481a2 | |||
6ab43d7335 | |||
fee316161d | |||
2e4244d521 | |||
3bedfdac9d | |||
ec0ce6005a | |||
a185efbd60 | |||
4aa4e6fd03 | |||
078b0be4dc | |||
13ae2d7271 | |||
99b93e55cb | |||
80e6c07d89 | |||
34aa5570ed | |||
e526f74dfd | |||
9e78f55c32 | |||
f1731d807e | |||
92c4f3d508 | |||
e92fce1c27 | |||
83a9159063 | |||
0a69a2832b | |||
717df13207 | |||
b40b5576df | |||
03f2f1ae47 | |||
a8978ebf8e | |||
7d7b59efa5 | |||
4cf89d4bd7 | |||
d47b318fee | |||
e485236502 | |||
41667de778 | |||
ef4736d052 | |||
db4a448439 | |||
7549c65502 | |||
1beccc1a6c | |||
0075017b43 | |||
4374931b0e | |||
64022f51d4 | |||
f9f6e2e1b3 | |||
aecf9de738 | |||
81195a238b | |||
96a3de6364 | |||
00027130ea | |||
4a18428de6 | |||
7f28845f58 | |||
1dd0b6cc18 | |||
e0415dbf16 | |||
95fc3d4c5c | |||
e145fa13b1 | |||
5fa7b8ba56 | |||
2c7d366c82 | |||
2dd6f25647 | |||
d2623f13d9 | |||
7d0af179e3 | |||
416c786774 | |||
93302b7fb8 | |||
eb8c6c7eff | |||
8660806ddc | |||
4c5e085c93 | |||
d707124fd9 | |||
aece3669e5 | |||
09c84169d5 | |||
d43c30688a | |||
ee70a18a75 | |||
76a8cd57ae | |||
4c7f89ff6d | |||
f9fb8338f5 | |||
6b3aa60446 | |||
2463548fa7 | |||
1140bbc25c | |||
1b4df6484e | |||
f40d51733a | |||
e893c5a330 | |||
8be8466a00 | |||
c8bef1259c | |||
568e9df1d6 | |||
a555fdba32 | |||
36535e9abd | |||
6402a9ae2a | |||
ca25c957bf | |||
93f07aee6c | |||
0af6e9fcbb | |||
008e12edda | |||
1927d0c7db | |||
9a0a90feb3 | |||
76d86d5a07 | |||
de7a9a3b93 | |||
04f61c0c3e | |||
c810ac7153 | |||
c24ad560fa | |||
3d2db5c5f0 | |||
32eafef6a7 | |||
2366480250 | |||
8e55a11283 | |||
e9de28111d | |||
75afd80ae8 | |||
a5eb0e56b6 | |||
4d458db1b5 | |||
24a92472bf | |||
43006bcc45 | |||
326240eb91 | |||
b59bc0e38c | |||
5516802142 | |||
cc4b813e75 | |||
64631063ae | |||
719224bffd | |||
fe2b6923ba | |||
38ad1d97ab | |||
372b9101e2 | |||
7e62aa0c6e | |||
36e927a8c6 | |||
9d8bb634f9 | |||
c5c57f6737 | |||
51a89c32c4 | |||
e1ac2efe4a | |||
702e17cfe2 | |||
4d1d0fa03b | |||
ec8bae1b27 | |||
08348fc2e8 | |||
63fbc71439 | |||
b44f7b5e16 | |||
c5df9ce474 | |||
2510e7dad6 | |||
80c68583d1 | |||
5b6ced5599 | |||
a383116b43 | |||
c6dd900f60 | |||
5ac308060d | |||
bc089abd32 | |||
c94a33c525 | |||
5cee709266 | |||
2d0847c307 | |||
995cd15a69 | |||
16497438d6 | |||
b54db86f43 | |||
44acf6734b | |||
b07b6edc2a | |||
83e4a76afa | |||
bfa7b1a494 | |||
8e0dec538e | |||
1c385a1c22 | |||
f9bc84cd99 | |||
2d7c95fb70 | |||
29e8a64cf0 | |||
00efacf561 | |||
d96995b4e3 | |||
fc3e5cb6d3 | |||
d37dad82f1 | |||
22710fc353 | |||
4f66250618 | |||
d783519835 | |||
b8e9a30d3b | |||
32ce8b1326 | |||
ff4eb0cb63 | |||
9ba46d9f88 | |||
b14ac96750 | |||
55dac05cf2 | |||
ae28d7c0b2 | |||
380de1e7b4 | |||
4f9717331d | |||
36fc28642a | |||
a323b9b1a3 | |||
58f4254fba | |||
4419907b08 | |||
9d415f9c3f | |||
fced8ee40e | |||
1cb7b88505 | |||
0e76b32aa5 | |||
e81ad3a1bc | |||
c3a85ceabc | |||
85e0e366df | |||
0ce8ad3493 | |||
528e25a81a | |||
d25012e864 | |||
c21c46a8e8 | |||
9cc8bd5f7d | |||
ae3eaf8b16 | |||
d714b95fb9 | |||
912692137a | |||
6fc85073d2 | |||
989dea7083 |
@ -1,3 +1,3 @@
|
|||||||
2.1.1
|
3.2.0
|
||||||
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
# [NB: this comment has to be after the first line, see https://github.com/bazelbuild/bazelisk/issues/117]
|
||||||
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
# When updating the Bazel version you also need to update the RBE toolchains version in package.bzl
|
||||||
|
@ -12,7 +12,7 @@ We use this as a symmetric AES encryption key to encrypt tokens like
|
|||||||
a GitHub token that enables publishing snapshots.
|
a GitHub token that enables publishing snapshots.
|
||||||
|
|
||||||
To create the github_token file, we take this approach:
|
To create the github_token file, we take this approach:
|
||||||
- Find the angular-builds:token in http://valentine
|
- Find the angular-builds:token in the internal pw database
|
||||||
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
|
- Go inside the CircleCI default docker image so you use the same version of openssl as we will at runtime: `docker run --rm -it circleci/node:10.12`
|
||||||
- echo "https://[token]:@github.com" > credentials
|
- echo "https://[token]:@github.com" > credentials
|
||||||
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
- openssl aes-256-cbc -e -in credentials -out .circleci/github_token -k $KEY
|
||||||
|
@ -19,4 +19,12 @@ build --local_ram_resources=14336
|
|||||||
|
|
||||||
# All build executed remotely should be done using our RBE configuration.
|
# All build executed remotely should be done using our RBE configuration.
|
||||||
build:remote --google_default_credentials
|
build:remote --google_default_credentials
|
||||||
|
|
||||||
|
# Upload to GCP's Build Status viewer to allow for us to have better viewing of execution/build
|
||||||
|
# logs. This is only done on CI as the BES (GCP's Build Status viewer) API requires credentials
|
||||||
|
# from service accounts, rather than end user accounts.
|
||||||
|
build:remote --bes_backend=buildeventservice.googleapis.com
|
||||||
|
build:remote --bes_timeout=30s
|
||||||
|
build:remote --bes_results_url="https://source.cloud.google.com/results/invocations/"
|
||||||
|
|
||||||
build --config=remote
|
build --config=remote
|
||||||
|
@ -22,18 +22,18 @@ version: 2.1
|
|||||||
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
# **NOTE 1 **: If you change the cache key prefix, also sync the cache_key_fallback to match.
|
||||||
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
# **NOTE 2 **: Keep the static part of the cache key as prefix to enable correct fallbacks.
|
||||||
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
# See https://circleci.com/docs/2.0/caching/#restoring-cache for how prefixes work in CircleCI.
|
||||||
var_3: &cache_key v6-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
var_3: &cache_key v7-angular-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
# We invalidate the cache if the Bazel version changes because otherwise the `bazelisk` cache
|
||||||
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
# folder will contain all previously used versions and ultimately cause the cache restoring to
|
||||||
# be slower due to its growing size.
|
# be slower due to its growing size.
|
||||||
var_4: &cache_key_fallback v6-angular-node-12-{{ checksum ".bazelversion" }}
|
var_4: &cache_key_fallback v7-angular-node-12-{{ checksum ".bazelversion" }}
|
||||||
var_3_win: &cache_key_win v6-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
var_3_win: &cache_key_win v7-angular-win-node-12-{{ checksum ".bazelversion" }}-{{ checksum "yarn.lock" }}-{{ checksum "WORKSPACE" }}-{{ checksum "packages/bazel/package.bzl" }}-{{ checksum "aio/yarn.lock" }}
|
||||||
var_4_win: &cache_key_win_fallback v6-angular-win-node-12-{{ checksum ".bazelversion" }}
|
var_4_win: &cache_key_win_fallback v7-angular-win-node-12-{{ checksum ".bazelversion" }}
|
||||||
|
|
||||||
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
# Cache key for the `components-repo-unit-tests` job. **Note** when updating the SHA in the
|
||||||
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
# cache keys also update the SHA for the "COMPONENTS_REPO_COMMIT" environment variable.
|
||||||
var_5: &components_repo_unit_tests_cache_key v6-angular-components-598db096e668aa7e9debd56eedfd127b7a55e371
|
var_5: &components_repo_unit_tests_cache_key v7-angular-components-189d98e8b01b33974328255f085de04251d61567
|
||||||
var_6: &components_repo_unit_tests_cache_key_fallback v6-angular-components-
|
var_6: &components_repo_unit_tests_cache_key_fallback v7-angular-components-
|
||||||
|
|
||||||
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
# Workspace initially persisted by the `setup` job, and then enhanced by `build-npm-packages` and
|
||||||
# `build-ivy-npm-packages`.
|
# `build-ivy-npm-packages`.
|
||||||
@ -67,9 +67,6 @@ var_10: &only_on_master
|
|||||||
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
# **NOTE 1**: Pin to exact images using an ID (SHA). See https://circleci.com/docs/2.0/circleci-images/#using-a-docker-image-id-to-pin-an-image-to-a-fixed-version.
|
||||||
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
# (Using the tag in not necessary when pinning by ID, but include it anyway for documentation purposes.)
|
||||||
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
# **NOTE 2**: If you change the version of the docker images, also change the `cache_key` suffix.
|
||||||
# **NOTE 3**: If you change the version of the `*-browsers` docker image, make sure the
|
|
||||||
# `--versions.chrome` arg in `integration/bazel-schematics/test.sh` specifies a
|
|
||||||
# ChromeDriver version that is compatible with the Chrome version in the image.
|
|
||||||
executors:
|
executors:
|
||||||
default-executor:
|
default-executor:
|
||||||
parameters:
|
parameters:
|
||||||
@ -120,7 +117,7 @@ commands:
|
|||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
|
# Install GTK+ graphical user interface (libgtk-3-0), advanced linux sound architecture (libasound2)
|
||||||
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
|
# and network security service libraries (libnss3) & X11 Screen Saver extension library (libssx1)
|
||||||
# which are dependendies of chrome & needed for karma & protractor headless chrome tests.
|
# which are dependencies of chrome & needed for karma & protractor headless chrome tests.
|
||||||
# This is a very small install which takes around 7s in comparing to using the full
|
# This is a very small install which takes around 7s in comparing to using the full
|
||||||
# circleci/node:x.x.x-browsers image.
|
# circleci/node:x.x.x-browsers image.
|
||||||
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
sudo apt-get -y install libgtk-3-0 libasound2 libnss3 libxss1
|
||||||
@ -163,7 +160,7 @@ commands:
|
|||||||
description: Sets up a domain that resolves to the local host.
|
description: Sets up a domain that resolves to the local host.
|
||||||
steps:
|
steps:
|
||||||
- run:
|
- run:
|
||||||
name: Preparing environment for running tests on Saucelabs.
|
name: Preparing environment for running tests on Sauce Labs.
|
||||||
command: |
|
command: |
|
||||||
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
# For SauceLabs jobs, we set up a domain which resolves to the machine which launched
|
||||||
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
# the tunnel. We do this because devices are sometimes not able to properly resolve
|
||||||
@ -175,13 +172,13 @@ commands:
|
|||||||
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
setSecretVar SAUCE_ACCESS_KEY $(echo $SAUCE_ACCESS_KEY | rev)
|
||||||
- run:
|
- run:
|
||||||
# Sets up a local domain in the machine's host file that resolves to the local
|
# Sets up a local domain in the machine's host file that resolves to the local
|
||||||
# host. This domain is helpful in Saucelabs tests where devices are not able to
|
# host. This domain is helpful in Sauce Labs tests where devices are not able to
|
||||||
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
# properly resolve `localhost` or `127.0.0.1` through the sauce-connect tunnel.
|
||||||
name: Setting up alias domain for local host.
|
name: Setting up alias domain for local host.
|
||||||
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
command: echo "127.0.0.1 $SAUCE_LOCALHOST_ALIAS_DOMAIN" | sudo tee -a /etc/hosts
|
||||||
|
|
||||||
# Normally this would be an individual job instead of a command.
|
# Normally this would be an individual job instead of a command.
|
||||||
# But startup and setup time for each invidual windows job are high enough to discourage
|
# But startup and setup time for each individual windows job are high enough to discourage
|
||||||
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
# many small jobs, so instead we use a command for setup unless the gain becomes significant.
|
||||||
setup_win:
|
setup_win:
|
||||||
description: Setup windows node environment
|
description: Setup windows node environment
|
||||||
@ -599,8 +596,8 @@ jobs:
|
|||||||
- run:
|
- run:
|
||||||
name: Decrypt github credentials
|
name: Decrypt github credentials
|
||||||
# We need ensure that the same default digest is used for encoding and decoding with
|
# We need ensure that the same default digest is used for encoding and decoding with
|
||||||
# openssl. Openssl versions might have different default digests which can cause
|
# OpenSSL. OpenSSL versions might have different default digests which can cause
|
||||||
# decryption failures based on the installed openssl version. https://stackoverflow.com/a/39641378/4317734
|
# decryption failures based on the installed OpenSSL version. https://stackoverflow.com/a/39641378/4317734
|
||||||
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
command: 'openssl aes-256-cbc -d -in .circleci/github_token -md md5 -k "${KEY}" -out ~/.git_credentials'
|
||||||
- run: ./scripts/ci/publish-build-artifacts.sh
|
- run: ./scripts/ci/publish-build-artifacts.sh
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ setPublicVar COMPONENTS_REPO_TMP_DIR "/tmp/angular-components-repo"
|
|||||||
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
setPublicVar COMPONENTS_REPO_URL "https://github.com/angular/components.git"
|
||||||
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
setPublicVar COMPONENTS_REPO_BRANCH "master"
|
||||||
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
# **NOTE**: When updating the commit SHA, also update the cache key in the CircleCI `config.yml`.
|
||||||
setPublicVar COMPONENTS_REPO_COMMIT "598db096e668aa7e9debd56eedfd127b7a55e371"
|
setPublicVar COMPONENTS_REPO_COMMIT "189d98e8b01b33974328255f085de04251d61567"
|
||||||
|
|
||||||
|
|
||||||
####################################################################################################
|
####################################################################################################
|
||||||
|
@ -60,14 +60,15 @@ if (require.resolve === module) {
|
|||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
function _main(args) {
|
function _main(args) {
|
||||||
triggerWebhook(...args).
|
triggerWebhook(...args)
|
||||||
then(({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
.then(
|
||||||
console.log(`Status: ${statusCode}\n${responseText}`) :
|
({statusCode, responseText}) => (200 <= statusCode && statusCode < 400) ?
|
||||||
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`))).
|
console.log(`Status: ${statusCode}\n${responseText}`) :
|
||||||
catch(err => {
|
Promise.reject(new Error(`Request failed (status: ${statusCode}): ${responseText}`)))
|
||||||
console.error(err);
|
.catch(err => {
|
||||||
process.exit(1);
|
console.error(err);
|
||||||
});
|
process.exit(1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function postJson(url, data) {
|
function postJson(url, data) {
|
||||||
@ -77,15 +78,12 @@ function postJson(url, data) {
|
|||||||
const statusCode = res.statusCode || -1;
|
const statusCode = res.statusCode || -1;
|
||||||
let responseText = '';
|
let responseText = '';
|
||||||
|
|
||||||
res.
|
res.on('error', reject)
|
||||||
on('error', reject).
|
.on('data', d => responseText += d)
|
||||||
on('data', d => responseText += d).
|
.on('end', () => resolve({statusCode, responseText}));
|
||||||
on('end', () => resolve({statusCode, responseText}));
|
|
||||||
};
|
};
|
||||||
|
|
||||||
request(url, opts, onResponse).
|
request(url, opts, onResponse).on('error', reject).end(JSON.stringify(data));
|
||||||
on('error', reject).
|
|
||||||
end(JSON.stringify(data));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,72 +0,0 @@
|
|||||||
{
|
|
||||||
"commitMessage": {
|
|
||||||
"maxLength": 120,
|
|
||||||
"minBodyLength": 100,
|
|
||||||
"types": [
|
|
||||||
"build",
|
|
||||||
"ci",
|
|
||||||
"docs",
|
|
||||||
"feat",
|
|
||||||
"fix",
|
|
||||||
"perf",
|
|
||||||
"refactor",
|
|
||||||
"release",
|
|
||||||
"style",
|
|
||||||
"test"
|
|
||||||
],
|
|
||||||
"scopes": [
|
|
||||||
"animations",
|
|
||||||
"bazel",
|
|
||||||
"benchpress",
|
|
||||||
"changelog",
|
|
||||||
"common",
|
|
||||||
"compiler",
|
|
||||||
"compiler-cli",
|
|
||||||
"core",
|
|
||||||
"dev-infra",
|
|
||||||
"docs-infra",
|
|
||||||
"elements",
|
|
||||||
"forms",
|
|
||||||
"http",
|
|
||||||
"language-service",
|
|
||||||
"localize",
|
|
||||||
"ngcc",
|
|
||||||
"packaging",
|
|
||||||
"platform-browser",
|
|
||||||
"platform-browser-dynamic",
|
|
||||||
"platform-server",
|
|
||||||
"platform-webworker",
|
|
||||||
"platform-webworker-dynamic",
|
|
||||||
"router",
|
|
||||||
"service-worker",
|
|
||||||
"upgrade",
|
|
||||||
"ve",
|
|
||||||
"zone.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"format": {
|
|
||||||
"clang-format": {
|
|
||||||
"matchers": [
|
|
||||||
"dev-infra/**/*.{js,ts}",
|
|
||||||
"packages/**/*.{js,ts}",
|
|
||||||
"!packages/zone.js",
|
|
||||||
"!packages/common/locales/**/*.{js,ts}",
|
|
||||||
"!packages/common/src/i18n/available_locales.ts",
|
|
||||||
"!packages/common/src/i18n/currencies.ts",
|
|
||||||
"!packages/common/src/i18n/locale_en.ts",
|
|
||||||
"modules/benchmarks/**/*.{js,ts}",
|
|
||||||
"modules/playground/**/*.{js,ts}",
|
|
||||||
"tools/**/*.{js,ts}",
|
|
||||||
"!tools/gulp-tasks/cldr/extract.js",
|
|
||||||
"!tools/public_api_guard/**/*.d.ts",
|
|
||||||
"!tools/ts-api-guardian/test/fixtures/**",
|
|
||||||
"./*.{js,ts}",
|
|
||||||
"!**/node_modules/**",
|
|
||||||
"!**/dist/**",
|
|
||||||
"!**/built/**",
|
|
||||||
"!shims_for_IE.js"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"buildifier": true
|
|
||||||
}
|
|
||||||
}
|
|
2
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
2
.github/ISSUE_TEMPLATE/1-bug-report.md
vendored
@ -32,7 +32,7 @@ Existing issues often contain information about workarounds, resolution, or prog
|
|||||||
|
|
||||||
## 🔬 Minimal Reproduction
|
## 🔬 Minimal Reproduction
|
||||||
<!--
|
<!--
|
||||||
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-issue-repro2
|
Please create and share minimal reproduction of the issue starting with this template: https://stackblitz.com/fork/angular-ivy
|
||||||
-->
|
-->
|
||||||
<!-- ✍️--> https://stackblitz.com/...
|
<!-- ✍️--> https://stackblitz.com/...
|
||||||
|
|
||||||
|
119
.ng-dev/config.ts
Normal file
119
.ng-dev/config.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import {MergeConfig} from '../dev-infra/pr/merge/config';
|
||||||
|
|
||||||
|
// The configuration for `ng-dev commit-message` commands.
|
||||||
|
const commitMessage = {
|
||||||
|
'maxLength': 120,
|
||||||
|
'minBodyLength': 100,
|
||||||
|
'types': [
|
||||||
|
'build',
|
||||||
|
'ci',
|
||||||
|
'docs',
|
||||||
|
'feat',
|
||||||
|
'fix',
|
||||||
|
'perf',
|
||||||
|
'refactor',
|
||||||
|
'release',
|
||||||
|
'style',
|
||||||
|
'test',
|
||||||
|
],
|
||||||
|
'scopes': [
|
||||||
|
'animations',
|
||||||
|
'bazel',
|
||||||
|
'benchpress',
|
||||||
|
'changelog',
|
||||||
|
'common',
|
||||||
|
'compiler',
|
||||||
|
'compiler-cli',
|
||||||
|
'core',
|
||||||
|
'dev-infra',
|
||||||
|
'docs-infra',
|
||||||
|
'elements',
|
||||||
|
'forms',
|
||||||
|
'http',
|
||||||
|
'language-service',
|
||||||
|
'localize',
|
||||||
|
'migrations',
|
||||||
|
'ngcc',
|
||||||
|
'packaging',
|
||||||
|
'platform-browser',
|
||||||
|
'platform-browser-dynamic',
|
||||||
|
'platform-server',
|
||||||
|
'platform-webworker',
|
||||||
|
'platform-webworker-dynamic',
|
||||||
|
'router',
|
||||||
|
'service-worker',
|
||||||
|
'upgrade',
|
||||||
|
've',
|
||||||
|
'zone.js',
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
// The configuration for `ng-dev format` commands.
|
||||||
|
const format = {
|
||||||
|
'clang-format': {
|
||||||
|
'matchers': [
|
||||||
|
'**/*.{js,ts}',
|
||||||
|
// TODO: burn down format failures and remove aio and integration exceptions.
|
||||||
|
'!aio/**',
|
||||||
|
'!integration/**',
|
||||||
|
// TODO: remove this exclusion as part of IE deprecation.
|
||||||
|
'!shims_for_IE.js',
|
||||||
|
// Both third_party and .yarn are directories containing copied code which should
|
||||||
|
// not be modified.
|
||||||
|
'!third_party/**',
|
||||||
|
'!.yarn/**',
|
||||||
|
// Do not format d.ts files as they are generated
|
||||||
|
'!**/*.d.ts',
|
||||||
|
]
|
||||||
|
},
|
||||||
|
'buildifier': true
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Github metadata information for `ng-dev` commands. */
|
||||||
|
const github = {
|
||||||
|
owner: 'angular',
|
||||||
|
name: 'angular',
|
||||||
|
};
|
||||||
|
|
||||||
|
// Configuration for the `ng-dev pr merge` command. The command can be used
|
||||||
|
// for merging upstream pull requests into branches based on a PR target label.
|
||||||
|
const merge = () => {
|
||||||
|
// TODO: resume dynamically determining patch branch
|
||||||
|
const patch = '10.0.x';
|
||||||
|
const config: MergeConfig = {
|
||||||
|
githubApiMerge: false,
|
||||||
|
claSignedLabel: 'cla: yes',
|
||||||
|
mergeReadyLabel: /^PR action: merge(-assistance)?/,
|
||||||
|
commitMessageFixupLabel: 'commit message fixup',
|
||||||
|
labels: [
|
||||||
|
{
|
||||||
|
pattern: 'PR target: master-only',
|
||||||
|
branches: ['master'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: 'PR target: patch-only',
|
||||||
|
branches: [patch],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
pattern: 'PR target: master & patch',
|
||||||
|
branches: ['master', patch],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
requiredBaseCommits: {
|
||||||
|
// PRs that target either `master` or the patch branch, need to be rebased
|
||||||
|
// on top of the latest commit message validation fix.
|
||||||
|
// These SHAs are the commits that update the required license text in the header.
|
||||||
|
'master': '5aeb9a4124922d8ac08eb73b8f322905a32b0b3a',
|
||||||
|
[patch]: '27b95ba64a5d99757f4042073fd1860e20e3ed24'
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export function to build ng-dev configuration object.
|
||||||
|
module.exports = {
|
||||||
|
commitMessage,
|
||||||
|
format,
|
||||||
|
github,
|
||||||
|
merge,
|
||||||
|
};
|
260
.pullapprove.yml
260
.pullapprove.yml
@ -34,41 +34,8 @@
|
|||||||
####################################################################################
|
####################################################################################
|
||||||
# GitHub usernames
|
# GitHub usernames
|
||||||
####################################################################################
|
####################################################################################
|
||||||
# aikidave - Dave Shevitz
|
# See reviewer list under `required-minimum-review` group. Team member names and
|
||||||
# alan-agius4 - Alan Agius
|
# usernames are managed there.
|
||||||
# alxhub - Alex Rickabaugh
|
|
||||||
# AndrewKushnir - Andrew Kushnir
|
|
||||||
# andrewseguin - Andrew Seguin
|
|
||||||
# atscott - Andrew Scott
|
|
||||||
# ayazhafiz - Ayaz Hafiz
|
|
||||||
# clydin - Charles Lyding
|
|
||||||
# crisbeto - Kristiyan Kostadinov
|
|
||||||
# dennispbrown - Denny Brown
|
|
||||||
# devversion - Paul Gschwendtner
|
|
||||||
# dgp1130 - Doug Parker
|
|
||||||
# filipesilva - Filipe Silva
|
|
||||||
# gkalpak - Georgios Kalpakas
|
|
||||||
# gregmagolan - Greg Magolan
|
|
||||||
# IgorMinar - Igor Minar
|
|
||||||
# jbogarthyde - Judy Bogart
|
|
||||||
# jelbourn - Jeremy Elbourn
|
|
||||||
# JiaLiPassion - Jia Li
|
|
||||||
# JoostK - Joost Koehoorn
|
|
||||||
# josephperrott - Joey Perrott
|
|
||||||
# juleskremer - Jules Kremer
|
|
||||||
# kapunahelewong - Kapunahele Wong
|
|
||||||
# kara - Kara Erickson
|
|
||||||
# kyliau - Keen Yee Liau
|
|
||||||
# manughub - Manu Murthy
|
|
||||||
# matsko - Matias Niemela
|
|
||||||
# mgechev - Minko Gechev
|
|
||||||
# mhevery - Miško Hevery
|
|
||||||
# michaelprentice - Michael Prentice
|
|
||||||
# mmalerba - Miles Malerba
|
|
||||||
# petebacondarwin - Pete Bacon Darwin
|
|
||||||
# pkozlowski-opensource - Pawel Kozlowski
|
|
||||||
# robwormald - Rob Wormald
|
|
||||||
# StephenFluin - Stephen Fluin
|
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
@ -80,8 +47,8 @@
|
|||||||
# Used for approving minor changes, large-scale refactorings, and in emergency situations.
|
# Used for approving minor changes, large-scale refactorings, and in emergency situations.
|
||||||
#
|
#
|
||||||
# IgorMinar
|
# IgorMinar
|
||||||
|
# jelbourn
|
||||||
# josephperrott
|
# josephperrott
|
||||||
# kara
|
|
||||||
# mhevery
|
# mhevery
|
||||||
#
|
#
|
||||||
# =========================================================
|
# =========================================================
|
||||||
@ -100,8 +67,16 @@ version: 3
|
|||||||
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
# Meta field that goes unused by PullApprove to allow for defining aliases to be
|
||||||
# used throughout the config.
|
# used throughout the config.
|
||||||
meta:
|
meta:
|
||||||
1: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
can-be-global-approved: &can-be-global-approved "\"global-approvers\" not in groups.approved"
|
||||||
2: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
can-be-global-docs-approved: &can-be-global-docs-approved "\"global-docs-approvers\" not in groups.approved"
|
||||||
|
defaults: &defaults
|
||||||
|
reviews:
|
||||||
|
# Authors provide their approval implicitly, this approval allows for a reviewer
|
||||||
|
# from a group not to need a review specifically for an area of the repository
|
||||||
|
# they own. This is coupled with the `required-minimum-review` group which requires
|
||||||
|
# that all PRs are reviewed by at least one team member who is not the author of
|
||||||
|
# the PR.
|
||||||
|
author_value: 1
|
||||||
|
|
||||||
# turn on 'draft' support
|
# turn on 'draft' support
|
||||||
# https://docs.pullapprove.com/config/github-api-version/
|
# https://docs.pullapprove.com/config/github-api-version/
|
||||||
@ -121,6 +96,55 @@ pullapprove_conditions:
|
|||||||
|
|
||||||
|
|
||||||
groups:
|
groups:
|
||||||
|
# =========================================================
|
||||||
|
# Require review on all PRs
|
||||||
|
#
|
||||||
|
# All PRs require at least one review. This rule will not
|
||||||
|
# request any reviewers, however will require that at least
|
||||||
|
# one review is provided before the group is satisfied.
|
||||||
|
# =========================================================
|
||||||
|
required-minimum-review:
|
||||||
|
reviews:
|
||||||
|
request: 0 # Do not request any reviews from the reviewer group
|
||||||
|
required: 1 # Require that all PRs have approval from at least one of the users in the group
|
||||||
|
author_value: 0 # The author of the PR cannot provide an approval for themself
|
||||||
|
reviewers:
|
||||||
|
users:
|
||||||
|
- aikidave # Dave Shevitz
|
||||||
|
- alan-agius4 # Alan Agius
|
||||||
|
- alxhub # Alex Rickabaugh
|
||||||
|
- AndrewKushnir # Andrew Kushnir
|
||||||
|
- andrewseguin # Andrew Seguin
|
||||||
|
- atscott # Andrew Scott
|
||||||
|
- ayazhafiz # Ayaz Hafiz
|
||||||
|
- clydin # Charles Lyding
|
||||||
|
- crisbeto # Kristiyan Kostadinov
|
||||||
|
- dennispbrown # Denny Brown
|
||||||
|
- devversion # Paul Gschwendtner
|
||||||
|
- dgp1130 # Doug Parker
|
||||||
|
- filipesilva # Filipe Silva
|
||||||
|
- gkalpak # Georgios Kalpakas
|
||||||
|
- gregmagolan # Greg Magolan
|
||||||
|
- IgorMinar # Igor Minar
|
||||||
|
- jbogarthyde # Judy Bogart
|
||||||
|
- jelbourn # Jeremy Elbourn
|
||||||
|
- JiaLiPassion # Jia Li
|
||||||
|
- JoostK # Joost Koehoorn
|
||||||
|
- josephperrott # Joey Perrott
|
||||||
|
- juleskremer # Jules Kremer
|
||||||
|
- kapunahelewong # Kapunahele Wong
|
||||||
|
- kara # Kara Erickson
|
||||||
|
- kyliau # Keen Yee Liau
|
||||||
|
- manughub # Manu Murthy
|
||||||
|
- matsko # Matias Niemela
|
||||||
|
- mgechev # Minko Gechev
|
||||||
|
- mhevery # Miško Hevery
|
||||||
|
- michaelprentice # Michael Prentice
|
||||||
|
- mmalerba # Miles Malerba
|
||||||
|
- petebacondarwin # Pete Bacon Darwin
|
||||||
|
- pkozlowski-opensource # Pawel Kozlowski
|
||||||
|
- StephenFluin # Stephen Fluin
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Global Approvers
|
# Global Approvers
|
||||||
#
|
#
|
||||||
@ -161,6 +185,7 @@ groups:
|
|||||||
# Framework: Animations
|
# Framework: Animations
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-animations:
|
fw-animations:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -185,6 +210,7 @@ groups:
|
|||||||
# Framework: Compiler
|
# Framework: Compiler
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-compiler:
|
fw-compiler:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -203,13 +229,13 @@ groups:
|
|||||||
- alxhub
|
- alxhub
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- JoostK
|
- JoostK
|
||||||
- kara
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Compiler / ngcc
|
# Framework: Compiler / ngcc
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-ngcc:
|
fw-ngcc:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -226,6 +252,7 @@ groups:
|
|||||||
# Framework: Migrations
|
# Framework: Migrations
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-migrations:
|
fw-migrations:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -235,13 +262,13 @@ groups:
|
|||||||
- alxhub
|
- alxhub
|
||||||
- crisbeto
|
- crisbeto
|
||||||
- devversion
|
- devversion
|
||||||
- kara
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Core
|
# Framework: Core
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-core:
|
fw-core:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -351,7 +378,8 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- kara
|
- atscott
|
||||||
|
- ~kara # do not request reviews from Kara, but allow her to approve PRs
|
||||||
- mhevery
|
- mhevery
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
@ -360,13 +388,13 @@ groups:
|
|||||||
# Framework: Http
|
# Framework: Http
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-http:
|
fw-http:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'packages/common/http/**',
|
'packages/common/http/**',
|
||||||
'packages/http/**',
|
|
||||||
'packages/examples/http/**',
|
'packages/examples/http/**',
|
||||||
'aio/content/guide/http.md',
|
'aio/content/guide/http.md',
|
||||||
'aio/content/examples/http/**',
|
'aio/content/examples/http/**',
|
||||||
@ -382,6 +410,7 @@ groups:
|
|||||||
# Framework: Elements
|
# Framework: Elements
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-elements:
|
fw-elements:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -402,6 +431,7 @@ groups:
|
|||||||
# Framework: Forms
|
# Framework: Forms
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-forms:
|
fw-forms:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -434,6 +464,7 @@ groups:
|
|||||||
# Framework: i18n
|
# Framework: i18n
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-i18n:
|
fw-i18n:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -467,6 +498,7 @@ groups:
|
|||||||
# Framework: Platform Server
|
# Framework: Platform Server
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-platform-server:
|
fw-platform-server:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -486,6 +518,7 @@ groups:
|
|||||||
# Framework: Router
|
# Framework: Router
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-router:
|
fw-router:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -494,6 +527,8 @@ groups:
|
|||||||
'packages/router/**',
|
'packages/router/**',
|
||||||
'packages/examples/router/**',
|
'packages/examples/router/**',
|
||||||
'aio/content/guide/router.md',
|
'aio/content/guide/router.md',
|
||||||
|
'aio/content/guide/router-tutorial.md',
|
||||||
|
'aio/content/examples/router-tutorial/**',
|
||||||
'aio/content/examples/router/**',
|
'aio/content/examples/router/**',
|
||||||
'aio/content/images/guide/router/**'
|
'aio/content/images/guide/router/**'
|
||||||
])
|
])
|
||||||
@ -506,6 +541,7 @@ groups:
|
|||||||
# Framework: Service Worker
|
# Framework: Service Worker
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-service-worker:
|
fw-service-worker:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -533,6 +569,7 @@ groups:
|
|||||||
# Framework: Upgrade
|
# Framework: Upgrade
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-upgrade:
|
fw-upgrade:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -563,11 +600,12 @@ groups:
|
|||||||
# Framework: Testing
|
# Framework: Testing
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-testing:
|
fw-testing:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files.exclude('packages/compiler-cli/**'), [
|
||||||
'**/testing/**',
|
'**/testing/**',
|
||||||
'aio/content/guide/testing.md',
|
'aio/content/guide/testing.md',
|
||||||
'aio/content/examples/testing/**',
|
'aio/content/examples/testing/**',
|
||||||
@ -577,7 +615,6 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- AndrewKushnir
|
- AndrewKushnir
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- kara
|
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
@ -585,6 +622,7 @@ groups:
|
|||||||
# Framework: Benchmarks
|
# Framework: Benchmarks
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-benchmarks:
|
fw-benchmarks:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -594,7 +632,6 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- kara
|
|
||||||
- pkozlowski-opensource
|
- pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
@ -602,6 +639,7 @@ groups:
|
|||||||
# Framework: Playground
|
# Framework: Playground
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-playground:
|
fw-playground:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -611,13 +649,15 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- kara
|
- jelbourn
|
||||||
|
- pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Framework: Security
|
# Framework: Security
|
||||||
# =========================================================
|
# =========================================================
|
||||||
fw-security:
|
fw-security:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -635,18 +675,25 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- mhevery
|
- mhevery
|
||||||
|
- jelbourn
|
||||||
|
- pkozlowski-opensource
|
||||||
|
reviews:
|
||||||
|
request: -1 # request reviews from everyone
|
||||||
|
required: 2 # require at least 2 approvals
|
||||||
|
reviewed_for: required
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Bazel
|
# Bazel
|
||||||
# =========================================================
|
# =========================================================
|
||||||
bazel:
|
bazel:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'packages/bazel/**',
|
'packages/bazel/**',
|
||||||
'aio/content/guide/bazel.md'
|
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
@ -659,6 +706,7 @@ groups:
|
|||||||
# Language Service
|
# Language Service
|
||||||
# =========================================================
|
# =========================================================
|
||||||
language-service:
|
language-service:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -678,6 +726,7 @@ groups:
|
|||||||
# zone.js
|
# zone.js
|
||||||
# =========================================================
|
# =========================================================
|
||||||
zone-js:
|
zone-js:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -696,6 +745,7 @@ groups:
|
|||||||
# Benchpress
|
# Benchpress
|
||||||
# =========================================================
|
# =========================================================
|
||||||
benchpress:
|
benchpress:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -706,12 +756,14 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- alxhub
|
- alxhub
|
||||||
|
- josephperrott
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Integration Tests
|
# Integration Tests
|
||||||
# =========================================================
|
# =========================================================
|
||||||
integration-tests:
|
integration-tests:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -722,7 +774,6 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- josephperrott
|
- josephperrott
|
||||||
- kara
|
|
||||||
- mhevery
|
- mhevery
|
||||||
|
|
||||||
|
|
||||||
@ -730,6 +781,7 @@ groups:
|
|||||||
# Docs: Gettings Started & Tutorial
|
# Docs: Gettings Started & Tutorial
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-getting-started-and-tutorial:
|
docs-getting-started-and-tutorial:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -762,6 +814,7 @@ groups:
|
|||||||
# Docs: Marketing
|
# Docs: Marketing
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-marketing:
|
docs-marketing:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -776,14 +829,15 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- aikidave
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- StephenFluin
|
- StephenFluin
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Docs: Observables
|
# Docs: Observables
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-observables:
|
docs-observables:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -809,6 +863,7 @@ groups:
|
|||||||
# Docs: Packaging, Tooling, Releasing
|
# Docs: Packaging, Tooling, Releasing
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-packaging-and-releasing:
|
docs-packaging-and-releasing:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -828,20 +883,47 @@ groups:
|
|||||||
'aio/content/guide/migration-localize.md',
|
'aio/content/guide/migration-localize.md',
|
||||||
'aio/content/guide/migration-module-with-providers.md',
|
'aio/content/guide/migration-module-with-providers.md',
|
||||||
'aio/content/guide/static-query-migration.md',
|
'aio/content/guide/static-query-migration.md',
|
||||||
'aio/content/guide/updating-to-version-9.md',
|
'aio/content/guide/updating-to-version-10.md',
|
||||||
'aio/content/guide/ivy-compatibility.md',
|
'aio/content/guide/ivy-compatibility.md',
|
||||||
'aio/content/guide/ivy-compatibility-examples.md'
|
'aio/content/guide/ivy-compatibility-examples.md'
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- kara
|
- jelbourn
|
||||||
|
|
||||||
|
|
||||||
|
# =========================================================
|
||||||
|
# Tooling: Compiler API shared with Angular CLI
|
||||||
|
#
|
||||||
|
# Changing this API might break Angular CLI, so we require
|
||||||
|
# the CLI team to approve changes here.
|
||||||
|
# =========================================================
|
||||||
|
tooling-cli-shared-api:
|
||||||
|
conditions:
|
||||||
|
- *can-be-global-approved
|
||||||
|
- *can-be-global-docs-approved
|
||||||
|
- >
|
||||||
|
contains_any_globs(files, [
|
||||||
|
'packages/compiler-cli/src/tooling.ts'
|
||||||
|
])
|
||||||
|
reviewers:
|
||||||
|
users:
|
||||||
|
- alan-agius4
|
||||||
|
- clydin
|
||||||
|
- kyliau
|
||||||
|
- IgorMinar
|
||||||
|
reviews:
|
||||||
|
request: -1 # request reviews from everyone
|
||||||
|
required: 2 # require at least 2 approvals
|
||||||
|
reviewed_for: required
|
||||||
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Docs: CLI
|
# Docs: CLI
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-cli:
|
docs-cli:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -858,8 +940,12 @@ groups:
|
|||||||
'aio/content/images/guide/deployment/**',
|
'aio/content/images/guide/deployment/**',
|
||||||
'aio/content/guide/file-structure.md',
|
'aio/content/guide/file-structure.md',
|
||||||
'aio/content/guide/ivy.md',
|
'aio/content/guide/ivy.md',
|
||||||
|
'aio/content/guide/strict-mode.md',
|
||||||
'aio/content/guide/web-worker.md',
|
'aio/content/guide/web-worker.md',
|
||||||
'aio/content/guide/workspace-config.md',
|
'aio/content/guide/workspace-config.md',
|
||||||
|
'aio/content/guide/migration-solution-style-tsconfig.md',
|
||||||
|
'aio/content/guide/migration-update-module-and-target-compiler-options.md',
|
||||||
|
'aio/content/guide/migration-update-libraries-tslib.md',
|
||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
@ -872,6 +958,7 @@ groups:
|
|||||||
# Docs: CLI Libraries
|
# Docs: CLI Libraries
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-libraries:
|
docs-libraries:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -892,6 +979,7 @@ groups:
|
|||||||
# Docs: Schematics
|
# Docs: Schematics
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-schematics:
|
docs-schematics:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -914,6 +1002,7 @@ groups:
|
|||||||
# Docs-infra
|
# Docs-infra
|
||||||
# =========================================================
|
# =========================================================
|
||||||
docs-infra:
|
docs-infra:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- *can-be-global-docs-approved
|
- *can-be-global-docs-approved
|
||||||
@ -943,14 +1032,16 @@ groups:
|
|||||||
# Dev-infra
|
# Dev-infra
|
||||||
# =========================================================
|
# =========================================================
|
||||||
dev-infra:
|
dev-infra:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files.exclude("CHANGELOG.md"), [
|
||||||
'*',
|
'*',
|
||||||
'.circleci/**',
|
'.circleci/**',
|
||||||
'.devcontainer/**',
|
'.devcontainer/**',
|
||||||
'.github/**',
|
'.github/**',
|
||||||
|
'.ng-dev/**',
|
||||||
'.vscode/**',
|
'.vscode/**',
|
||||||
'.yarn/**',
|
'.yarn/**',
|
||||||
'dev-infra/**',
|
'dev-infra/**',
|
||||||
@ -966,8 +1057,6 @@ groups:
|
|||||||
'docs/TOOLS.md',
|
'docs/TOOLS.md',
|
||||||
'docs/TRIAGE_AND_LABELS.md',
|
'docs/TRIAGE_AND_LABELS.md',
|
||||||
'goldens/*',
|
'goldens/*',
|
||||||
'modules/e2e_util/e2e_util.ts',
|
|
||||||
'modules/e2e_util/perf_util.ts',
|
|
||||||
'modules/*',
|
'modules/*',
|
||||||
'packages/*',
|
'packages/*',
|
||||||
'packages/examples/test-utils/**',
|
'packages/examples/test-utils/**',
|
||||||
@ -975,15 +1064,10 @@ groups:
|
|||||||
'packages/examples/*',
|
'packages/examples/*',
|
||||||
'scripts/**',
|
'scripts/**',
|
||||||
'third_party/**',
|
'third_party/**',
|
||||||
'tools/brotli-cli/**',
|
|
||||||
'tools/browsers/**',
|
|
||||||
'tools/build/**',
|
'tools/build/**',
|
||||||
'tools/circular_dependency_test/**',
|
'tools/circular_dependency_test/**',
|
||||||
'tools/contributing-stats/**',
|
'tools/contributing-stats/**',
|
||||||
'tools/components/**',
|
|
||||||
'tools/gulp-tasks/**',
|
'tools/gulp-tasks/**',
|
||||||
'tools/ng_rollup_bundle/**',
|
|
||||||
'tools/ngcontainer/**',
|
|
||||||
'tools/npm/**',
|
'tools/npm/**',
|
||||||
'tools/npm_integration_test/**',
|
'tools/npm_integration_test/**',
|
||||||
'tools/rxjs/**',
|
'tools/rxjs/**',
|
||||||
@ -1013,11 +1097,13 @@ groups:
|
|||||||
# Public API
|
# Public API
|
||||||
# =========================================================
|
# =========================================================
|
||||||
public-api:
|
public-api:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
contains_any_globs(files, [
|
contains_any_globs(files, [
|
||||||
'goldens/public-api/**',
|
'goldens/public-api/**',
|
||||||
|
'CHANGELOG.md',
|
||||||
'docs/NAMING.md',
|
'docs/NAMING.md',
|
||||||
'aio/content/guide/glossary.md',
|
'aio/content/guide/glossary.md',
|
||||||
'aio/content/guide/styleguide.md',
|
'aio/content/guide/styleguide.md',
|
||||||
@ -1026,13 +1112,21 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- alxhub
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
|
- jelbourn
|
||||||
|
- pkozlowski-opensource
|
||||||
|
reviews:
|
||||||
|
request: -1 # request reviews from everyone
|
||||||
|
required: 3 # require at least 3 approvals
|
||||||
|
reviewed_for: required
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
# ================================================
|
||||||
# Size tracking
|
# Size tracking
|
||||||
# ================================================
|
# ================================================
|
||||||
size-tracking:
|
size-tracking:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -1041,14 +1135,21 @@ groups:
|
|||||||
])
|
])
|
||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
|
- alxhub
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- kara
|
- jelbourn
|
||||||
|
- pkozlowski-opensource
|
||||||
|
reviews:
|
||||||
|
request: -1 # request reviews from everyone
|
||||||
|
required: 2 # require at least 2 approvals
|
||||||
|
reviewed_for: required
|
||||||
|
|
||||||
|
|
||||||
# ================================================
|
# ================================================
|
||||||
# Circular dependencies
|
# Circular dependencies
|
||||||
# ================================================
|
# ================================================
|
||||||
circular-dependencies:
|
circular-dependencies:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -1058,8 +1159,9 @@ groups:
|
|||||||
reviewers:
|
reviewers:
|
||||||
users:
|
users:
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
|
- jelbourn
|
||||||
- josephperrott
|
- josephperrott
|
||||||
- kara
|
- pkozlowski-opensource
|
||||||
|
|
||||||
|
|
||||||
####################################################################################
|
####################################################################################
|
||||||
@ -1070,6 +1172,7 @@ groups:
|
|||||||
# Code Ownership
|
# Code Ownership
|
||||||
# =========================================================
|
# =========================================================
|
||||||
code-ownership:
|
code-ownership:
|
||||||
|
<<: *defaults
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
- >
|
- >
|
||||||
@ -1085,12 +1188,33 @@ groups:
|
|||||||
# Catch all for if no groups match the code change
|
# Catch all for if no groups match the code change
|
||||||
# ====================================================
|
# ====================================================
|
||||||
fallback:
|
fallback:
|
||||||
|
<<: *defaults
|
||||||
|
# A group is considered to be `active` for a PR if at least one of group's
|
||||||
|
# conditions matches the PR.
|
||||||
|
#
|
||||||
|
# The PullApprove CI check should fail if a PR has no `active` groups, as
|
||||||
|
# this indicates the PR is modifying a file that has no owner.
|
||||||
|
#
|
||||||
|
# This is enforced through the pullapprove verification check done
|
||||||
|
# as part of the CircleCI lint job. Failures in this lint job should be
|
||||||
|
# fixed as part of the PR. This can be done by updating the
|
||||||
|
# `.pullapprove.yml` file cover the unmatched path.
|
||||||
|
# The pullapprove verification script is part of the ng-dev tool and can be
|
||||||
|
# run locally with the command: `yarn -s ng-dev pullapprove verify`
|
||||||
|
#
|
||||||
|
# For cases in which the verification check fails to ensure coverage, this
|
||||||
|
# group will be active. The expectation is that this should be remedied
|
||||||
|
# before merging the PR as described above. In an emergency situation
|
||||||
|
# `global-approvers` can still approve PRs that match this `fallback` rule,
|
||||||
|
# but that should be an exception and not an expectation.
|
||||||
conditions:
|
conditions:
|
||||||
- *can-be-global-approved
|
- *can-be-global-approved
|
||||||
# Groups which are found to have matching conditions are `active`
|
# The following groups have no conditions and will be `active` on all PRs
|
||||||
# according to PullApprove. If no groups are matched and considered
|
# - `global-approvers`
|
||||||
# active, we still want to have a review occur.
|
# - `global-docs-approvers`
|
||||||
- len(groups.active) == 0
|
#
|
||||||
reviewers:
|
# Since this means the minimum number of active groups a PR can have is 2, this
|
||||||
users:
|
# `fallback` group should be matched anytime the number of active groups is at or
|
||||||
- IgorMinar
|
# below this minimum. This work as a protection to ensure that pullapprove does
|
||||||
|
# not incidently mark a PR as passing without meeting the review criteria.
|
||||||
|
- len(groups.active) <= 2
|
||||||
|
@ -2,7 +2,6 @@ package(default_visibility = ["//visibility:public"])
|
|||||||
|
|
||||||
exports_files([
|
exports_files([
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
"protractor-perf.conf.js",
|
|
||||||
"karma-js.conf.js",
|
"karma-js.conf.js",
|
||||||
"browser-providers.conf.js",
|
"browser-providers.conf.js",
|
||||||
"scripts/ci/track-payload-size.sh",
|
"scripts/ci/track-payload-size.sh",
|
||||||
|
587
CHANGELOG.md
587
CHANGELOG.md
@ -1,3 +1,366 @@
|
|||||||
|
<a name="10.0.0"></a>
|
||||||
|
# [10.0.0](https://github.com/angular/angular/compare/10.0.0-rc.6...10.0.0) (2020-06-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **forms:** optimize internal method _anyControls in FormGroup ([#32534](https://github.com/angular/angular/issues/32534)) ([6c7467a](https://github.com/angular/angular/commit/6c7467a))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!--
|
||||||
|
<a name="10.0.0"></a>
|
||||||
|
# [10.0.0](https://github.com/angular/angular/compare/10.0.0-next.0...10.0.0) (2020-06-24)
|
||||||
|
|
||||||
|
|
||||||
|
### Release Highlights & Update instructions
|
||||||
|
To learn about the release highlights and our CLI-powered automated update workflow for your projects please check out the [v10 release announcement](https://blog.angular.io/TODO).
|
||||||
|
|
||||||
|
|
||||||
|
#### Dependency updates
|
||||||
|
@angular/compiler-cli now requires:
|
||||||
|
- TypeScript 3.9
|
||||||
|
|
||||||
|
TODO: concat all the 10.0.0-next.* & -rc.* release notes, and group them by area/package just like in https://github.com/angular/angular/blob/master/CHANGELOG.md#900-2020-02-06 but consider listing features before bugfixes because people care about feature in major releases more than about bug fixes.
|
||||||
|
-->
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-rc.6"></a>
|
||||||
|
# [10.0.0-rc.6](https://github.com/angular/angular/compare/10.0.0-rc.5...10.0.0-rc.6) (2020-06-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** unable to resolve destructuring variable declarations ([#37497](https://github.com/angular/angular/issues/37497)) ([df10597](https://github.com/angular/angular/commit/df10597)), closes [#36917](https://github.com/angular/angular/issues/36917)
|
||||||
|
* **core:** should fake a top event task when coalescing events to prevent draining microTaskQueue too early. ([#36841](https://github.com/angular/angular/issues/36841)) ([9b8eb42](https://github.com/angular/angular/commit/9b8eb42)), closes [#36839](https://github.com/angular/angular/issues/36839)
|
||||||
|
* **language-service:** wrong completions in conditional operator ([#37505](https://github.com/angular/angular/issues/37505)) ([32020f9](https://github.com/angular/angular/commit/32020f9))
|
||||||
|
* **ngcc:** correctly get config for packages in nested `node_modules/` ([#37040](https://github.com/angular/angular/issues/37040)) ([9ade1c3](https://github.com/angular/angular/commit/9ade1c3))
|
||||||
|
* **ngcc:** correctly get config for sub-entry-points when primary entry-point is ignored ([#37040](https://github.com/angular/angular/issues/37040)) ([bf57776](https://github.com/angular/angular/commit/bf57776))
|
||||||
|
* **ngcc:** correctly retrieve a package's version from its `package.json` ([#37040](https://github.com/angular/angular/issues/37040)) ([11c0402](https://github.com/angular/angular/commit/11c0402))
|
||||||
|
* **router:** fix navigation ignoring logic to compare to the browser url ([#37408](https://github.com/angular/angular/issues/37408)) ([5db2e79](https://github.com/angular/angular/commit/5db2e79)), closes [#16710](https://github.com/angular/angular/issues/16710) [/github.com/angular/angular/issues/16710#issuecomment-634869739](https://github.com//github.com/angular/angular/issues/16710/issues/issuecomment-634869739) [#13586](https://github.com/angular/angular/issues/13586)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **bazel:** expose explicit mapping from closure to devmode files ([#36262](https://github.com/angular/angular/issues/36262)) ([ba796bb](https://github.com/angular/angular/commit/ba796bb))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-rc.5"></a>
|
||||||
|
# [10.0.0-rc.5](https://github.com/angular/angular/compare/10.0.0-rc.4...10.0.0-rc.5) (2020-06-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **ngcc:** do not scan import expressions in d.ts files ([#37503](https://github.com/angular/angular/issues/37503)) ([8248307](https://github.com/angular/angular/commit/8248307))
|
||||||
|
* **ngcc:** use annotateForClosureCompiler option ([#36652](https://github.com/angular/angular/issues/36652)) ([eca8d11](https://github.com/angular/angular/commit/eca8d11)), closes [#36618](https://github.com/angular/angular/issues/36618)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **language-service:** Remove HTML entities autocompletion ([#37515](https://github.com/angular/angular/issues/37515)) ([67bd88b](https://github.com/angular/angular/commit/67bd88b))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-rc.4"></a>
|
||||||
|
# [10.0.0-rc.4](https://github.com/angular/angular/compare/10.0.0-rc.3...10.0.0-rc.4) (2020-06-10)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** prevent duplicate URL change notifications ([#37459](https://github.com/angular/angular/issues/37459)) ([0864726](https://github.com/angular/angular/commit/0864726))
|
||||||
|
* **compiler-cli:** downlevel angular decorators to static properties ([#37382](https://github.com/angular/angular/issues/37382)) ([323651b](https://github.com/angular/angular/commit/323651b)), closes [#30586](https://github.com/angular/angular/issues/30586) [#30106](https://github.com/angular/angular/issues/30106) [#30586](https://github.com/angular/angular/issues/30586) [#30141](https://github.com/angular/angular/issues/30141)
|
||||||
|
* **language-service:** Improve signature selection by finding exact match ([#37494](https://github.com/angular/angular/issues/37494)) ([e97a2d4](https://github.com/angular/angular/commit/e97a2d4))
|
||||||
|
* **platform-server:** correctly handle absolute relative URLs ([#37341](https://github.com/angular/angular/issues/37341)) ([420d1c3](https://github.com/angular/angular/commit/420d1c3)), closes [#37314](https://github.com/angular/angular/issues/37314)
|
||||||
|
* **router:** Fix relative link generation from empty path components ([#37446](https://github.com/angular/angular/issues/37446)) ([585e3f6](https://github.com/angular/angular/commit/585e3f6)), closes [#26243](https://github.com/angular/angular/issues/26243) [#13011](https://github.com/angular/angular/issues/13011) [#35687](https://github.com/angular/angular/issues/35687)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **language-service:** TS references from template items ([#37437](https://github.com/angular/angular/issues/37437)) ([bf2cb6f](https://github.com/angular/angular/commit/bf2cb6f))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **core:** avoid pulling in jit-specific code in aot bundles ([#37372](https://github.com/angular/angular/issues/37372)) ([#37514](https://github.com/angular/angular/issues/37514)) ([6114cd2](https://github.com/angular/angular/commit/6114cd2)), closes [#29083](https://github.com/angular/angular/issues/29083)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-rc.3"></a>
|
||||||
|
# [10.0.0-rc.3](https://github.com/angular/angular/compare/10.0.0-rc.2...10.0.0-rc.3) (2020-06-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** prevent duplicate URL change notifications ([#37404](https://github.com/angular/angular/issues/37404)) ([fff424a](https://github.com/angular/angular/commit/fff424a))
|
||||||
|
* **compiler-cli:** use ModuleWithProviders type if static eval fails ([#37126](https://github.com/angular/angular/issues/37126)) ([305b5a3](https://github.com/angular/angular/commit/305b5a3))
|
||||||
|
* **core:** infinite loop if injectable using inheritance has a custom decorator ([#37022](https://github.com/angular/angular/issues/37022)) ([bc54936](https://github.com/angular/angular/commit/bc54936)), closes [#35733](https://github.com/angular/angular/issues/35733)
|
||||||
|
* **elements:** fire custom element output events during component initialization ([#36161](https://github.com/angular/angular/issues/36161)) ([e9bff5f](https://github.com/angular/angular/commit/e9bff5f)), closes [/github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts#L167-L170](https://github.com//github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts/issues/L167-L170) [/github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts#L164](https://github.com//github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/create-custom-element.ts/issues/L164) [/github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/component-factory-strategy.ts#L158](https://github.com//github.com/angular/angular/blob/c0143cb2abdd172de1b95fd1d2c4cfc738640e28/packages/elements/src/component-factory-strategy.ts/issues/L158) [#36141](https://github.com/angular/angular/issues/36141)
|
||||||
|
* **language-service:** Recover from error in analyzing Ng Modules ([#37108](https://github.com/angular/angular/issues/37108)) ([2c1f35e](https://github.com/angular/angular/commit/2c1f35e))
|
||||||
|
* **ngcc:** capture dynamic import expressions as well as declarations ([#37075](https://github.com/angular/angular/issues/37075)) ([5c0bdae](https://github.com/angular/angular/commit/5c0bdae))
|
||||||
|
* **ngcc:** do not inline source-maps for non-inline typings source-maps ([#37363](https://github.com/angular/angular/issues/37363)) ([b4e26b5](https://github.com/angular/angular/commit/b4e26b5)), closes [#37324](https://github.com/angular/angular/issues/37324)
|
||||||
|
* **ngcc:** ensure that more dependencies are found by `EsmDependencyHost` ([#37075](https://github.com/angular/angular/issues/37075)) ([c6872c0](https://github.com/angular/angular/commit/c6872c0))
|
||||||
|
* **ngcc:** find decorated constructor params on IIFE wrapped classes ([#37436](https://github.com/angular/angular/issues/37436)) ([2cb3b66](https://github.com/angular/angular/commit/2cb3b66)), closes [#37330](https://github.com/angular/angular/issues/37330)
|
||||||
|
* **service-worker:** Don't stay locked in EXISTING_CLIENTS_ONLY if corrupted data ([#37453](https://github.com/angular/angular/issues/37453)) ([6f93847](https://github.com/angular/angular/commit/6f93847)), closes [#31109](https://github.com/angular/angular/issues/31109) [#31865](https://github.com/angular/angular/issues/31865) [/github.com/angular/angular/blob/3569fdf/packages/service-worker/worker/src/driver.ts#L559-L563](https://github.com//github.com/angular/angular/blob/3569fdf/packages/service-worker/worker/src/driver.ts/issues/L559-L563) [/github.com/angular/angular/blob/3569fdf/packages/service-worker/worker/src/driver.ts#L505-L519](https://github.com//github.com/angular/angular/blob/3569fdf/packages/service-worker/worker/src/driver.ts/issues/L505-L519)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **ngcc:** implement a program-based entry-point finder ([#37075](https://github.com/angular/angular/issues/37075)) ([f3ccd29](https://github.com/angular/angular/commit/f3ccd29))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **ngcc:** allow immediately reporting a stale lock file ([#37250](https://github.com/angular/angular/issues/37250)) ([930d204](https://github.com/angular/angular/commit/930d204))
|
||||||
|
* **ngcc:** cache parsed tsconfig between runs ([#37417](https://github.com/angular/angular/issues/37417)) ([f9daa13](https://github.com/angular/angular/commit/f9daa13)), closes [#36882](https://github.com/angular/angular/issues/36882)
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-rc.2"></a>
|
||||||
|
# [10.0.0-rc.2](https://github.com/angular/angular/compare/10.0.0-rc.0...10.0.0-rc.2) (2020-06-01)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** reenable decorator downleveling for Angular npm packages ([#37317](https://github.com/angular/angular/issues/37317)) ([d16a7f3](https://github.com/angular/angular/commit/d16a7f3)), closes [#37221](https://github.com/angular/angular/issues/37221) [#37221](https://github.com/angular/angular/issues/37221)
|
||||||
|
|
||||||
|
|
||||||
|
Note: the 10.0.0-rc.1 release on npm accidentally glitched-out midway, so we cut 10.0.0-rc.2 instead. oops :-)
|
||||||
|
|
||||||
|
<a name="10.0.0-rc.0"></a>
|
||||||
|
# [10.0.0-rc.0](https://github.com/angular/angular/compare/10.0.0-next.9...10.0.0-rc.0) (2020-05-21)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** disable tsickle pass when producing APF packages ([#37221](https://github.com/angular/angular/issues/37221)) ([a1001f2](https://github.com/angular/angular/commit/a1001f2))
|
||||||
|
* **elements:** capture input properties set before upgrading the element ([#36114](https://github.com/angular/angular/issues/36114)) ([2fc5ae5](https://github.com/angular/angular/commit/2fc5ae5)), closes [#30848](https://github.com/angular/angular/issues/30848) [#31416](https://github.com/angular/angular/issues/31416)
|
||||||
|
* **elements:** correctly handle getting/setting properties before connecting the element ([#36114](https://github.com/angular/angular/issues/36114)) ([327980b](https://github.com/angular/angular/commit/327980b)), closes [/github.com/angular/angular/pull/31416/files#r300326698](https://github.com//github.com/angular/angular/pull/31416/files/issues/r300326698)
|
||||||
|
* **elements:** do not break when the constructor of an Angular Element is not called ([#36114](https://github.com/angular/angular/issues/36114)) ([89b44d1](https://github.com/angular/angular/commit/89b44d1))
|
||||||
|
* **ngcc:** identifier ModuleWithProviders functions in IIFE wrapped classes ([#37206](https://github.com/angular/angular/issues/37206)) ([97e1399](https://github.com/angular/angular/commit/97e1399)), closes [#37189](https://github.com/angular/angular/issues/37189)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** Angular npm packages no longer contain jsdoc comments
|
||||||
|
to support Closure Compiler's advanced optimizations
|
||||||
|
|
||||||
|
The support for Closure Compiler in Angular packages has been
|
||||||
|
experimental and broken for quite some time.
|
||||||
|
|
||||||
|
As of TS3.9, Closure is unusable with the JavaScript emit. Please follow
|
||||||
|
https://github.com/microsoft/TypeScript/issues/38374 for more
|
||||||
|
information and updates.
|
||||||
|
|
||||||
|
If you used Closure Compiler with Angular in the past, you will likely
|
||||||
|
be better off consuming Angular packages built from sources directly
|
||||||
|
rather than consuming the version we publish on npm,
|
||||||
|
which is primarily optimized for Webpack/Rollup + Terser build pipeline.
|
||||||
|
|
||||||
|
As a temporary workaround, you might consider using your current build
|
||||||
|
pipeline with Closure flag `--compilation_level=SIMPLE`. This flag
|
||||||
|
will ensure that your build pipeline produces buildable and
|
||||||
|
runnable artifacts, at the cost of increased payload size due to
|
||||||
|
advanced optimizations being disabled.
|
||||||
|
|
||||||
|
If you were affected by this change, please help us understand your
|
||||||
|
needs by leaving a comment on https://github.com/angular/angular/issues/37234.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="9.1.9"></a>
|
||||||
|
## [9.1.9](https://github.com/angular/angular/compare/9.1.8...9.1.9) (2020-05-20)
|
||||||
|
|
||||||
|
This release contains a re-submit of the following 3 commits with fixes for TS 3.8.
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **elements:** capture input properties set before upgrading the element ([#36114](https://github.com/angular/angular/issues/36114)) ([#37226](https://github.com/angular/angular/issues/37226)) ([a33cb2d](https://github.com/angular/angular/commit/a33cb2d)), closes [#30848](https://github.com/angular/angular/issues/30848) [#31416](https://github.com/angular/angular/issues/31416)
|
||||||
|
* **elements:** correctly handle getting/setting properties before connecting the element ([#36114](https://github.com/angular/angular/issues/36114)) ([#37226](https://github.com/angular/angular/issues/37226)) ([6ac0042](https://github.com/angular/angular/commit/6ac0042)), closes [/github.com/angular/angular/pull/31416/files#r300326698](https://github.com//github.com/angular/angular/pull/31416/files/issues/r300326698)
|
||||||
|
* **elements:** do not break when the constructor of an Angular Element is not called ([#36114](https://github.com/angular/angular/issues/36114)) ([#37226](https://github.com/angular/angular/issues/37226)) ([1465372](https://github.com/angular/angular/commit/1465372))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="9.1.8"></a>
|
||||||
|
## [9.1.8](https://github.com/angular/angular/compare/9.1.6...9.1.8) (2020-05-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** Host classes should not be fed back into `@Input` ([#35889](https://github.com/angular/angular/issues/35889)) ([f872b69](https://github.com/angular/angular/commit/f872b69)), closes [#35383](https://github.com/angular/angular/issues/35383)
|
||||||
|
* **core:** inheritance delegate ctor regex updated to work on minified code ([#36962](https://github.com/angular/angular/issues/36962)) ([e3d3395](https://github.com/angular/angular/commit/e3d3395))
|
||||||
|
* **elements:** capture input properties set before upgrading the element ([#36114](https://github.com/angular/angular/issues/36114)) ([1c8f179](https://github.com/angular/angular/commit/1c8f179)), closes [#30848](https://github.com/angular/angular/issues/30848) [#31416](https://github.com/angular/angular/issues/31416)
|
||||||
|
* **elements:** correctly handle getting/setting properties before connecting the element ([#36114](https://github.com/angular/angular/issues/36114)) ([363f14c](https://github.com/angular/angular/commit/363f14c)), closes [/github.com/angular/angular/pull/31416/files#r300326698](https://github.com//github.com/angular/angular/pull/31416/files/issues/r300326698)
|
||||||
|
* **elements:** do not break when the constructor of an Angular Element is not called ([#36114](https://github.com/angular/angular/issues/36114)) ([87b9f08](https://github.com/angular/angular/commit/87b9f08))
|
||||||
|
* **router:** update type for routerLink to include null and undefined ([#37018](https://github.com/angular/angular/issues/37018)) ([7de7871](https://github.com/angular/angular/commit/7de7871)), closes [#13380](https://github.com/angular/angular/issues/13380) [#36544](https://github.com/angular/angular/issues/36544)
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.9"></a>
|
||||||
|
# [10.0.0-next.9](https://github.com/angular/angular/compare/10.0.0-next.8...10.0.0-next.9) (2020-05-19)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** ensure LogicalFileSystem maintains case in paths ([#37008](https://github.com/angular/angular/issues/37008)) ([3dfc770](https://github.com/angular/angular/commit/3dfc770)), closes [#36992](https://github.com/angular/angular/issues/36992) [#36993](https://github.com/angular/angular/issues/36993) [#37000](https://github.com/angular/angular/issues/37000)
|
||||||
|
* **router:** update type for routerLink to include null and undefined ([#37018](https://github.com/angular/angular/issues/37018)) ([ef9f8df](https://github.com/angular/angular/commit/ef9f8df)), closes [#13380](https://github.com/angular/angular/issues/13380) [#36544](https://github.com/angular/angular/issues/36544)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **core** update to tslib 2.0 and move to direct dependencies ([#37198](https://github.com/angular/angular/pull/37198)), closes [#37188](https://github.com/angular/angular/issues/37188)
|
||||||
|
|
||||||
|
<a name="10.0.0-next.8"></a>
|
||||||
|
# [10.0.0-next.8](https://github.com/angular/angular/compare/10.0.0-next.7...10.0.0-next.8) (2020-05-18)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** compute the correct target output for `$localize` messages ([#36989](https://github.com/angular/angular/issues/36989)) ([4e1b5e4](https://github.com/angular/angular/commit/4e1b5e4))
|
||||||
|
* **core:** Host classes should not be fed back into `@Input` ([#35889](https://github.com/angular/angular/issues/35889)) ([cda2530](https://github.com/angular/angular/commit/cda2530)), closes [#35383](https://github.com/angular/angular/issues/35383)
|
||||||
|
* **core:** inheritance delegate ctor regex updated to work on minified code ([#36962](https://github.com/angular/angular/issues/36962)) ([ea971f7](https://github.com/angular/angular/commit/ea971f7))
|
||||||
|
* **language-service:** use empty statement as parent of type node ([#36989](https://github.com/angular/angular/issues/36989)) ([a32cbed](https://github.com/angular/angular/commit/a32cbed))
|
||||||
|
* **ngcc:** `viaModule` should be `null` for local imports ([#36989](https://github.com/angular/angular/issues/36989)) ([d268d2a](https://github.com/angular/angular/commit/d268d2a))
|
||||||
|
* **ngcc:** ensure reflection hosts can handle TS 3.9 IIFE wrapped classes ([#36989](https://github.com/angular/angular/issues/36989)) ([d7440c4](https://github.com/angular/angular/commit/d7440c4))
|
||||||
|
* **ngcc:** ensure rendering formatters work with IIFE wrapped classes ([#36989](https://github.com/angular/angular/issues/36989)) ([c8ee390](https://github.com/angular/angular/commit/c8ee390))
|
||||||
|
* **ngcc:** support `defineProperty()` re-exports in CommonJS and UMD ([#36989](https://github.com/angular/angular/issues/36989)) ([91092f6](https://github.com/angular/angular/commit/91092f6))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* remove support for TypeScript 3.8 ([#37129](https://github.com/angular/angular/issues/37129)) ([6466fb2](https://github.com/angular/angular/commit/6466fb2))
|
||||||
|
* **platform-server:** use absolute URLs from Location for HTTP ([#37071](https://github.com/angular/angular/issues/37071)) ([9edea0b](https://github.com/angular/angular/commit/9edea0b))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* TypeScript 3.8 is no longer supported, please update to TypeScript 3.9.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.7"></a>
|
||||||
|
# [10.0.0-next.7](https://github.com/angular/angular/compare/10.0.0-next.6...10.0.0-next.7) (2020-05-13)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** correct "development mode" console message ([#36571](https://github.com/angular/angular/issues/36571)) ([8d8e419](https://github.com/angular/angular/commit/8d8e419)), closes [#36570](https://github.com/angular/angular/issues/36570)
|
||||||
|
* add aikidave as reviewer for DOCS: Marketing ([#37014](https://github.com/angular/angular/issues/37014)) ([286fbf4](https://github.com/angular/angular/commit/286fbf4))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler:** add name spans for property reads and method calls ([#36826](https://github.com/angular/angular/issues/36826)) ([eb34aa5](https://github.com/angular/angular/commit/eb34aa5))
|
||||||
|
* **language-service:** [ivy] wrap ngtsc to handle typecheck files ([#36930](https://github.com/angular/angular/issues/36930)) ([1142c37](https://github.com/angular/angular/commit/1142c37))
|
||||||
|
* **core** make generic mandatory for ModuleWithProviders ([#36892](https://github.com/angular/angular/issues/36892)) ([20cc3ab](https://github.com/angular/angular/commit/20cc3ab))
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** make generic mandatory for ModuleWithProviders
|
||||||
|
|
||||||
|
A generic type parameter has always been required for the `ModuleWithProviders` pattern to work with Ivy, but prior to this commit, View Engine allowed the generic type to be omitted (though support was officially deprecated).
|
||||||
|
If you're using `ModuleWithProviders` without a generic type in your application code, a v10 migration will update your code for you.
|
||||||
|
|
||||||
|
However, if you are using View Engine and also depending on a library that omits the generic type, you will now get a build time error similar to:
|
||||||
|
|
||||||
|
```
|
||||||
|
error TS2314: Generic type 'ModuleWithProviders<T>' requires 1 type argument(s).
|
||||||
|
```
|
||||||
|
|
||||||
|
In this case, ngcc won't help you (because it's Ivy-only) and the migration only covers application code.
|
||||||
|
You should contact the library author to fix their library to provide a type parameter when they use this class.
|
||||||
|
|
||||||
|
As a workaround, we suggest setting `skipLibChecks` to false in your tsconfig or updating your app to use Ivy.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="9.1.7"></a>
|
||||||
|
## [9.1.7](https://github.com/angular/angular/compare/9.1.6...9.1.7) (2020-05-13)
|
||||||
|
|
||||||
|
This release contains various API docs improvements.
|
||||||
|
|
||||||
|
|
||||||
|
<a name="9.1.6"></a>
|
||||||
|
## [9.1.6](https://github.com/angular/angular/compare/9.1.5...9.1.6) (2020-05-08)
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli**: Revert "fix(compiler-cli): fix case-sensitivity issues in NgtscCompilerHost (#36968)" (#37003)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.6"></a>
|
||||||
|
# [10.0.0-next.6](https://github.com/angular/angular/compare/10.0.0-next.5...10.0.0-next.6) (2020-05-07)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **bazel:** ng_package rule should update "package.json" of ts_library targets ([#36944](https://github.com/angular/angular/issues/36944)) ([d5293d2](https://github.com/angular/angular/commit/d5293d2))
|
||||||
|
* **compiler:** remove outdated and invalid warning for unresolved DI parameters ([#36985](https://github.com/angular/angular/issues/36985)) ([d0280a0](https://github.com/angular/angular/commit/d0280a0))
|
||||||
|
* **compiler:** switch to 'referencedFiles' for shim generation ([#36211](https://github.com/angular/angular/issues/36211)) ([4213e8d](https://github.com/angular/angular/commit/4213e8d))
|
||||||
|
* **compiler-cli:** `isCaseSensitive()` returns correct value ([#36859](https://github.com/angular/angular/issues/36859)) ([fc4741f](https://github.com/angular/angular/commit/fc4741f))
|
||||||
|
* **compiler-cli:** don't try to tag non-ts files as shims ([#36987](https://github.com/angular/angular/issues/36987)) ([42d1091](https://github.com/angular/angular/commit/42d1091))
|
||||||
|
* **compiler-cli:** ensure `getRootDirs()` handles case-sensitivity ([#36859](https://github.com/angular/angular/issues/36859)) ([3f3e9b7](https://github.com/angular/angular/commit/3f3e9b7))
|
||||||
|
* **compiler-cli:** ensure `MockFileSystem` handles case-sensitivity ([#36859](https://github.com/angular/angular/issues/36859)) ([26eacd4](https://github.com/angular/angular/commit/26eacd4))
|
||||||
|
* **compiler-cli:** ensure LogicalFileSystem handles case-sensitivity ([#36859](https://github.com/angular/angular/issues/36859)) ([53a8459](https://github.com/angular/angular/commit/53a8459))
|
||||||
|
* **compiler-cli:** fix bug tracking indirect NgModule dependencies ([#36211](https://github.com/angular/angular/issues/36211)) ([bab90a7](https://github.com/angular/angular/commit/bab90a7))
|
||||||
|
* **compiler-cli:** fix case-sensitivity issues in NgtscCompilerHost ([#36859](https://github.com/angular/angular/issues/36859)) ([0ec0ff3](https://github.com/angular/angular/commit/0ec0ff3))
|
||||||
|
* **compiler-cli:** normalize mock Windows file paths correctly ([#36859](https://github.com/angular/angular/issues/36859)) ([b682bd1](https://github.com/angular/angular/commit/b682bd1))
|
||||||
|
* **compiler-cli:** use CompilerHost to ensure canonical file paths ([#36859](https://github.com/angular/angular/issues/36859)) ([a10c126](https://github.com/angular/angular/commit/a10c126))
|
||||||
|
* **core:** handle pluralize functions that expect a number ([#36901](https://github.com/angular/angular/issues/36901)) ([2ff4b35](https://github.com/angular/angular/commit/2ff4b35)), closes [#36888](https://github.com/angular/angular/issues/36888)
|
||||||
|
* **core:** properly get root nodes from embedded views with <ng-content> ([#36051](https://github.com/angular/angular/issues/36051)) ([e30e132](https://github.com/angular/angular/commit/e30e132)), closes [#35967](https://github.com/angular/angular/issues/35967)
|
||||||
|
* **forms:** handle numeric values properly in the validator ([#36157](https://github.com/angular/angular/issues/36157)) ([88a235d](https://github.com/angular/angular/commit/88a235d)), closes [#35591](https://github.com/angular/angular/issues/35591)
|
||||||
|
* **forms:** number input fires valueChanges twice ([#36087](https://github.com/angular/angular/issues/36087)) ([97d6d90](https://github.com/angular/angular/commit/97d6d90)), closes [#12540](https://github.com/angular/angular/issues/12540)
|
||||||
|
* **localize:** ensure `getLocation()` works ([#36853](https://github.com/angular/angular/issues/36853)) ([70b25a3](https://github.com/angular/angular/commit/70b25a3))
|
||||||
|
* **ngcc:** support ModuleWithProviders functions that delegate ([#36948](https://github.com/angular/angular/issues/36948)) ([fafa50d](https://github.com/angular/angular/commit/fafa50d)), closes [#36892](https://github.com/angular/angular/issues/36892)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
|
||||||
|
* **bazel:** simplify ng_package by dropping esm5 and fesm5 ([#36944](https://github.com/angular/angular/issues/36944)) ([9dbb30f](https://github.com/angular/angular/commit/9dbb30f))
|
||||||
|
* **compiler-cli:** report error if undecorated class with Angular features is discovered ([#36921](https://github.com/angular/angular/issues/36921)) ([4c92cf4](https://github.com/angular/angular/commit/4c92cf4))
|
||||||
|
* **core:** undecorated-classes-with-decorated-fields migration should handle classes with lifecycle hooks ([#36921](https://github.com/angular/angular/issues/36921)) ([c6ecdc9](https://github.com/angular/angular/commit/c6ecdc9))
|
||||||
|
* **ngcc:** support for new APF where `module` points to esm2015 output ([#36944](https://github.com/angular/angular/issues/36944)) ([c98a4d6](https://github.com/angular/angular/commit/c98a4d6))
|
||||||
|
* **language-service:** [ivy] Parse Angular compiler options ([#36922](https://github.com/angular/angular/issues/36922)) ([dbd0f8e](https://github.com/angular/angular/commit/dbd0f8e))
|
||||||
|
* remove TypeScript 3.6 and 3.7 support ([#36329](https://github.com/angular/angular/issues/36329)) ([fbd281c](https://github.com/angular/angular/commit/fbd281c))
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **compiler-cli:** perform template type-checking incrementally ([#36211](https://github.com/angular/angular/issues/36211)) ([ecffc35](https://github.com/angular/angular/commit/ecffc35))
|
||||||
|
* **compiler-cli:** split Ivy template type-checking into multiple files ([#36211](https://github.com/angular/angular/issues/36211)) ([b861e9c](https://github.com/angular/angular/commit/b861e9c))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* TypeScript versions 3.6 and 3.7 are no longer supported, please update to TypeScript 3.8
|
||||||
|
|
||||||
|
* **forms:** Number inputs no longer listen to the `change` event.
|
||||||
|
|
||||||
|
Tests which trigger `change` events need to be updated to trigger `input` events instead.
|
||||||
|
|
||||||
|
The `change` event was in place to support IE9, as we found that `input` events were not fired with backspace or cut actions. If you need to maintain IE9 support, you will need to add a change event listener to number inputs and call the `onChange` method of `NumberValueAccessor` manually.
|
||||||
|
|
||||||
|
Lastly, old versions of WebDriver would synthetically trigger the `change` event on `WebElement.clear` and `WebElement.sendKeys`. If you are using an old version of WebDriver, you may need to update tests to ensure `input` events are triggered. For example, you could use `element.sendKeys(Keys.chord(Keys.CONTROL, "a"), Keys.BACK_SPACE);` in place of `element.clear()`.
|
||||||
|
* **forms:** The `minLength` and `maxLength` validators now verify that the form control's value has a
|
||||||
|
numeric `length` property, and only validate for length if that's the case.
|
||||||
|
|
||||||
|
Previously, falsey values without the length property (such as `0` or
|
||||||
|
`false` values) were triggering validation errors. If your code relies on
|
||||||
|
the old behavior, you can include other validators such as [min][1] or
|
||||||
|
[requiredTrue][2] to the list of validators for a particular field.
|
||||||
|
|
||||||
|
[1]: https://angular.io/api/forms/Validators#min
|
||||||
|
[2]: https://angular.io/api/forms/Validators#requiredTrue
|
||||||
|
* **bazel:** esm5 and fesm5 format is no longer distributed in
|
||||||
|
Angular's npm packages e.g. @angular/core
|
||||||
|
|
||||||
|
If you are not using Angular CLI to build your application or library,
|
||||||
|
and you need to be able to build es5 artifacts, then you will need to
|
||||||
|
downlevel the distributed Angular code to es5 on your own.
|
||||||
|
|
||||||
|
Angular CLI will automatically downlevel the code to es5 if differential
|
||||||
|
loading is enabled in the Angular project, so no action is required from
|
||||||
|
Angular CLI users.
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.5"></a>
|
<a name="9.1.5"></a>
|
||||||
## [9.1.5](https://github.com/angular/angular/compare/9.1.4...9.1.5) (2020-05-07)
|
## [9.1.5](https://github.com/angular/angular/compare/9.1.4...9.1.5) (2020-05-07)
|
||||||
|
|
||||||
@ -30,6 +393,96 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.5"></a>
|
||||||
|
# [10.0.0-next.5](https://github.com/angular/angular/compare/10.0.0-next.4...10.0.0-next.5) (2020-05-04)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **core:** log error instead of warning for unknown properties and elements ([#36399](https://github.com/angular/angular/issues/36399)) ([9d9d46f](https://github.com/angular/angular/commit/9d9d46f)), closes [#35699](https://github.com/angular/angular/issues/35699)
|
||||||
|
* **core:** Refresh transplanted views at insertion point only ([#35968](https://github.com/angular/angular/issues/35968)) ([1786586](https://github.com/angular/angular/commit/1786586)), closes [#35400](https://github.com/angular/angular/issues/35400) [#21324](https://github.com/angular/angular/issues/21324)
|
||||||
|
* **ngcc:** do not run in parallel mode if there are less than 3 CPU cores ([#36626](https://github.com/angular/angular/issues/36626)) ([4c63241](https://github.com/angular/angular/commit/4c63241))
|
||||||
|
* **ngcc:** give up re-spawning crashed worker process after 3 attempts ([#36626](https://github.com/angular/angular/issues/36626)) ([793cb32](https://github.com/angular/angular/commit/793cb32))
|
||||||
|
* **ngcc:** handle `ENOMEM` errors in worker processes ([#36626](https://github.com/angular/angular/issues/36626)) ([4779c4b](https://github.com/angular/angular/commit/4779c4b))
|
||||||
|
* **ngcc:** provide a unique exit code for timeouts ([#36838](https://github.com/angular/angular/issues/36838)) ([d805526](https://github.com/angular/angular/commit/d805526))
|
||||||
|
* **ngcc:** support recovering when a worker process crashes ([#36626](https://github.com/angular/angular/issues/36626)) ([966598c](https://github.com/angular/angular/commit/966598c)), closes [#36278](https://github.com/angular/angular/issues/36278)
|
||||||
|
* **ngcc:** support TS 3.9 wrapped ES2015 classes ([#36884](https://github.com/angular/angular/issues/36884)) ([db4c59d](https://github.com/angular/angular/commit/db4c59d))
|
||||||
|
* **router:** cancel navigation when at least one resolver completes with no "next" emission ([#24621](https://github.com/angular/angular/issues/24621)) ([d9c4840](https://github.com/angular/angular/commit/d9c4840)), closes [#24195](https://github.com/angular/angular/issues/24195)
|
||||||
|
|
||||||
|
|
||||||
|
### Code Refactoring
|
||||||
|
|
||||||
|
* **common:** remove WrappedValue from AsyncPipe ([#36633](https://github.com/angular/angular/issues/36633)) ([49be32c](https://github.com/angular/angular/commit/49be32c)), closes [#29927](https://github.com/angular/angular/issues/29927)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **localize:** support merging multiple translation files ([#36792](https://github.com/angular/angular/issues/36792)) ([72f534f](https://github.com/angular/angular/commit/72f534f))
|
||||||
|
* **ngcc:** allow async locking timeouts to be configured ([#36838](https://github.com/angular/angular/issues/36838)) ([38f805c](https://github.com/angular/angular/commit/38f805c))
|
||||||
|
* **ngcc:** support marking an in-progress task as unprocessed ([#36626](https://github.com/angular/angular/issues/36626)) ([4665c35](https://github.com/angular/angular/commit/4665c35))
|
||||||
|
* **ngcc:** support reverting a file written by `FileWriter` ([#36626](https://github.com/angular/angular/issues/36626)) ([772ccf0](https://github.com/angular/angular/commit/772ccf0))
|
||||||
|
* **service-worker:** include `CacheQueryOptions` options in ngsw-config ([#34663](https://github.com/angular/angular/issues/34663)) ([dc9f4b9](https://github.com/angular/angular/commit/dc9f4b9)), closes [#28443](https://github.com/angular/angular/issues/28443)
|
||||||
|
* **service-worker:** use `ignoreVary: true` when retrieving responses from cache ([#34663](https://github.com/angular/angular/issues/34663)) ([ee35e22](https://github.com/angular/angular/commit/ee35e22)), closes [#36638](https://github.com/angular/angular/issues/36638)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **ngcc:** only compute basePaths in TargetedEntryPointFinder when needed ([#36881](https://github.com/angular/angular/issues/36881)) ([ec6b9cc](https://github.com/angular/angular/commit/ec6b9cc)), closes [#36874](https://github.com/angular/angular/issues/36874)
|
||||||
|
* **ngcc:** speed up the `getBasePaths()` computation ([#36881](https://github.com/angular/angular/issues/36881)) ([e037840](https://github.com/angular/angular/commit/e037840))
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **core:** Warnings about unknown elements are now logged as errors. This won't break your app, but it may trip up tools that expect nothing to be logged via `console.error`.
|
||||||
|
* **router:** Any resolver which return EMPTY will cancel navigation.
|
||||||
|
If you want to allow the navigation to continue, you will need to update the resolvers to emit
|
||||||
|
some value, (i.e. defaultIfEmpty(...), of(...), etc).
|
||||||
|
* **service-worker:** Previously, [Vary](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Vary)
|
||||||
|
headers would be taken into account when retrieving resources from the
|
||||||
|
cache, completely preventing the retrieval of cached assets (due to
|
||||||
|
ServiceWorker implementation details) and leading to unpredictable
|
||||||
|
behavior due to inconsistent/buggy implementations in different
|
||||||
|
browsers.
|
||||||
|
|
||||||
|
Now, `Vary` headers are ignored when retrieving resources from the
|
||||||
|
ServiceWorker caches, which can result in resources being retrieved even
|
||||||
|
when their headers are different. If your application needs to
|
||||||
|
differentiate its responses based on request headers, please make sure
|
||||||
|
the Angular ServiceWorker is [configured](https://angular.io/guide/service-worker-config)
|
||||||
|
to avoid caching the affected resources.
|
||||||
|
* **common:** This change could result in ExpressionChangedAfterItHasBeenChecked errors that
|
||||||
|
were not detected before. The error could previously have gone undetected
|
||||||
|
because two WrappedValues are considered "equal" in all cases for the purposes
|
||||||
|
of the check, even if their respective unwrapped values are not.
|
||||||
|
|
||||||
|
Additionally, `[val]=(observable | async).someProperty` will no longer
|
||||||
|
trigger change detection if the value of `someProperty` is identical to
|
||||||
|
the value in the previous emit. If you need to force change detection,
|
||||||
|
either update the binding to use an object whose reference changes or
|
||||||
|
subscribe to the observable and call markForCheck as needed.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.4"></a>
|
||||||
|
# [10.0.0-next.4](https://github.com/angular/angular/compare/10.0.0-next.3...10.0.0-next.4) (2020-04-29)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** normalize line endings in ICU expansions ([#36741](https://github.com/angular/angular/issues/36741)) ([70dd27f](https://github.com/angular/angular/commit/70dd27f)), closes [#36725](https://github.com/angular/angular/issues/36725)
|
||||||
|
* **core:** attempt to recover from user errors during creation ([#36381](https://github.com/angular/angular/issues/36381)) ([3d82aa7](https://github.com/angular/angular/commit/3d82aa7)), closes [#31221](https://github.com/angular/angular/issues/31221)
|
||||||
|
* **core:** handle synthetic props in Directive host bindings correctly ([#35568](https://github.com/angular/angular/issues/35568)) ([f27deea](https://github.com/angular/angular/commit/f27deea)), closes [#35501](https://github.com/angular/angular/issues/35501)
|
||||||
|
* **language-service:** disable update the `[@angular](https://github.com/angular)/core` module ([#36783](https://github.com/angular/angular/issues/36783)) ([dd049ca](https://github.com/angular/angular/commit/dd049ca))
|
||||||
|
* **localize:** include legacy ids when describing messages ([#36761](https://github.com/angular/angular/issues/36761)) ([47f9867](https://github.com/angular/angular/commit/47f9867))
|
||||||
|
* **ngcc:** recognize enum declarations emitted in JavaScript ([#36550](https://github.com/angular/angular/issues/36550)) ([89c5890](https://github.com/angular/angular/commit/89c5890)), closes [#35584](https://github.com/angular/angular/issues/35584)
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **router:** allow CanLoad guard to return UrlTree ([#36610](https://github.com/angular/angular/issues/36610)) ([00e6cb1](https://github.com/angular/angular/commit/00e6cb1)), closes [#26521](https://github.com/angular/angular/issues/26521) [#28306](https://github.com/angular/angular/issues/28306)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.4"></a>
|
<a name="9.1.4"></a>
|
||||||
## [9.1.4](https://github.com/angular/angular/compare/9.1.3...9.1.4) (2020-04-29)
|
## [9.1.4](https://github.com/angular/angular/compare/9.1.3...9.1.4) (2020-04-29)
|
||||||
|
|
||||||
@ -44,6 +497,42 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.3"></a>
|
||||||
|
# [10.0.0-next.3](https://github.com/angular/angular/compare/10.0.0-next.2...10.0.0-next.3) (2020-04-22)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** format day-periods that cross midnight ([#36611](https://github.com/angular/angular/issues/36611)) ([c6e5fc4](https://github.com/angular/angular/commit/c6e5fc4)), closes [#36566](https://github.com/angular/angular/issues/36566)
|
||||||
|
* **compiler:** avoid generating i18n attributes in plain form ([#36422](https://github.com/angular/angular/issues/36422)) ([88b0985](https://github.com/angular/angular/commit/88b0985))
|
||||||
|
* **core:** do not use unbound attributes as inputs to structural directives ([#36441](https://github.com/angular/angular/issues/36441)) ([acf6075](https://github.com/angular/angular/commit/acf6075))
|
||||||
|
* **core:** handle empty translations correctly ([#36499](https://github.com/angular/angular/issues/36499)) ([b1f1d3f](https://github.com/angular/angular/commit/b1f1d3f)), closes [#36476](https://github.com/angular/angular/issues/36476)
|
||||||
|
* **core:** missing-injectable migration should not migrate `@NgModule` classes ([#36369](https://github.com/angular/angular/issues/36369)) ([28995db](https://github.com/angular/angular/commit/28995db)), closes [#35700](https://github.com/angular/angular/issues/35700)
|
||||||
|
* **core:** pipes injecting viewProviders when used on a component host node ([#36512](https://github.com/angular/angular/issues/36512)) ([81d23b3](https://github.com/angular/angular/commit/81d23b3)), closes [#36146](https://github.com/angular/angular/issues/36146)
|
||||||
|
* **core:** prevent unknown property check for AOT-compiled components ([#36072](https://github.com/angular/angular/issues/36072)) ([4a9f0be](https://github.com/angular/angular/commit/4a9f0be)), closes [#35945](https://github.com/angular/angular/issues/35945)
|
||||||
|
* **core:** properly identify modules affected by overrides in TestBed ([#36649](https://github.com/angular/angular/issues/36649)) ([942b986](https://github.com/angular/angular/commit/942b986)), closes [#36619](https://github.com/angular/angular/issues/36619)
|
||||||
|
* **language-service:** properly evaluate types in comparable expressions ([#36529](https://github.com/angular/angular/issues/36529)) ([8be0972](https://github.com/angular/angular/commit/8be0972))
|
||||||
|
* **ngcc:** display unlocker process output in sync mode ([#36637](https://github.com/angular/angular/issues/36637)) ([cabf997](https://github.com/angular/angular/commit/cabf997)), closes [/github.com/nodejs/node/issues/3596#issuecomment-250890218](https://github.com//github.com/nodejs/node/issues/3596/issues/issuecomment-250890218)
|
||||||
|
* **ngcc:** do not use cached file-system ([#36687](https://github.com/angular/angular/issues/36687)) ([0c2ed4c](https://github.com/angular/angular/commit/0c2ed4c)), closes [/github.com/angular/angular-cli/issues/16860#issuecomment-614694269](https://github.com//github.com/angular/angular-cli/issues/16860/issues/issuecomment-614694269)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **common:** format day-periods that cross midnight
|
||||||
|
|
||||||
|
When formatting a time with the `b` or `B` format codes, the rendered
|
||||||
|
string was not correctly handling day periods that spanned midnight.
|
||||||
|
Instead the logic was falling back to the default case of `AM`.
|
||||||
|
|
||||||
|
Now the logic has been updated so that it matches times that are within
|
||||||
|
a day period that spans midnight, so it will now render the correct
|
||||||
|
output, such as `at night` in the case of English.
|
||||||
|
|
||||||
|
Applications that are using either `formatDate()` or `DatePipe` and any
|
||||||
|
of the `b` or `B` format codes will be affected by this change.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.3"></a>
|
<a name="9.1.3"></a>
|
||||||
## [9.1.3](https://github.com/angular/angular/compare/9.1.2...9.1.3) (2020-04-22)
|
## [9.1.3](https://github.com/angular/angular/compare/9.1.2...9.1.3) (2020-04-22)
|
||||||
|
|
||||||
@ -63,6 +552,32 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.2"></a>
|
||||||
|
# [10.0.0-next.2](https://github.com/angular/angular/compare/10.0.0-next.1...10.0.0-next.2) (2020-04-15)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** `locales/global/*.js` are not ES5 compliant ([#36342](https://github.com/angular/angular/issues/36342)) ([078b0be](https://github.com/angular/angular/commit/078b0be)), closes [angular/angular-cli#16394](https://github.com/angular/angular-cli/issues/16394)
|
||||||
|
* **compiler:** handle type references to namespaced symbols correctly ([#36106](https://github.com/angular/angular/issues/36106)) ([4aa4e6f](https://github.com/angular/angular/commit/4aa4e6f)), closes [#36006](https://github.com/angular/angular/issues/36006)
|
||||||
|
* **core:** undecorated-classes-with-decorated-fields migration should avoid error if base class has no value declaration ([#36543](https://github.com/angular/angular/issues/36543)) ([ca67748](https://github.com/angular/angular/commit/ca67748)), closes [#36522](https://github.com/angular/angular/issues/36522)
|
||||||
|
* **ngcc:** correctly detect external files from nested `node_modules/` ([#36559](https://github.com/angular/angular/issues/36559)) ([6ab43d7](https://github.com/angular/angular/commit/6ab43d7)), closes [#36526](https://github.com/angular/angular/issues/36526)
|
||||||
|
* **ngcc:** display output from the unlocker process on Windows ([#36569](https://github.com/angular/angular/issues/36569)) ([e041ac6](https://github.com/angular/angular/commit/e041ac6))
|
||||||
|
* **ngcc:** do not spawn unlocker processes on cluster workers ([#36569](https://github.com/angular/angular/issues/36569)) ([66effde](https://github.com/angular/angular/commit/66effde)), closes [#35861](https://github.com/angular/angular/issues/35861)
|
||||||
|
* **ngcc:** do not warn if `paths` mapping does not exist ([#36525](https://github.com/angular/angular/issues/36525)) ([717df13](https://github.com/angular/angular/commit/717df13)), closes [#36518](https://github.com/angular/angular/issues/36518)
|
||||||
|
* **ngcc:** force ngcc to exit on error ([#36622](https://github.com/angular/angular/issues/36622)) ([663b768](https://github.com/angular/angular/commit/663b768)), closes [#36616](https://github.com/angular/angular/issues/36616)
|
||||||
|
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([80e6c07](https://github.com/angular/angular/commit/80e6c07)), closes [#34614](https://github.com/angular/angular/issues/34614)
|
||||||
|
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([0cc53fb](https://github.com/angular/angular/commit/0cc53fb)), closes [#36492](https://github.com/angular/angular/issues/36492)
|
||||||
|
|
||||||
|
|
||||||
|
### Performance Improvements
|
||||||
|
|
||||||
|
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([3bedfda](https://github.com/angular/angular/commit/3bedfda))
|
||||||
|
* **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([a185efb](https://github.com/angular/angular/commit/a185efb)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
|
||||||
|
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([ec0ce60](https://github.com/angular/angular/commit/ec0ce60))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.2"></a>
|
<a name="9.1.2"></a>
|
||||||
## [9.1.2](https://github.com/angular/angular/compare/9.1.1...9.1.2) (2020-04-15)
|
## [9.1.2](https://github.com/angular/angular/compare/9.1.1...9.1.2) (2020-04-15)
|
||||||
|
|
||||||
@ -79,14 +594,77 @@
|
|||||||
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([8e7f903](https://github.com/angular/angular/commit/8e7f903)), closes [#34614](https://github.com/angular/angular/issues/34614)
|
* **router:** pass correct component to canDeactivate checks when using two or more sibling router-outlets ([#36302](https://github.com/angular/angular/issues/36302)) ([8e7f903](https://github.com/angular/angular/commit/8e7f903)), closes [#34614](https://github.com/angular/angular/issues/34614)
|
||||||
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([a67afcc](https://github.com/angular/angular/commit/a67afcc)), closes [#36492](https://github.com/angular/angular/issues/36492)
|
* **upgrade:** update $locationShim to handle Location changes before initialization ([#36498](https://github.com/angular/angular/issues/36498)) ([a67afcc](https://github.com/angular/angular/commit/a67afcc)), closes [#36492](https://github.com/angular/angular/issues/36492)
|
||||||
|
|
||||||
|
|
||||||
### Performance Improvements
|
### Performance Improvements
|
||||||
|
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([e06512b](https://github.com/angular/angular/commit/e06512b)) * **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([918e628](https://github.com/angular/angular/commit/918e628)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
|
||||||
* **ngcc:** only load if it is needed ([#36486](https://github.com/angular/angular/issues/36486)) ([e06512b](https://github.com/angular/angular/commit/e06512b))
|
|
||||||
* **ngcc:** read dependencies from entry-point manifest ([#36486](https://github.com/angular/angular/issues/36486)) ([918e628](https://github.com/angular/angular/commit/918e628)), closes [#issuecomment-608401834](https://github.com/angular/angular/issues/issuecomment-608401834)
|
|
||||||
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([603b094](https://github.com/angular/angular/commit/603b094))
|
* **ngcc:** reduce the size of the entry-point manifest file ([#36486](https://github.com/angular/angular/issues/36486)) ([603b094](https://github.com/angular/angular/commit/603b094))
|
||||||
|
|
||||||
|
|
||||||
|
<a name="10.0.0-next.1"></a>
|
||||||
|
# [10.0.0-next.1](https://github.com/angular/angular/compare/10.0.0-next.0...10.0.0-next.1) (2020-04-08)
|
||||||
|
|
||||||
|
This release contains various API docs improvements.
|
||||||
|
|
||||||
|
<a name="10.0.0-next.0"></a>
|
||||||
|
# [10.0.0-next.0](https://github.com/angular/angular/compare/9.1.0-rc.0...10.0.0-next.0) (2020-04-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** let `KeyValuePipe` accept type unions with `null` ([#36093](https://github.com/angular/angular/issues/36093)) ([d783519](https://github.com/angular/angular/commit/d783519)), closes [#35743](https://github.com/angular/angular/issues/35743)
|
||||||
|
* **compiler:** avoid undefined expressions in holey array ([#36343](https://github.com/angular/angular/issues/36343)) ([5516802](https://github.com/angular/angular/commit/5516802))
|
||||||
|
* **compiler:** record correct end of expression ([#34690](https://github.com/angular/angular/issues/34690)) ([df890d7](https://github.com/angular/angular/commit/df890d7)), closes [#33477](https://github.com/angular/angular/issues/33477)
|
||||||
|
* **compiler:** resolve enum values in binary operations ([#36461](https://github.com/angular/angular/issues/36461)) ([64022f5](https://github.com/angular/angular/commit/64022f5)), closes [#35584](https://github.com/angular/angular/issues/35584)
|
||||||
|
* **compiler-cli:** pass real source spans where they are empty ([#31805](https://github.com/angular/angular/issues/31805)) ([e893c5a](https://github.com/angular/angular/commit/e893c5a))
|
||||||
|
* **core:** avoid migration error when non-existent symbol is imported ([#36367](https://github.com/angular/angular/issues/36367)) ([d43c306](https://github.com/angular/angular/commit/d43c306)), closes [#36346](https://github.com/angular/angular/issues/36346)
|
||||||
|
* **core:** ngOnDestroy on multi providers called with incorrect context ([#35840](https://github.com/angular/angular/issues/35840)) ([95fc3d4](https://github.com/angular/angular/commit/95fc3d4)), closes [#35231](https://github.com/angular/angular/issues/35231)
|
||||||
|
* **core:** run `APP_INITIALIZER`s before accessing `LOCALE_ID` token in Ivy TestBed ([#36237](https://github.com/angular/angular/issues/36237)) ([1649743](https://github.com/angular/angular/commit/1649743)), closes [#36230](https://github.com/angular/angular/issues/36230)
|
||||||
|
* **core:** undecorated-classes-with-decorated-fields migration does not decorate derived classes ([#35339](https://github.com/angular/angular/issues/35339)) ([32eafef](https://github.com/angular/angular/commit/32eafef)), closes [#34376](https://github.com/angular/angular/issues/34376)
|
||||||
|
* **core:** workaround Terser inlining bug ([#36200](https://github.com/angular/angular/issues/36200)) ([0ce8ad3](https://github.com/angular/angular/commit/0ce8ad3))
|
||||||
|
* **elements:** correctly handle setting inputs to `undefined` ([#36140](https://github.com/angular/angular/issues/36140)) ([9ba46d9](https://github.com/angular/angular/commit/9ba46d9))
|
||||||
|
* **elements:** correctly set `SimpleChange#firstChange` for pre-existing inputs ([#36140](https://github.com/angular/angular/issues/36140)) ([b14ac96](https://github.com/angular/angular/commit/b14ac96)), closes [#36130](https://github.com/angular/angular/issues/36130)
|
||||||
|
* **language-service:** infer type of elements of array-like objects ([#36312](https://github.com/angular/angular/issues/36312)) ([fe2b692](https://github.com/angular/angular/commit/fe2b692)), closes [#36191](https://github.com/angular/angular/issues/36191)
|
||||||
|
* **language-service:** use the `HtmlAst` to get the span of HTML tag ([#36371](https://github.com/angular/angular/issues/36371)) ([81195a2](https://github.com/angular/angular/commit/81195a2))
|
||||||
|
* **localize:** allow ICU expansion case to start with any character except `}` ([#36123](https://github.com/angular/angular/issues/36123)) ([fced8ee](https://github.com/angular/angular/commit/fced8ee)), closes [#31586](https://github.com/angular/angular/issues/31586)
|
||||||
|
* **ngcc:** add process title ([#36448](https://github.com/angular/angular/issues/36448)) ([76a8cd5](https://github.com/angular/angular/commit/76a8cd5)), closes [/github.com/angular/angular/issues/36414#issuecomment-609644282](https://github.com//github.com/angular/angular/issues/36414/issues/issuecomment-609644282)
|
||||||
|
* **ngcc:** allow ngcc configuration to match pre-release versions of packages ([#36370](https://github.com/angular/angular/issues/36370)) ([326240e](https://github.com/angular/angular/commit/326240e))
|
||||||
|
* **ngcc:** correctly detect imported TypeScript helpers ([#36284](https://github.com/angular/angular/issues/36284)) ([ca25c95](https://github.com/angular/angular/commit/ca25c95)), closes [#36089](https://github.com/angular/angular/issues/36089)
|
||||||
|
* **ngcc:** correctly identify relative Windows-style import paths ([#36372](https://github.com/angular/angular/issues/36372)) ([aecf9de](https://github.com/angular/angular/commit/aecf9de))
|
||||||
|
* **ngcc:** correctly identify the package path of secondary entry-points ([#36249](https://github.com/angular/angular/issues/36249)) ([995cd15](https://github.com/angular/angular/commit/995cd15)), closes [#35747](https://github.com/angular/angular/issues/35747)
|
||||||
|
* **ngcc:** detect non-emitted, non-imported TypeScript helpers ([#36418](https://github.com/angular/angular/issues/36418)) ([5fa7b8b](https://github.com/angular/angular/commit/5fa7b8b))
|
||||||
|
* **ngcc:** do not spawn more processes than intended in parallel mode ([#36280](https://github.com/angular/angular/issues/36280)) ([5cee709](https://github.com/angular/angular/commit/5cee709)), closes [#35719](https://github.com/angular/angular/issues/35719) [#36278](https://github.com/angular/angular/issues/36278) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts#L429](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/main.ts/issues/L429) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L108](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L108) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L110](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L110) [/github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts#L199](https://github.com//github.com/angular/angular/blob/b8e9a30d3b6/packages/compiler-cli/ngcc/src/execution/cluster/master.ts/issues/L199)
|
||||||
|
* **ngcc:** do not write entry-point manifest outside node_modules ([#36299](https://github.com/angular/angular/issues/36299)) ([c6dd900](https://github.com/angular/angular/commit/c6dd900)), closes [#36296](https://github.com/angular/angular/issues/36296)
|
||||||
|
* **ngcc:** don't crash on cyclic source-map references ([#36452](https://github.com/angular/angular/issues/36452)) ([ee70a18](https://github.com/angular/angular/commit/ee70a18)), closes [#35727](https://github.com/angular/angular/issues/35727) [#35757](https://github.com/angular/angular/issues/35757)
|
||||||
|
* **ngcc:** handle bad path mappings when finding entry-points ([#36331](https://github.com/angular/angular/issues/36331)) ([cc4b813](https://github.com/angular/angular/commit/cc4b813)), closes [#36313](https://github.com/angular/angular/issues/36313) [#36283](https://github.com/angular/angular/issues/36283)
|
||||||
|
* **ngcc:** handle entry-points within container folders ([#36305](https://github.com/angular/angular/issues/36305)) ([38ad1d9](https://github.com/angular/angular/commit/38ad1d9)), closes [#35756](https://github.com/angular/angular/issues/35756) [#36216](https://github.com/angular/angular/issues/36216)
|
||||||
|
* **ngcc:** sniff `main` property for ESM5 format ([#36396](https://github.com/angular/angular/issues/36396)) ([2463548](https://github.com/angular/angular/commit/2463548)), closes [#35788](https://github.com/angular/angular/issues/35788)
|
||||||
|
* **ngcc:** support ignoring deep-imports via package config ([#36423](https://github.com/angular/angular/issues/36423)) ([f9fb833](https://github.com/angular/angular/commit/f9fb833)), closes [#35750](https://github.com/angular/angular/issues/35750)
|
||||||
|
* **ngcc:** support simple `browser` property in entry-points ([#36396](https://github.com/angular/angular/issues/36396)) ([6b3aa60](https://github.com/angular/angular/commit/6b3aa60)), closes [#36062](https://github.com/angular/angular/issues/36062)
|
||||||
|
* **ngcc:** use path-mappings from tsconfig in dependency resolution ([#36180](https://github.com/angular/angular/issues/36180)) ([380de1e](https://github.com/angular/angular/commit/380de1e)), closes [#36119](https://github.com/angular/angular/issues/36119)
|
||||||
|
* **ngcc:** use preserve whitespaces from tsconfig if provided ([#36189](https://github.com/angular/angular/issues/36189)) ([b8e9a30](https://github.com/angular/angular/commit/b8e9a30)), closes [#35871](https://github.com/angular/angular/issues/35871)
|
||||||
|
* **platform-server:** update `xhr2` dependency ([#36366](https://github.com/angular/angular/issues/36366)) ([b59bc0e](https://github.com/angular/angular/commit/b59bc0e)), closes [#36358](https://github.com/angular/angular/issues/36358)
|
||||||
|
* **router:** allow UrlMatcher to return null ([#36402](https://github.com/angular/angular/issues/36402)) ([568e9df](https://github.com/angular/angular/commit/568e9df)), closes [#29824](https://github.com/angular/angular/issues/29824)
|
||||||
|
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([e0415db](https://github.com/angular/angular/commit/e0415db)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
||||||
|
* **service-worker:** by default register the SW after 30s even the app never stabilizes ([#35870](https://github.com/angular/angular/issues/35870)) ([29e8a64](https://github.com/angular/angular/commit/29e8a64)), closes [#34464](https://github.com/angular/angular/issues/34464)
|
||||||
|
* **service-worker:** prevent SW registration strategies from affecting app stabilization ([#35870](https://github.com/angular/angular/issues/35870)) ([2d7c95f](https://github.com/angular/angular/commit/2d7c95f))
|
||||||
|
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **compiler:** add dependency info and ng-content selectors to metadata ([#35695](https://github.com/angular/angular/issues/35695)) ([32ce8b1](https://github.com/angular/angular/commit/32ce8b1))
|
||||||
|
* **compiler:** Propagate value span of ExpressionBinding to ParsedProperty ([#36133](https://github.com/angular/angular/issues/36133)) ([d714b95](https://github.com/angular/angular/commit/d714b95))
|
||||||
|
* **core:** undecorated-classes migration should handle derived abstract classes ([#35339](https://github.com/angular/angular/issues/35339)) ([c24ad56](https://github.com/angular/angular/commit/c24ad56))
|
||||||
|
* **service-worker:** support timeout in `registerWhenStable` SW registration strategy ([#35870](https://github.com/angular/angular/issues/35870)) ([00efacf](https://github.com/angular/angular/commit/00efacf)), closes [#34464](https://github.com/angular/angular/issues/34464)
|
||||||
|
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **router:** UrlMatcher's type now reflects that it could always return
|
||||||
|
null.
|
||||||
|
|
||||||
|
If you implemented your own Router or Recognizer class, please update it to
|
||||||
|
handle matcher returning null.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.1"></a>
|
<a name="9.1.1"></a>
|
||||||
## [9.1.1](https://github.com/angular/angular/compare/9.1.0...9.1.1) (2020-04-07)
|
## [9.1.1](https://github.com/angular/angular/compare/9.1.0...9.1.1) (2020-04-07)
|
||||||
@ -122,7 +700,6 @@
|
|||||||
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([0e7a89a](https://github.com/angular/angular/commit/0e7a89a)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
* **router:** state data missing in routerLink ([#36462](https://github.com/angular/angular/issues/36462)) ([0e7a89a](https://github.com/angular/angular/commit/0e7a89a)), closes [#33173](https://github.com/angular/angular/issues/33173)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="9.1.0"></a>
|
<a name="9.1.0"></a>
|
||||||
# [9.1.0](https://github.com/angular/angular/compare/9.0.0...9.1.0) (2020-03-25)
|
# [9.1.0](https://github.com/angular/angular/compare/9.0.0...9.1.0) (2020-03-25)
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ Do not open issues for general support questions as we want to keep GitHub issue
|
|||||||
Stack Overflow is a much better place to ask questions since:
|
Stack Overflow is a much better place to ask questions since:
|
||||||
|
|
||||||
- there are thousands of people willing to help on Stack Overflow
|
- there are thousands of people willing to help on Stack Overflow
|
||||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
- questions and answers stay available for public viewing so your question/answer might help someone else
|
||||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||||
|
|
||||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||||
@ -42,7 +42,9 @@ Please consider what kind of change it is:
|
|||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
discussed. This will also allow us to better coordinate our efforts, prevent duplication of work,
|
||||||
and help you to craft the change so that it is successfully accepted into the project.
|
and help you to craft the change so that it is successfully accepted into the project. **Note**:
|
||||||
|
Adding a new topic to the documentation, or significantly re-writing a topic, counts as a major
|
||||||
|
feature.
|
||||||
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
* **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr).
|
||||||
|
|
||||||
## <a name="submit"></a> Submission Guidelines
|
## <a name="submit"></a> Submission Guidelines
|
||||||
@ -55,7 +57,7 @@ We want to fix all the issues as soon as possible, but before fixing a bug we ne
|
|||||||
|
|
||||||
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
A minimal reproduction allows us to quickly confirm a bug (or point out a coding problem) as well as confirm that we are fixing the right problem.
|
||||||
|
|
||||||
We will be insisting on a minimal reproduction scenario in order to save maintainers time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
We will be insisting on a minimal reproduction scenario in order to save maintainers' time and ultimately be able to fix more bugs. Interestingly, from our experience, users often find coding problems themselves while preparing a minimal reproduction. We understand that sometimes it might be hard to extract essential bits of code from a larger codebase but we really need to isolate the problem before we can fix it.
|
||||||
|
|
||||||
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
|
Unfortunately, we are not able to investigate / fix bugs without a minimal reproduction, so if we don't hear back from you, we are going to close an issue that doesn't have enough info to be reproduced.
|
||||||
|
|
||||||
@ -68,7 +70,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||||
that relates to your submission. You don't want to duplicate effort.
|
that relates to your submission. You don't want to duplicate effort.
|
||||||
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
1. Be sure that an issue describes the problem you're fixing, or documents the design for the feature you'd like to add.
|
||||||
Discussing the design up front helps to ensure that we're ready to accept your work.
|
Discussing the design upfront helps to ensure that we're ready to accept your work.
|
||||||
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||||
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
|
We cannot accept code without this. Make sure you sign with the primary email address of the Git identity that has been granted access to the Angular repository.
|
||||||
1. Fork the angular/angular repo.
|
1. Fork the angular/angular repo.
|
||||||
@ -83,8 +85,7 @@ Before you submit your Pull Request (PR) consider the following guidelines:
|
|||||||
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||||
and ensure that all tests pass.
|
and ensure that all tests pass.
|
||||||
1. Commit your changes using a descriptive commit message that follows our
|
1. Commit your changes using a descriptive commit message that follows our
|
||||||
[commit message conventions](#commit). Adherence to these conventions
|
[commit message conventions](#commit). Adherence to these conventions is necessary because release notes are automatically generated from these messages.
|
||||||
is necessary because release notes are automatically generated from these messages.
|
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
git commit -a
|
git commit -a
|
||||||
@ -179,13 +180,13 @@ Samples: (even more [samples](https://github.com/angular/angular/commits/master)
|
|||||||
docs(changelog): update changelog to beta.5
|
docs(changelog): update changelog to beta.5
|
||||||
```
|
```
|
||||||
```
|
```
|
||||||
fix(release): need to depend on latest rxjs and zone.js
|
fix(release): need to depend on the latest rxjs and zone.js
|
||||||
|
|
||||||
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
The version in our package.json gets copied to the one we publish, and users need the latest of these.
|
||||||
```
|
```
|
||||||
|
|
||||||
### Revert
|
### Revert
|
||||||
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit. In the body, it should say: `This reverts commit <hash>.`, where the hash is the SHA of the commit being reverted.
|
||||||
|
|
||||||
### Type
|
### Type
|
||||||
Must be one of the following:
|
Must be one of the following:
|
||||||
@ -236,6 +237,7 @@ There are currently a few exceptions to the "use package name" rule:
|
|||||||
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
||||||
repo
|
repo
|
||||||
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
* **dev-infra**: used for dev-infra related changes within the directories /scripts, /tools and /dev-infra
|
||||||
|
* **migrations**: used for changes to the `ng update` migrations.
|
||||||
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
* **ngcc**: used for changes to the [Angular Compatibility Compiler](./packages/compiler-cli/ngcc/README.md)
|
||||||
* **ve**: used for changes specific to ViewEngine (legacy compiler/renderer).
|
* **ve**: used for changes specific to ViewEngine (legacy compiler/renderer).
|
||||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
||||||
@ -279,7 +281,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
|||||||
* https://help.github.com/articles/about-commit-email-addresses/
|
* https://help.github.com/articles/about-commit-email-addresses/
|
||||||
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||||
|
|
||||||
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
|
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA before you commit changes. If not, your PR will fail the CLA check.
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
|
15
WORKSPACE
15
WORKSPACE
@ -8,8 +8,8 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
|
|||||||
# Fetch rules_nodejs so we can install our npm dependencies
|
# Fetch rules_nodejs so we can install our npm dependencies
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
sha256 = "f9e7b9f42ae202cc2d2ce6d698ccb49a9f7f7ea572a78fd451696d03ef2ee116",
|
sha256 = "84abf7ac4234a70924628baa9a73a5a5cbad944c4358cf9abdb4aab29c9a5b77",
|
||||||
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.6.0/rules_nodejs-1.6.0.tar.gz"],
|
urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/1.7.0/rules_nodejs-1.7.0.tar.gz"],
|
||||||
)
|
)
|
||||||
|
|
||||||
# Check the rules_nodejs version and download npm dependencies
|
# Check the rules_nodejs version and download npm dependencies
|
||||||
@ -17,7 +17,7 @@ http_archive(
|
|||||||
# assert on that.
|
# assert on that.
|
||||||
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
load("@build_bazel_rules_nodejs//:index.bzl", "check_rules_nodejs_version", "node_repositories", "yarn_install")
|
||||||
|
|
||||||
check_rules_nodejs_version(minimum_version_string = "1.6.0")
|
check_rules_nodejs_version(minimum_version_string = "1.7.0")
|
||||||
|
|
||||||
# Setup the Node.js toolchain
|
# Setup the Node.js toolchain
|
||||||
node_repositories(
|
node_repositories(
|
||||||
@ -64,7 +64,7 @@ load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories"
|
|||||||
|
|
||||||
web_test_repositories()
|
web_test_repositories()
|
||||||
|
|
||||||
load("//tools/browsers:browser_repositories.bzl", "browser_repositories")
|
load("//dev-infra/browsers:browser_repositories.bzl", "browser_repositories")
|
||||||
|
|
||||||
browser_repositories()
|
browser_repositories()
|
||||||
|
|
||||||
@ -91,17 +91,18 @@ rbe_autoconfig(
|
|||||||
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
# Need to specify a base container digest in order to ensure that we can use the checked-in
|
||||||
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
# platform configurations for the "ubuntu16_04" image. Otherwise the autoconfig rule would
|
||||||
# need to pull the image and run it in order determine the toolchain configuration. See:
|
# need to pull the image and run it in order determine the toolchain configuration. See:
|
||||||
# https://github.com/bazelbuild/bazel-toolchains/blob/1.1.2/configs/ubuntu16_04_clang/versions.bzl
|
# https://github.com/bazelbuild/bazel-toolchains/blob/3.2.0/configs/ubuntu16_04_clang/versions.bzl
|
||||||
base_container_digest = "sha256:1ab40405810effefa0b2f45824d6d608634ccddbf06366760c341ef6fbead011",
|
base_container_digest = "sha256:5e750dd878df9fcf4e185c6f52b9826090f6e532b097f286913a428290622332",
|
||||||
# Note that if you change the `digest`, you might also need to update the
|
# Note that if you change the `digest`, you might also need to update the
|
||||||
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
# `base_container_digest` to make sure marketplace.gcr.io/google/rbe-ubuntu16-04-webtest:<digest>
|
||||||
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
# and marketplace.gcr.io/google/rbe-ubuntu16-04:<base_container_digest> have
|
||||||
# the same Clang and JDK installed. Clang is needed because of the dependency on
|
# the same Clang and JDK installed. Clang is needed because of the dependency on
|
||||||
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
# @com_google_protobuf. Java is needed for the Bazel's test executor Java tool.
|
||||||
digest = "sha256:0b8fa87db4b8e5366717a7164342a029d1348d2feea7ecc4b18c780bc2507059",
|
digest = "sha256:f743114235a43355bf8324e2ba0fa6a597236fe06f7bc99aaa9ac703631c306b",
|
||||||
env = clang_env(),
|
env = clang_env(),
|
||||||
registry = "marketplace.gcr.io",
|
registry = "marketplace.gcr.io",
|
||||||
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
# We can't use the default "ubuntu16_04" RBE image provided by the autoconfig because we need
|
||||||
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
|
# a specific Linux kernel that comes with "libx11" in order to run headless browser tests.
|
||||||
repository = "google/rbe-ubuntu16-04-webtest",
|
repository = "google/rbe-ubuntu16-04-webtest",
|
||||||
|
use_checked_in_confs = "Force",
|
||||||
)
|
)
|
||||||
|
@ -4,9 +4,8 @@
|
|||||||
## Install git, Node.js and yarn
|
## Install git, Node.js and yarn
|
||||||
- `sudo apt-get update`
|
- `sudo apt-get update`
|
||||||
- `sudo apt-get install -y git`
|
- `sudo apt-get install -y git`
|
||||||
- Install [nvm](https://github.com/nvm-sh/nvm#installing-and-updating).
|
- Install the latest stable version of [Node.js](https://nodejs.org/en/download).
|
||||||
- Install Node.js: `nvm install 12`
|
- Install the latest stable version of [yarn](https://classic.yarnpkg.com/en/docs/install).
|
||||||
- Install yarn: `npm install --global yarn`
|
|
||||||
|
|
||||||
|
|
||||||
## Checkout repository
|
## Checkout repository
|
||||||
@ -18,7 +17,11 @@
|
|||||||
- You can overwrite the default environment variables inside the image, by passing new values using
|
- You can overwrite the default environment variables inside the image, by passing new values using
|
||||||
`--build-arg`.
|
`--build-arg`.
|
||||||
|
|
||||||
**Note:** The script has to execute docker commands with `sudo`.
|
**Note 1:** The script has to execute docker commands with `sudo`.
|
||||||
|
|
||||||
|
**Note 2:**
|
||||||
|
The script has to execute `yarn` commands, so make sure `yarn` is on the `PATH` when invoking the
|
||||||
|
script.
|
||||||
|
|
||||||
|
|
||||||
## Example
|
## Example
|
||||||
|
@ -8,7 +8,7 @@ VM host to update the preview server based on changes in the source code.
|
|||||||
|
|
||||||
The script will pull the latest changes from the origin's master branch and examine if there have
|
The script will pull the latest changes from the origin's master branch and examine if there have
|
||||||
been any changes in files inside the preview server source code directory (see below). If there are,
|
been any changes in files inside the preview server source code directory (see below). If there are,
|
||||||
it will create a new image and verify that is works as expected. Finally, it will stop and remove
|
it will create a new image and verify that it works as expected. Finally, it will stop and remove
|
||||||
the old docker container and image, create a new container based on the new image and start it.
|
the old docker container and image, create a new container based on the new image and start it.
|
||||||
|
|
||||||
The script assumes that the preview server source code is in the repository's
|
The script assumes that the preview server source code is in the repository's
|
||||||
@ -25,7 +25,11 @@ used for.
|
|||||||
|
|
||||||
**Note 1:** The script has to execute docker commands with `sudo`.
|
**Note 1:** The script has to execute docker commands with `sudo`.
|
||||||
|
|
||||||
**Note 2:** Make sure the user that executes the script has access to update the repository
|
**Note 2:**
|
||||||
|
The script has to execute `yarn` commands, so make sure `yarn` is on the `PATH` when invoking the
|
||||||
|
script.
|
||||||
|
|
||||||
|
**Note 3:** Make sure the user that executes the script has access to update the repository.
|
||||||
|
|
||||||
|
|
||||||
## Run the script manually
|
## Run the script manually
|
||||||
@ -50,3 +54,9 @@ log its output to `update-preview-server.log` (assuming the user has the necessa
|
|||||||
# Periodically check for changes and update the preview server (if necessary)
|
# Periodically check for changes and update the preview server (if necessary)
|
||||||
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/secrets /path/to/builds /path/to/localcerts /path/to/logs >> /path/to/update-preview-server.log 2>&1
|
*/30 * * * * /path/to/update-preview-server.sh /path/to/repo /path/to/secrets /path/to/builds /path/to/localcerts /path/to/logs >> /path/to/update-preview-server.log 2>&1
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Note:**
|
||||||
|
Keep in mind that cron jobs run in non-interactive, non-login shells. This means that the execution
|
||||||
|
context might be different compared to when running the same commands from an interactive, login
|
||||||
|
shell. For example, `.bashrc` files are normally _not_ sourced automatically in cron jobs. See
|
||||||
|
[here](http://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html) for more info.
|
||||||
|
@ -13,7 +13,8 @@ readonly HOST_LOCALCERTS_DIR=$4
|
|||||||
readonly HOST_LOGS_DIR=$5
|
readonly HOST_LOGS_DIR=$5
|
||||||
|
|
||||||
# Constants
|
# Constants
|
||||||
readonly PROVISIONAL_IMAGE_NAME=aio-builds:provisional
|
readonly PROVISIONAL_TAG=provisional
|
||||||
|
readonly PROVISIONAL_IMAGE_NAME=aio-builds:$PROVISIONAL_TAG
|
||||||
readonly LATEST_IMAGE_NAME=aio-builds:latest
|
readonly LATEST_IMAGE_NAME=aio-builds:latest
|
||||||
readonly CONTAINER_NAME=aio
|
readonly CONTAINER_NAME=aio
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ readonly CONTAINER_NAME=aio
|
|||||||
# Do not update the server unless files inside `aio-builds-setup/` have changed
|
# Do not update the server unless files inside `aio-builds-setup/` have changed
|
||||||
# or the last attempt failed (identified by the provisional image still being around).
|
# or the last attempt failed (identified by the provisional image still being around).
|
||||||
readonly relevantChangedFilesCount=$(git diff --name-only $lastDeployedCommit...HEAD | grep -P "^aio/aio-builds-setup/" | wc -l)
|
readonly relevantChangedFilesCount=$(git diff --name-only $lastDeployedCommit...HEAD | grep -P "^aio/aio-builds-setup/" | wc -l)
|
||||||
readonly lastAttemptFailed=$(sudo docker rmi "$PROVISIONAL_IMAGE_NAME" >> /dev/fd/3 && echo "true" || echo "false")
|
readonly lastAttemptFailed=$(sudo docker image ls | grep "$PROVISIONAL_TAG" >> /dev/fd/3 && echo "true" || echo "false")
|
||||||
if [[ $relevantChangedFilesCount -eq 0 ]] && [[ "$lastAttemptFailed" != "true" ]]; then
|
if [[ $relevantChangedFilesCount -eq 0 ]] && [[ "$lastAttemptFailed" != "true" ]]; then
|
||||||
echo "Skipping update because no relevant files have been touched."
|
echo "Skipping update because no relevant files have been touched."
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -109,9 +109,3 @@ Options that specify files can be given as absolute paths, or as paths relative
|
|||||||
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
The [ng generate](cli/generate) and [ng add](cli/add) commands take as an argument the artifact or library to be generated or added to the current project.
|
||||||
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
In addition to any general options, each artifact or library defines its own options in a *schematic*.
|
||||||
Schematic options are supplied to the command in the same format as immediate command options.
|
Schematic options are supplied to the command in the same format as immediate command options.
|
||||||
|
|
||||||
|
|
||||||
### Building with Bazel
|
|
||||||
|
|
||||||
Optionally, you can configure the Angular CLI to use [Bazel](https://docs.bazel.build) as the build tool. For more information, see [Building with Bazel](guide/bazel).
|
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ import { NgModule } from '@angular/core';
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { HeroFormComponent } from './hero-form/hero-form.component';
|
import { HeroFormComponent } from './hero-form/hero-form.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -200,13 +200,4 @@
|
|||||||
(ngModelChange)="model.name = $event">
|
(ngModelChange)="model.name = $event">
|
||||||
TODO: remove this: {{model.name}}
|
TODO: remove this: {{model.name}}
|
||||||
<!-- #enddocregion ngModel-3-->
|
<!-- #enddocregion ngModel-3-->
|
||||||
<hr>
|
|
||||||
<!-- #docregion ngModelName-2 -->
|
|
||||||
<input type="text" class="form-control" id="name"
|
|
||||||
required
|
|
||||||
[(ngModel)]="model.name" name="name"
|
|
||||||
#spy>
|
|
||||||
<br>TODO: remove this: {{spy.className}}
|
|
||||||
<!-- #enddocregion ngModelName-2 -->
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// #docregion , v1, final
|
// #docregion , v1, final
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from '../hero';
|
import { Hero } from '../hero';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-form',
|
selector: 'app-hero-form',
|
||||||
|
@ -13,13 +13,13 @@ import { searchUrl } from '../package-search/package-search.service';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If request is cachable (e.g., package search) and
|
* If request is cacheable (e.g., package search) and
|
||||||
* response is in cache return the cached response as observable.
|
* response is in cache return the cached response as observable.
|
||||||
* If has 'x-refresh' header that is true,
|
* If has 'x-refresh' header that is true,
|
||||||
* then also re-run the package search, using response from next(),
|
* then also re-run the package search, using response from next(),
|
||||||
* returning an observable that emits the cached response first.
|
* returning an observable that emits the cached response first.
|
||||||
*
|
*
|
||||||
* If not in cache or not cachable,
|
* If not in cache or not cacheable,
|
||||||
* pass request through to next()
|
* pass request through to next()
|
||||||
*/
|
*/
|
||||||
// #docregion v1
|
// #docregion v1
|
||||||
@ -28,8 +28,8 @@ export class CachingInterceptor implements HttpInterceptor {
|
|||||||
constructor(private cache: RequestCache) {}
|
constructor(private cache: RequestCache) {}
|
||||||
|
|
||||||
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
intercept(req: HttpRequest<any>, next: HttpHandler) {
|
||||||
// continue if not cachable.
|
// continue if not cacheable.
|
||||||
if (!isCachable(req)) { return next.handle(req); }
|
if (!isCacheable(req)) { return next.handle(req); }
|
||||||
|
|
||||||
const cachedResponse = this.cache.get(req);
|
const cachedResponse = this.cache.get(req);
|
||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
@ -51,11 +51,11 @@ export class CachingInterceptor implements HttpInterceptor {
|
|||||||
// #enddocregion v1
|
// #enddocregion v1
|
||||||
|
|
||||||
|
|
||||||
/** Is this request cachable? */
|
/** Is this request cacheable? */
|
||||||
function isCachable(req: HttpRequest<any>) {
|
function isCacheable(req: HttpRequest<any>) {
|
||||||
// Only GET requests are cachable
|
// Only GET requests are cacheable
|
||||||
return req.method === 'GET' &&
|
return req.method === 'GET' &&
|
||||||
// Only npm package search is cachable in this app
|
// Only npm package search is cacheable in this app
|
||||||
-1 < req.url.indexOf(searchUrl);
|
-1 < req.url.indexOf(searchUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
10
aio/content/examples/i18n/stackblitz.json
Normal file
10
aio/content/examples/i18n/stackblitz.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"description": "i18n",
|
||||||
|
"files":[
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js",
|
||||||
|
"!**/*.[0-9].*"
|
||||||
|
],
|
||||||
|
"file": "src/app/app.component.ts",
|
||||||
|
"tags": ["Angular", "i18n", "internationalization"]
|
||||||
|
}
|
@ -3,6 +3,7 @@ import {
|
|||||||
AfterContentInit,
|
AfterContentInit,
|
||||||
AfterViewChecked,
|
AfterViewChecked,
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
|
Directive,
|
||||||
DoCheck,
|
DoCheck,
|
||||||
OnChanges,
|
OnChanges,
|
||||||
OnDestroy,
|
OnDestroy,
|
||||||
@ -15,7 +16,8 @@ import { LoggerService } from './logger.service';
|
|||||||
let nextId = 1;
|
let nextId = 1;
|
||||||
|
|
||||||
// #docregion ngOnInit
|
// #docregion ngOnInit
|
||||||
export class PeekABoo implements OnInit {
|
@Directive()
|
||||||
|
export class PeekABooDirective implements OnInit {
|
||||||
constructor(private logger: LoggerService) { }
|
constructor(private logger: LoggerService) { }
|
||||||
|
|
||||||
// implement OnInit's `ngOnInit` method
|
// implement OnInit's `ngOnInit` method
|
||||||
@ -34,7 +36,7 @@ export class PeekABoo implements OnInit {
|
|||||||
})
|
})
|
||||||
// Don't HAVE to mention the Lifecycle Hook interfaces
|
// Don't HAVE to mention the Lifecycle Hook interfaces
|
||||||
// unless we want typing and tool support.
|
// unless we want typing and tool support.
|
||||||
export class PeekABooComponent extends PeekABoo implements
|
export class PeekABooComponent extends PeekABooDirective implements
|
||||||
OnChanges, OnInit, DoCheck,
|
OnChanges, OnInit, DoCheck,
|
||||||
AfterContentInit, AfterContentChecked,
|
AfterContentInit, AfterContentChecked,
|
||||||
AfterViewInit, AfterViewChecked,
|
AfterViewInit, AfterViewChecked,
|
||||||
|
@ -23,7 +23,7 @@ export class GreetingModule {
|
|||||||
// #enddocregion ctor
|
// #enddocregion ctor
|
||||||
|
|
||||||
// #docregion for-root
|
// #docregion for-root
|
||||||
static forRoot(config: UserServiceConfig): ModuleWithProviders {
|
static forRoot(config: UserServiceConfig): ModuleWithProviders<GreetingModule> {
|
||||||
return {
|
return {
|
||||||
ngModule: GreetingModule,
|
ngModule: GreetingModule,
|
||||||
providers: [
|
providers: [
|
||||||
|
42
aio/content/examples/router-tutorial/e2e/src/app.e2e-spec.ts
Normal file
42
aio/content/examples/router-tutorial/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { browser, element, by } from 'protractor';
|
||||||
|
|
||||||
|
describe('Router basic tutorial e2e tests', () => {
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
browser.get('');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display Angular Router Sample', () => {
|
||||||
|
expect(element(by.css('h1')).getText()).toBe('Angular Router Sample');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display Crisis Center button', () => {
|
||||||
|
expect(element.all(by.css('a')).get(0).getText()).toBe('Crisis Center');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display Heroes button', () => {
|
||||||
|
expect(element.all(by.css('a')).get(1).getText()).toBe('Heroes');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display HEROES', () => {
|
||||||
|
expect(element(by.css('h3')).getText()).toBe('HEROES');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change to display crisis list component', async () => {
|
||||||
|
const crisisButton = element.all(by.css('a')).get(0);
|
||||||
|
await crisisButton.click();
|
||||||
|
expect(element(by.css('h3')).getText()).toBe('CRISIS CENTER');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change to display heroes component', async () => {
|
||||||
|
const heroesButton = element.all(by.css('a')).get(1);
|
||||||
|
await heroesButton.click();
|
||||||
|
expect(element(by.css('h3')).getText()).toBe('HEROES');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use wildcard route', async () => {
|
||||||
|
browser.get('/fake-page');
|
||||||
|
expect(browser.getCurrentUrl()).toContain('fake-page');
|
||||||
|
expect(element(by.css('h2')).getText()).toBe('Page Not Found');
|
||||||
|
});
|
||||||
|
});
|
@ -0,0 +1,34 @@
|
|||||||
|
.button {
|
||||||
|
box-shadow: inset 0px 1px 0px 0px #ffffff;
|
||||||
|
background: linear-gradient(to bottom, #ffffff 5%, #f6f6f6 100%);
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #dcdcdc;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666666;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 6px 24px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0px 1px 0px #ffffff;
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
.activebutton {
|
||||||
|
box-shadow: inset 0px 1px 0px 0px #dcecfb;
|
||||||
|
background: linear-gradient(to bottom, #bddbfa 5%, #80b5ea 100%);
|
||||||
|
background-color: #bddbfa;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #84bbf3;
|
||||||
|
display: inline-block;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #ffffff;
|
||||||
|
font-family: Arial;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 6px 24px;
|
||||||
|
text-decoration: none;
|
||||||
|
text-shadow: 0px 1px 0px #528ecc;
|
||||||
|
outline: 0;
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
<!-- #docplaster -->
|
||||||
|
<!-- #docregion setup -->
|
||||||
|
<h1>Angular Router Sample</h1>
|
||||||
|
<!-- #enddocregion setup-->
|
||||||
|
<!-- #docregion routeractivelink -->
|
||||||
|
<nav>
|
||||||
|
<a class="button" routerLink="/crisis-list" routerLinkActive="activebutton">Crisis Center</a> |
|
||||||
|
<a class="button" routerLink="/heroes-list" routerLinkActive="activebutton">Heroes</a>
|
||||||
|
</nav>
|
||||||
|
<!-- #enddocregion routeractivelink-->
|
||||||
|
<!-- #docregion router-outlet -->
|
||||||
|
<router-outlet></router-outlet>
|
||||||
|
<!-- #enddocregion router-outlet -->
|
||||||
|
|
||||||
|
<div style="display: none;">
|
||||||
|
<!-- This HTML represents the initial state for the tutorial. It is not intended to appear in the app. -->
|
||||||
|
<!-- #docregion setup, components -->
|
||||||
|
<app-crisis-list></app-crisis-list>
|
||||||
|
<app-heroes-list></app-heroes-list>
|
||||||
|
<!-- #enddocregion setup, components -->
|
||||||
|
|
||||||
|
<!-- This HTML snippet is for when the user first adds the routerlink navigation. -->
|
||||||
|
<!-- #docregion nav -->
|
||||||
|
<nav>
|
||||||
|
<a class="button" routerLink="/crisis-list">Crisis Center</a> |
|
||||||
|
<a class="button" routerLink="/heroes-list">Heroes</a>
|
||||||
|
</nav>
|
||||||
|
<!-- #enddocregion nav-->
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-root',
|
||||||
|
templateUrl: './app.component.html',
|
||||||
|
styleUrls: ['./app.component.css']
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
title = 'angular-router-sample';
|
||||||
|
}
|
38
aio/content/examples/router-tutorial/src/app/app.module.ts
Normal file
38
aio/content/examples/router-tutorial/src/app/app.module.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// #docplaster
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
// #docregion router-import
|
||||||
|
import { RouterModule } from '@angular/router';
|
||||||
|
// #enddocregion router-import
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
import { CrisisListComponent } from './crisis-list/crisis-list.component';
|
||||||
|
import { HeroesListComponent } from './heroes-list/heroes-list.component';
|
||||||
|
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent,
|
||||||
|
CrisisListComponent,
|
||||||
|
HeroesListComponent,
|
||||||
|
PageNotFoundComponent
|
||||||
|
],
|
||||||
|
// #docplaster
|
||||||
|
// #docregion import-basic, import-redirect, import-wildcard
|
||||||
|
imports: [
|
||||||
|
BrowserModule,
|
||||||
|
RouterModule.forRoot([
|
||||||
|
{path: 'crisis-list', component: CrisisListComponent},
|
||||||
|
{path: 'heroes-list', component: HeroesListComponent},
|
||||||
|
// #enddocregion import-basic
|
||||||
|
{path: '', redirectTo: '/heroes-list', pathMatch: 'full'},
|
||||||
|
// #enddocregion import-redirect
|
||||||
|
{path: '**', component: PageNotFoundComponent}
|
||||||
|
// #enddocregion import-wildcard
|
||||||
|
// #docregion import-basic, import-redirect, import-wildcard
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
// #enddocregion import-basic, import-redirect, import-wildcard
|
||||||
|
providers: [],
|
||||||
|
bootstrap: [AppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
@ -0,0 +1,2 @@
|
|||||||
|
<h3>CRISIS CENTER</h3>
|
||||||
|
<p>Get your crisis here</p>
|
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-crisis-list',
|
||||||
|
templateUrl: './crisis-list.component.html',
|
||||||
|
styleUrls: ['./crisis-list.component.css']
|
||||||
|
})
|
||||||
|
export class CrisisListComponent {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<h3>HEROES</h3>
|
||||||
|
<p>Get your heroes here</p>
|
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-heroes-list',
|
||||||
|
templateUrl: './heroes-list.component.html',
|
||||||
|
styleUrls: ['./heroes-list.component.css']
|
||||||
|
})
|
||||||
|
export class HeroesListComponent {
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,2 @@
|
|||||||
|
<h2>Page Not Found</h2>
|
||||||
|
<p>We couldn't find that page! Not even with x-ray vision.</p>
|
@ -0,0 +1,10 @@
|
|||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-page-not-found',
|
||||||
|
templateUrl: './page-not-found.component.html',
|
||||||
|
styleUrls: ['./page-not-found.component.css']
|
||||||
|
})
|
||||||
|
export class PageNotFoundComponent {
|
||||||
|
|
||||||
|
}
|
13
aio/content/examples/router-tutorial/src/index.html
Normal file
13
aio/content/examples/router-tutorial/src/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Angular Router Sample</title>
|
||||||
|
<base href="/">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
12
aio/content/examples/router-tutorial/src/main.ts
Normal file
12
aio/content/examples/router-tutorial/src/main.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { enableProdMode } from '@angular/core';
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app/app.module';
|
||||||
|
import { environment } from './environments/environment';
|
||||||
|
|
||||||
|
if (environment.production) {
|
||||||
|
enableProdMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule)
|
||||||
|
.catch(err => console.error(err));
|
9
aio/content/examples/router-tutorial/stackblitz.json
Normal file
9
aio/content/examples/router-tutorial/stackblitz.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"description": "Router",
|
||||||
|
"files":[
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js",
|
||||||
|
"!**/*.[0-9].*"
|
||||||
|
],
|
||||||
|
"tags": ["router-tutorial"]
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
|
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
|
||||||
|
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
@ -12,21 +12,20 @@ export class AuthGuard implements CanActivate {
|
|||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
next: ActivatedRouteSnapshot,
|
next: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): boolean {
|
state: RouterStateSnapshot): true|UrlTree {
|
||||||
let url: string = state.url;
|
let url: string = state.url;
|
||||||
|
|
||||||
return this.checkLogin(url);
|
return this.checkLogin(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLogin(url: string): boolean {
|
checkLogin(url: string): true|UrlTree {
|
||||||
if (this.authService.isLoggedIn) { return true; }
|
if (this.authService.isLoggedIn) { return true; }
|
||||||
|
|
||||||
// Store the attempted URL for redirecting
|
// Store the attempted URL for redirecting
|
||||||
this.authService.redirectUrl = url;
|
this.authService.redirectUrl = url;
|
||||||
|
|
||||||
// Navigate to the login page with extras
|
// Redirect to the login page
|
||||||
this.router.navigate(['/login']);
|
return this.router.parseUrl('/login');
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
@ -4,7 +4,8 @@ import {
|
|||||||
CanActivate, Router,
|
CanActivate, Router,
|
||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
CanActivateChild
|
CanActivateChild,
|
||||||
|
UrlTree
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
@ -16,7 +17,7 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): boolean {
|
state: RouterStateSnapshot): true|UrlTree {
|
||||||
let url: string = state.url;
|
let url: string = state.url;
|
||||||
|
|
||||||
return this.checkLogin(url);
|
return this.checkLogin(url);
|
||||||
@ -24,20 +25,19 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
|
|
||||||
canActivateChild(
|
canActivateChild(
|
||||||
route: ActivatedRouteSnapshot,
|
route: ActivatedRouteSnapshot,
|
||||||
state: RouterStateSnapshot): boolean {
|
state: RouterStateSnapshot): true|UrlTree {
|
||||||
return this.canActivate(route, state);
|
return this.canActivate(route, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// #enddocregion can-activate-child
|
// #enddocregion can-activate-child
|
||||||
checkLogin(url: string): boolean {
|
checkLogin(url: string): true|UrlTree {
|
||||||
if (this.authService.isLoggedIn) { return true; }
|
if (this.authService.isLoggedIn) { return true; }
|
||||||
|
|
||||||
// Store the attempted URL for redirecting
|
// Store the attempted URL for redirecting
|
||||||
this.authService.redirectUrl = url;
|
this.authService.redirectUrl = url;
|
||||||
|
|
||||||
// Navigate to the login page
|
// Redirect to the login page
|
||||||
this.router.navigate(['/login']);
|
return this.router.parseUrl('/login');
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
// #docregion can-activate-child
|
// #docregion can-activate-child
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,8 @@ import {
|
|||||||
ActivatedRouteSnapshot,
|
ActivatedRouteSnapshot,
|
||||||
RouterStateSnapshot,
|
RouterStateSnapshot,
|
||||||
CanActivateChild,
|
CanActivateChild,
|
||||||
NavigationExtras
|
NavigationExtras,
|
||||||
|
UrlTree
|
||||||
} from '@angular/router';
|
} from '@angular/router';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
@ -16,17 +17,17 @@ import { AuthService } from './auth.service';
|
|||||||
export class AuthGuard implements CanActivate, CanActivateChild {
|
export class AuthGuard implements CanActivate, CanActivateChild {
|
||||||
constructor(private authService: AuthService, private router: Router) {}
|
constructor(private authService: AuthService, private router: Router) {}
|
||||||
|
|
||||||
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree {
|
||||||
let url: string = state.url;
|
let url: string = state.url;
|
||||||
|
|
||||||
return this.checkLogin(url);
|
return this.checkLogin(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
|
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): true|UrlTree {
|
||||||
return this.canActivate(route, state);
|
return this.canActivate(route, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkLogin(url: string): boolean {
|
checkLogin(url: string): true|UrlTree {
|
||||||
if (this.authService.isLoggedIn) { return true; }
|
if (this.authService.isLoggedIn) { return true; }
|
||||||
|
|
||||||
// Store the attempted URL for redirecting
|
// Store the attempted URL for redirecting
|
||||||
@ -42,8 +43,7 @@ export class AuthGuard implements CanActivate, CanActivateChild {
|
|||||||
fragment: 'anchor'
|
fragment: 'anchor'
|
||||||
};
|
};
|
||||||
|
|
||||||
// Navigate to the login page with extras
|
// Redirect to the login page with extras
|
||||||
this.router.navigate(['/login'], navigationExtras);
|
return this.router.createUrlTree(['/login'], navigationExtras);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import nodeResolve from 'rollup-plugin-node-resolve'
|
import nodeResolve from 'rollup-plugin-node-resolve'
|
||||||
import commonjs from 'rollup-plugin-commonjs';
|
import commonjs from 'rollup-plugin-commonjs';
|
||||||
import uglify from 'rollup-plugin-uglify'
|
import {terser} from 'rollup-plugin-terser'
|
||||||
|
|
||||||
//paths are relative to the execution path
|
//paths are relative to the execution path
|
||||||
export default {
|
export default {
|
||||||
@ -17,6 +17,6 @@ export default {
|
|||||||
commonjs({
|
commonjs({
|
||||||
include: ['node_modules/rxjs/**']
|
include: ['node_modules/rxjs/**']
|
||||||
}),
|
}),
|
||||||
uglify()
|
terser()
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ to provide semantic meaning where it might otherwise be missing.
|
|||||||
Use [attribute binding](guide/template-syntax#attribute-binding) template syntax to control the values of accessibility-related attributes.
|
Use [attribute binding](guide/template-syntax#attribute-binding) template syntax to control the values of accessibility-related attributes.
|
||||||
|
|
||||||
When binding to ARIA attributes in Angular, you must use the `attr.` prefix, as the ARIA
|
When binding to ARIA attributes in Angular, you must use the `attr.` prefix, as the ARIA
|
||||||
specification depends specifically on HTML attributes rather than properties on DOM elements.
|
specification depends specifically on HTML attributes rather than properties of DOM elements.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Use attr. when binding to an ARIA attribute -->
|
<!-- Use attr. when binding to an ARIA attribute -->
|
||||||
@ -44,7 +44,7 @@ NOTE:
|
|||||||
|
|
||||||
By convention, HTML attributes use lowercase names (`tabindex`), while properties use camelCase names (`tabIndex`).
|
By convention, HTML attributes use lowercase names (`tabindex`), while properties use camelCase names (`tabIndex`).
|
||||||
|
|
||||||
See the [Template Syntax](https://angular.io/guide/template-syntax#html-attribute-vs-dom-property) guide for more background on the difference between attributes and properties.
|
See the [Template Syntax](guide/template-syntax#html-attribute-vs-dom-property) guide for more background on the difference between attributes and properties.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Angular compiler options
|
# Angular compiler options
|
||||||
|
|
||||||
When you use [AOT compilation](guide/aot-compiler), you can control how your application is compiled by specifying *template* compiler options in the `tsconfig.json` [TypeScript configuration file](guide/typescript-configuration).
|
When you use [AOT compilation](guide/aot-compiler), you can control how your application is compiled by specifying *template* compiler options in the [TypeScript configuration file](guide/typescript-configuration).
|
||||||
|
|
||||||
The template options object, `angularCompilerOptions`, is a sibling to the `compilerOptions` object that supplies standard options to the TypeScript compiler.
|
The template options object, `angularCompilerOptions`, is a sibling to the `compilerOptions` object that supplies standard options to the TypeScript compiler.
|
||||||
|
|
||||||
@ -21,11 +21,11 @@ The template options object, `angularCompilerOptions`, is a sibling to the `comp
|
|||||||
{@a tsconfig-extends}
|
{@a tsconfig-extends}
|
||||||
## Configuration inheritance with extends
|
## Configuration inheritance with extends
|
||||||
|
|
||||||
Like the TypeScript compiler, The Angular AOT compiler also supports `extends` in the `angularCompilerOptions` section of the TypeScript configuration file, `tsconfig.json`.
|
Like the TypeScript compiler, The Angular AOT compiler also supports `extends` in the `angularCompilerOptions` section of the TypeScript configuration file.
|
||||||
The `extends` property is at the top level, parallel to `compilerOptions` and `angularCompilerOptions`.
|
The `extends` property is at the top level, parallel to `compilerOptions` and `angularCompilerOptions`.
|
||||||
|
|
||||||
A TypeScript configuration can inherit settings from another file using the `extends` property.
|
A TypeScript configuration can inherit settings from another file using the `extends` property.
|
||||||
The configuration options from the base file are loaded first, then overridden by those in the inheriting `tsconfig` file.
|
The configuration options from the base file are loaded first, then overridden by those in the inheriting configuration file.
|
||||||
|
|
||||||
For example:
|
For example:
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ This guide explains how to specify metadata and apply available compiler options
|
|||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo">Watch compiler author Tobias Bosch explain the Angular compiler</a> at AngularConnect 2016.
|
<a href="https://www.youtube.com/watch?v=anphffaCZrQ">Watch Alex Rickabaugh explain the Angular compiler</a> at AngularConnect 2019.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -43,33 +43,13 @@ Here are some reasons you might want to use AOT.
|
|||||||
|
|
||||||
Angular offers two ways to compile your application:
|
Angular offers two ways to compile your application:
|
||||||
|
|
||||||
* **_Just-in-Time_ (JIT)**, which compiles your app in the browser at runtime.
|
* **_Just-in-Time_ (JIT)**, which compiles your app in the browser at runtime. This was the default until Angular 8.
|
||||||
* **_Ahead-of-Time_ (AOT)**, which compiles your app at build time.
|
* **_Ahead-of-Time_ (AOT)**, which compiles your app and libraries at build time. This is the default since Angular 9.
|
||||||
|
|
||||||
JIT compilation is the default when you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands:
|
When you run the [`ng build`](cli/build) (build only) or [`ng serve`](cli/serve) (build and serve locally) CLI commands, the type of compilation (JIT or AOT) depends on the value of the `aot` property in your build configuration specified in `angular.json`. By default, `aot` is set to `true` for new CLI apps.
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng build
|
|
||||||
ng serve
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
{@a compile}
|
|
||||||
|
|
||||||
For AOT compilation, include the `--aot` option with the `ng build` or `ng serve` command:
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
ng build --aot
|
|
||||||
ng serve --aot
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
The `ng build` command with the `--prod` meta-flag (`ng build --prod`) compiles with AOT by default.
|
|
||||||
|
|
||||||
See the [CLI command reference](cli) and [Building and serving Angular apps](guide/build) for more information.
|
See the [CLI command reference](cli) and [Building and serving Angular apps](guide/build) for more information.
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## How AOT works
|
## How AOT works
|
||||||
|
|
||||||
The Angular AOT compiler extracts **metadata** to interpret the parts of the application that Angular is supposed to manage.
|
The Angular AOT compiler extracts **metadata** to interpret the parts of the application that Angular is supposed to manage.
|
||||||
@ -125,7 +105,7 @@ For help in understanding and resolving these problems, see [AOT Metadata Errors
|
|||||||
|
|
||||||
### Configuring AOT compilation
|
### Configuring AOT compilation
|
||||||
|
|
||||||
You can provide options in the `tsconfig.json` [TypeScript configuration file](guide/typescript-configuration) that control the compilation process. See [Angular compiler options](guide/angular-compiler-options) for a complete list of available options.
|
You can provide options in the [TypeScript configuration file](guide/typescript-configuration) that controls the compilation process. See [Angular compiler options](guide/angular-compiler-options) for a complete list of available options.
|
||||||
|
|
||||||
## Phase 1: Code analysis
|
## Phase 1: Code analysis
|
||||||
|
|
||||||
@ -231,7 +211,7 @@ The compiler later reports the error if it needs that piece of metadata to gener
|
|||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
If you want `ngc` to report syntax errors immediately rather than produce a `.metadata.json` file with errors, set the `strictMetadataEmit` option in the TypeScript configuration file, `tsconfig.json`.
|
If you want `ngc` to report syntax errors immediately rather than produce a `.metadata.json` file with errors, set the `strictMetadataEmit` option in the TypeScript configuration file.
|
||||||
|
|
||||||
```
|
```
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
@ -562,12 +542,13 @@ It does not, however, rewrite the `.d.ts` file, so TypeScript doesn't recognize
|
|||||||
|
|
||||||
|
|
||||||
{@a binding-expression-validation}
|
{@a binding-expression-validation}
|
||||||
|
|
||||||
## Phase 3: Template type checking
|
## Phase 3: Template type checking
|
||||||
|
|
||||||
One of the Angular compiler's most helpful features is the ability to type-check expressions within templates, and catch any errors before they cause crashes at runtime.
|
One of the Angular compiler's most helpful features is the ability to type-check expressions within templates, and catch any errors before they cause crashes at runtime.
|
||||||
In the template type-checking phase, the Angular template compiler uses the TypeScript compiler to validate the binding expressions in templates.
|
In the template type-checking phase, the Angular template compiler uses the TypeScript compiler to validate the binding expressions in templates.
|
||||||
|
|
||||||
Enable this phase explicitly by adding the compiler option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's `tsconfig.json`
|
Enable this phase explicitly by adding the compiler option `"fullTemplateTypeCheck"` in the `"angularCompilerOptions"` of the project's TypeScript configuration file
|
||||||
(see [Angular Compiler Options](guide/angular-compiler-options)).
|
(see [Angular Compiler Options](guide/angular-compiler-options)).
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
@ -579,7 +560,7 @@ As a result, templates that previously compiled under View Engine can fail type
|
|||||||
This stricter type checking is not enabled by default in version 9, but can be enabled by setting the `strictTemplates` configuration option.
|
This stricter type checking is not enabled by default in version 9, but can be enabled by setting the `strictTemplates` configuration option.
|
||||||
We do expect to make strict type checking the default in the future.
|
We do expect to make strict type checking the default in the future.
|
||||||
|
|
||||||
<!-- For more information about type-checking options, and about improvements to template type checking in version 9 and above, see [Template type checking](guide/template-type-checking). -->
|
For more information about type-checking options, and about improvements to template type checking in version 9 and above, see [Template type checking](guide/template-typecheck).
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -638,16 +619,7 @@ For example, to avoid `Object is possibly 'undefined'` error in the template abo
|
|||||||
|
|
||||||
Using `*ngIf` allows the TypeScript compiler to infer that the `person` used in the binding expression will never be `undefined`.
|
Using `*ngIf` allows the TypeScript compiler to infer that the `person` used in the binding expression will never be `undefined`.
|
||||||
|
|
||||||
#### Custom `ngIf` like directives
|
For more information about input type narrowing, see [Input setter coercion](guide/template-typecheck#input-setter-coercion) and [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks).
|
||||||
|
|
||||||
Directives that behave like `*ngIf` can declare that they want the same treatment by including a static member marker that is a signal to the template compiler to treat them like `*ngIf`. This static member for `*ngIf` is:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
public static ngIfUseIfTypeGuard: void;
|
|
||||||
```
|
|
||||||
|
|
||||||
This declares that the input property `ngIf` of the `NgIf` directive should be treated as a guard to the use of its template, implying that the template will only be instantiated if the `ngIf` input property is true.
|
|
||||||
|
|
||||||
|
|
||||||
### Non-null type assertion operator
|
### Non-null type assertion operator
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ For some platforms and applications, you might also want to use the PWA (Progres
|
|||||||
|
|
||||||
## Support for the development cycle
|
## Support for the development cycle
|
||||||
|
|
||||||
The **Development Workflow** section describes the tools and processes you use to compile, test, and and deploy Angular applications.
|
The **Development Workflow** section describes the tools and processes you use to compile, test, and deploy Angular applications.
|
||||||
|
|
||||||
* [CLI Command Reference](cli): The Angular CLI is a command-line tool that you use to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
* [CLI Command Reference](cli): The Angular CLI is a command-line tool that you use to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||||
|
|
||||||
|
@ -11,7 +11,7 @@ The basic building blocks are *NgModules*, which provide a compilation context f
|
|||||||
|
|
||||||
* Components use *services*, which provide specific functionality not directly related to views. Service providers can be *injected* into components as *dependencies*, making your code modular, reusable, and efficient.
|
* Components use *services*, which provide specific functionality not directly related to views. Service providers can be *injected* into components as *dependencies*, making your code modular, reusable, and efficient.
|
||||||
|
|
||||||
Both components and services are simply classes, with *decorators* that mark their type and provide metadata that tells Angular how to use them.
|
Modules, components and services are classes that use *decorators*. These decorators mark their type and provide metadata that tells Angular how to use them.
|
||||||
|
|
||||||
* The metadata for a component class associates it with a *template* that defines a view. A template combines ordinary HTML with Angular *directives* and *binding markup* that allow Angular to modify the HTML before rendering it for display.
|
* The metadata for a component class associates it with a *template* that defines a view. A template combines ordinary HTML with Angular *directives* and *binding markup* that allow Angular to modify the HTML before rendering it for display.
|
||||||
|
|
||||||
|
@ -1,122 +0,0 @@
|
|||||||
# Building with Bazel
|
|
||||||
|
|
||||||
This guide explains how to build and test Angular apps with Bazel.
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
This guide assumes you are already familiar with developing and building Angular applications using the [CLI](cli).
|
|
||||||
|
|
||||||
It describes features which are part of Angular Labs, and are not considered a stable, supported API.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Using Bazel with the Angular CLI
|
|
||||||
|
|
||||||
The `@angular/bazel` package provides a builder that allows Angular CLI to use Bazel as the build tool.
|
|
||||||
|
|
||||||
To opt-in an existing application, run
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ng add @angular/bazel
|
|
||||||
```
|
|
||||||
|
|
||||||
To use Bazel in a new application, first install `@angular/bazel` globally
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install -g @angular/bazel
|
|
||||||
```
|
|
||||||
|
|
||||||
then create the new application with
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ng new --collection=@angular/bazel
|
|
||||||
```
|
|
||||||
|
|
||||||
Now when you use Angular CLI build commands such as `ng build` and `ng serve`,
|
|
||||||
Bazel is used behind the scenes.
|
|
||||||
Outputs from Bazel appear in the `dist/bin` folder.
|
|
||||||
|
|
||||||
> The command-line output includes extra logging from Bazel.
|
|
||||||
> We plan to reduce this in the future.
|
|
||||||
|
|
||||||
### Removing Bazel
|
|
||||||
|
|
||||||
If you need to opt-out from using Bazel, you can restore the backup files:
|
|
||||||
|
|
||||||
- `/angular.json.bak` replaces `/angular.json`
|
|
||||||
|
|
||||||
## Advanced configuration
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
Editing the Bazel configuration may prevent you opting out of Bazel.
|
|
||||||
Custom behaviors driven by Bazel won't be available in other Builders.
|
|
||||||
|
|
||||||
This section assumes you are familiar with [Bazel](https://docs.bazel.build).
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
You can manually adjust the Bazel configuration to:
|
|
||||||
|
|
||||||
* customize the build steps
|
|
||||||
* parallellize the build for scale and incrementality
|
|
||||||
|
|
||||||
Create the initial Bazel configuration files by running the following command:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
ng build --leaveBazelFilesOnDisk
|
|
||||||
```
|
|
||||||
|
|
||||||
Now you'll find new files in the Angular workspace:
|
|
||||||
|
|
||||||
* `/WORKSPACE` tells Bazel how to download external dependencies.
|
|
||||||
* `/BUILD.bazel` and `/src/BUILD.bazel` tell Bazel about your source code.
|
|
||||||
|
|
||||||
You can find a full-featured example with custom Bazel configurations at https://github.com/bazelbuild/rules_nodejs/tree/master/examples/angular.
|
|
||||||
|
|
||||||
Documentation for using Bazel for frontend projects is linked from https://docs.bazel.build/versions/master/bazel-and-javascript.html.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Running Bazel directly
|
|
||||||
|
|
||||||
In some cases you'll want to bypass the Angular CLI builder, and run the Bazel CLI directly.
|
|
||||||
The Bazel tool is managed by the `@bazel/bazelisk` package (similar to how Node.js can be managed by `nvm`).
|
|
||||||
You can install it globally to get the `bazelisk` command in your path, or use `$(npm bin)/bazelisk` in place of bazelisk below.
|
|
||||||
|
|
||||||
The common commands in Bazel are:
|
|
||||||
|
|
||||||
* `bazelisk build [targets]`: Compile the default output artifacts of the given targets.
|
|
||||||
* `bazelisk test [targets]`: For whichever `*_test` targets are found in the patterns, run the tests.
|
|
||||||
* `bazelisk run [target]`: Compile the program represented by target, and then run it.
|
|
||||||
|
|
||||||
To repeat the command any time the inputs change (watch mode), replace `bazelisk` with `ibazel` in these commands.
|
|
||||||
|
|
||||||
The output locations are printed in the output.
|
|
||||||
|
|
||||||
Full documentation for the Bazel CLI is at https://docs.bazel.build/versions/master/command-line-reference.html.
|
|
||||||
|
|
||||||
|
|
||||||
## Querying the build graph
|
|
||||||
|
|
||||||
Because Bazel constructs a graph out of your targets, you can find lots of useful information.
|
|
||||||
|
|
||||||
Using the graphviz optional dependency, you'll have a program `dot`, which you can use with `bazel query`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ bazel query --output=graph ... | dot -Tpng > graph.png
|
|
||||||
```
|
|
||||||
|
|
||||||
See https://docs.bazel.build/versions/master/query-how-to.html for more details on `bazel query`.
|
|
||||||
|
|
||||||
|
|
||||||
## Customizing `BUILD.bazel` files
|
|
||||||
|
|
||||||
"Rules" are like plugins for Bazel. Many rule sets are available. This guide documents the ones maintained by the Angular team at Google.
|
|
||||||
|
|
||||||
Rules are used in `BUILD.bazel` files, which are markers for the packages in your workspace. Each `BUILD.bazel` file declares a separate package to Bazel, though you can have more coarse-grained distributions so that the packages you publish (for example, to `npm`) can be made up of many Bazel packages.
|
|
||||||
|
|
||||||
In the `BUILD.bazel` file, each rule must first be imported, using the `load` statement. Then the rule is called with some attributes, and the result of calling the rule is that you've declared to Bazel how it can derive some outputs given some inputs and dependencies. Then later, when you run a `bazel` command line, Bazel loads all the rules you've declared to determine an absolute ordering of what needs to be run. Note that only the rules needed to produce the requested output will actually be executed.
|
|
||||||
|
|
||||||
A list of common rules for frontend development is documented in the README at https://github.com/bazelbuild/rules_nodejs/.
|
|
@ -34,7 +34,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td>
|
<td>
|
||||||
latest
|
latest and extended support release (ESR)
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
@ -53,16 +53,18 @@ Angular supports most recent browsers. This includes the following specific vers
|
|||||||
IE
|
IE
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
11, 10, 9 ("compatibility view" mode not supported)
|
<div> 11, 10*, 9* ("compatibility view" mode not supported) </div>
|
||||||
|
<div>*deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
IE Mobile
|
IE Mobile*
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
11
|
11
|
||||||
|
<div>*deprecated in v10, see the {@link guide/deprecations#ie-9-10-and-mobile deprecations guide}.</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
@ -262,6 +262,33 @@ Each budget entry is a JSON object with the following properties:
|
|||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
{@a commonjs }
|
||||||
|
## Configuring CommonJS dependencies
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
It is recommended that you avoid depending on CommonJS modules in your Angular applications.
|
||||||
|
Depending on CommonJS modules can prevent bundlers and minifiers from optimizing your application, which results in larger bundle sizes.
|
||||||
|
Instead, it is recommended that you use [ECMAScript modules](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) in your entire application.
|
||||||
|
For more information, see [How CommonJS is making your bundles larger](https://web.dev/commonjs-larger-bundles/).
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
The Angular CLI outputs warnings if it detects that your browser application depends on CommonJS modules.
|
||||||
|
To disable these warnings, you can add the CommonJS module name to `allowedCommonJsDependencies` option in the `build` options located in `angular.json` file.
|
||||||
|
|
||||||
|
<code-example lang="json">
|
||||||
|
"build": {
|
||||||
|
"builder": "@angular-devkit/build-angular:browser",
|
||||||
|
"options": {
|
||||||
|
"allowedCommonJsDependencies": [
|
||||||
|
"lodash"
|
||||||
|
]
|
||||||
|
...
|
||||||
|
}
|
||||||
|
...
|
||||||
|
},
|
||||||
|
</code-example>
|
||||||
|
|
||||||
{@a browser-compat}
|
{@a browser-compat}
|
||||||
|
|
||||||
|
@ -109,7 +109,9 @@ To learn more, see [Schematics Overview](guide/schematics) and [Schematics for
|
|||||||
|
|
||||||
## Publishing your library
|
## Publishing your library
|
||||||
|
|
||||||
Use the Angular CLI and the npm package manager to build and publish your library as an npm package. It is not recommended to publish Ivy libraries to NPM repositories. Before publishing a library to NPM, build it using the `--prod` flag which will use the older compiler and runtime known as View Engine instead of Ivy.
|
Use the Angular CLI and the npm package manager to build and publish your library as an npm package.
|
||||||
|
|
||||||
|
Before publishing a library to NPM, build it using the `--prod` flag which will use the older compiler and runtime known as View Engine instead of Ivy.
|
||||||
|
|
||||||
<code-example language="bash">
|
<code-example language="bash">
|
||||||
ng build my-lib --prod
|
ng build my-lib --prod
|
||||||
@ -119,6 +121,14 @@ npm publish
|
|||||||
|
|
||||||
If you've never published a package in npm before, you must create a user account. Read more in [Publishing npm Packages](https://docs.npmjs.com/getting-started/publishing-npm-packages).
|
If you've never published a package in npm before, you must create a user account. Read more in [Publishing npm Packages](https://docs.npmjs.com/getting-started/publishing-npm-packages).
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
For now, it is not recommended to publish Ivy libraries to NPM because Ivy generated code is not backward compatible with View Engine, so apps using View Engine will not be able to consume them. Furthermore, the internal Ivy instructions are not yet stable, which can potentially break consumers using a different Angular version from the one used to build the library.
|
||||||
|
|
||||||
|
When a published library is used in an Ivy app, the Angular CLI will automatically convert it to Ivy using a tool known as the Angular compatibility compiler (`ngcc`). Thus, by publishing your libraries using the View Engine compiler ensures that they can be transparently consumed by both View Engine and Ivy apps.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{@a lib-assets}
|
{@a lib-assets}
|
||||||
|
|
||||||
## Managing assets in a library
|
## Managing assets in a library
|
||||||
|
@ -55,7 +55,7 @@ This method is for development and testing only, and is not a supported or secur
|
|||||||
|
|
||||||
### Automatic deployment with the CLI
|
### Automatic deployment with the CLI
|
||||||
|
|
||||||
The Angular CLI command `ng deploy` (introduced in version 8.3.0) executes the `deploy` [CLI builder](https://angular.io/guide/cli-builder) associated with your project. A number of third-party builders implement deployment capabilities to different platforms. You can add any of them to your project by running `ng add [package name]`.
|
The Angular CLI command `ng deploy` (introduced in version 8.3.0) executes the `deploy` [CLI builder](guide/cli-builder) associated with your project. A number of third-party builders implement deployment capabilities to different platforms. You can add any of them to your project by running `ng add [package name]`.
|
||||||
|
|
||||||
When you add a package with deployment capability, it'll automatically update your workspace configuration (`angular.json` file) with a `deploy` section for the selected project. You can then use the `ng deploy` command to deploy that project.
|
When you add a package with deployment capability, it'll automatically update your workspace configuration (`angular.json` file) with a `deploy` section for the selected project. You can then use the `ng deploy` command to deploy that project.
|
||||||
|
|
||||||
@ -304,7 +304,7 @@ In addition to build optimizations, Angular also has a runtime production mode.
|
|||||||
|
|
||||||
<code-example format="nocode">
|
<code-example format="nocode">
|
||||||
|
|
||||||
Angular is running in the development mode. Call enableProdMode() to enable the production mode.
|
Angular is running in development mode. Call enableProdMode() to enable production mode.
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
@ -469,7 +469,7 @@ The following configurations determine your requirements.
|
|||||||
|
|
||||||
* TypeScript configuration
|
* TypeScript configuration
|
||||||
|
|
||||||
In the TypeScript configuration file, `tsconfig.json`, the "target" option in the `compilerOptions` section determines the ECMAScript target version that the code is compiled to.
|
In the TypeScript configuration file, the "target" option in the `compilerOptions` section determines the ECMAScript target version that the code is compiled to.
|
||||||
Modern browsers support ES2015 natively, while ES5 is more commonly used to support legacy browsers.
|
Modern browsers support ES2015 natively, while ES5 is more commonly used to support legacy browsers.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
@ -35,24 +35,24 @@ v9 - v12
|
|||||||
|
|
||||||
| Area | API or Feature | May be removed in |
|
| Area | API or Feature | May be removed in |
|
||||||
| ----------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
| ----------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
||||||
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v10 |
|
| `@angular/bazel` | [`Bazel builder and schematics`](#bazelbuilder) | v10 |
|
||||||
|
| `@angular/common` | [`ReflectiveInjector`](#reflectiveinjector) | <!--v8--> v11 |
|
||||||
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
| `@angular/common` | [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation) | <!--v9--> v11 |
|
||||||
| `@angular/core` | [`CollectionChangeRecord`](#core) | <!--v7--> v10 |
|
| `@angular/core` | [`CollectionChangeRecord`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v10 |
|
| `@angular/core` | [`DefaultIterableDiffer`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v10 |
|
| `@angular/core` | [`ReflectiveKey`](#core) | <!--v8--> v11 |
|
||||||
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v10 |
|
| `@angular/core` | [`RenderComponentType`](#core) | <!--v7--> v11 |
|
||||||
| `@angular/core` | [`ViewEncapsulation.Native`](#core) | <!--v6--> v10 |
|
| `@angular/core` | [`ViewEncapsulation.Native`](#core) | <!--v6--> v11 |
|
||||||
| `@angular/core` | [`ModuleWithProviders` without a generic](#moduleWithProviders) | <!--v9--> v10 |
|
| `@angular/core` | [`WrappedValue`](#core) | <!--v10--> v12 |
|
||||||
| `@angular/core` | [Undecorated base classes that use Angular features](#undecorated-base-classes) | <!--v9--> v10 |
|
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v11 |
|
||||||
| `@angular/forms` | [`ngModel` with reactive forms](#ngmodel-reactive) | <!--v6--> v10 |
|
| `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v11 |
|
||||||
| `@angular/router` | [`preserveQueryParams`](#router) | <!--v7--> v10 |
|
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`@angular/upgrade`](#upgrade) | <!--v8--> v10 |
|
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`getAngularLib`](#upgrade-static) | <!--v8--> v10 |
|
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v11 |
|
||||||
| `@angular/upgrade` | [`setAngularLib`](#upgrade-static) | <!--v8--> v10 |
|
| `@angular/platform-webworker` | [All entry points](api/platform-webworker) | <!--v8--> v11 |
|
||||||
| `@angular/platform-webworker` | [All entry points](api/platform-webworker) | <!--v8--> v10 |
|
| template syntax | [`<template`>](#template-tag) | <!--v7--> v11 |
|
||||||
| template syntax | [`<template`>](#template-tag) | <!--v7--> v10 |
|
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v11 |
|
||||||
| polyfills | [reflect-metadata](#reflect-metadata) | <!--v8--> v10 |
|
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v11 |
|
||||||
| npm package format | [`esm5` and `fesm5` entry-points in @angular/* npm packages](guide/deprecations#esm5-fesm5) | <!-- v9 --> v10 |
|
|
||||||
| `@angular/core` | [`defineInjectable`](#core) | <!--v8--> v11 |
|
| `@angular/core` | [`defineInjectable`](#core) | <!--v8--> v11 |
|
||||||
| `@angular/core` | [`entryComponents`](api/core/NgModule#entryComponents) | <!--v9--> v11 |
|
| `@angular/core` | [`entryComponents`](api/core/NgModule#entryComponents) | <!--v9--> v11 |
|
||||||
| `@angular/core` | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | <!--v9--> v11 |
|
| `@angular/core` | [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | <!--v9--> v11 |
|
||||||
@ -60,6 +60,7 @@ v9 - v12
|
|||||||
| `@angular/core/testing` | [`TestBed.get`](#testing) | <!--v9--> v12 |
|
| `@angular/core/testing` | [`TestBed.get`](#testing) | <!--v9--> v12 |
|
||||||
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
| `@angular/router` | [`ActivatedRoute` params and `queryParams` properties](#activatedroute-props) | unspecified |
|
||||||
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
| template syntax | [`/deep/`, `>>>`, and `::ng-deep`](#deep-component-style-selector) | <!--v7--> unspecified |
|
||||||
|
| browser support | [`IE 9 and 10, IE mobile`](#ie-9-10-and-mobile) | <!--v10--> v11 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -96,9 +97,7 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
|
| [`defineInjectable`](api/core/defineInjectable) | `ɵɵdefineInjectable` | v8 | Used only in generated code. No source code should depend on this API. |
|
||||||
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
|
| [`entryComponents`](api/core/NgModule#entryComponents) | none | v9 | See [`entryComponents`](#entryComponents) |
|
||||||
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |
|
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | v9 | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](#entryComponents) |
|
||||||
| `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | v9 | See [`ModuleWithProviders` section](#moduleWithProviders) |
|
| [`WrappedValue`](api/core/WrappedValue) | none | v10 | See [removing `WrappedValue`](#wrapped-value) |
|
||||||
| Undecorated base classes that use Angular features | Base classes with `@Directive()` decorator that use Angular features | v9 | See [undecorated base classes section](#undecorated-base-classes) |
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -162,7 +161,11 @@ Tip: In the [API reference section](api) of this doc site, deprecated APIs are i
|
|||||||
|
|
||||||
This section lists all of the currently-deprecated features, which includes template syntax, configuration options, and any other deprecations not listed in the [Deprecated APIs](#deprecated-apis) section above. It also includes deprecated API usage scenarios or API combinations, to augment the information above.
|
This section lists all of the currently-deprecated features, which includes template syntax, configuration options, and any other deprecations not listed in the [Deprecated APIs](#deprecated-apis) section above. It also includes deprecated API usage scenarios or API combinations, to augment the information above.
|
||||||
|
|
||||||
|
{@a bazelbuilder}
|
||||||
|
### Bazel builder and schematics
|
||||||
|
|
||||||
|
Bazel builder and schematics were introduced in Angular Labs to let users try out Bazel without having to manage Bazel version and BUILD files.
|
||||||
|
This feature has been deprecated. For more information, please refer to the [migration doc](https://github.com/angular/angular/blob/master/packages/bazel/src/schematics/README.md).
|
||||||
|
|
||||||
{@a wtf}
|
{@a wtf}
|
||||||
### Web Tracing Framework integration
|
### Web Tracing Framework integration
|
||||||
@ -376,60 +379,6 @@ However, in practice, Angular simply ignores two-way bindings to template variab
|
|||||||
<option *ngFor="let optionName of options" [value]="optionName"></option>
|
<option *ngFor="let optionName of options" [value]="optionName"></option>
|
||||||
```
|
```
|
||||||
|
|
||||||
{@a undecorated-base-classes}
|
|
||||||
### Undecorated base classes using Angular features
|
|
||||||
|
|
||||||
As of version 9, it's deprecated to have an undecorated base class that:
|
|
||||||
|
|
||||||
- uses Angular features
|
|
||||||
- is extended by a directive or component
|
|
||||||
|
|
||||||
Angular lifecycle hooks or any of the following Angular field decorators are considered Angular features:
|
|
||||||
|
|
||||||
- `@Input()`
|
|
||||||
- `@Output()`
|
|
||||||
- `@HostBinding()`
|
|
||||||
- `@HostListener()`
|
|
||||||
- `@ViewChild()` / `@ViewChildren()`
|
|
||||||
- `@ContentChild()` / `@ContentChildren()`
|
|
||||||
|
|
||||||
For example, the following case is deprecated because the base class uses `@Input()` and does not have a class-level decorator:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
class Base {
|
|
||||||
@Input()
|
|
||||||
foo: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive(...)
|
|
||||||
class Dir extends Base {
|
|
||||||
ngOnChanges(): void {
|
|
||||||
// notified when bindings to [foo] are updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In a future version of Angular, this code will start to throw an error.
|
|
||||||
To fix this example, add a selectorless `@Directive()` decorator to the base class:
|
|
||||||
|
|
||||||
```ts
|
|
||||||
@Directive()
|
|
||||||
class Base {
|
|
||||||
@Input()
|
|
||||||
foo: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive(...)
|
|
||||||
class Dir extends Base {
|
|
||||||
ngOnChanges(): void {
|
|
||||||
// notified when bindings to [foo] are updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
In version 9, the CLI has an automated migration that will update your code for you when `ng update` is run.
|
|
||||||
See [the dedicated migration guide](guide/migration-undecorated-classes) for more information about the change and more examples.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a binding-to-innertext}
|
{@a binding-to-innertext}
|
||||||
@ -514,13 +463,104 @@ export class MyModule {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
{@a ie-9-10-and-mobile}
|
||||||
|
### IE 9, 10, and IE mobile support
|
||||||
|
|
||||||
|
Support for IE 9 and 10 has been deprecated, as well as support for IE Mobile. These will be dropped in a future version.
|
||||||
|
Supporting outdated browsers like these increases bundle size, code complexity, and test load, and also requires time and effort that could be spent on improvements to the framework.
|
||||||
|
For example, fixing issues can be more difficult, as a straightforward fix for modern browsers could break old ones that have quirks due to not receiving updates from vendors.
|
||||||
|
|
||||||
|
The final decision was made on three key points:
|
||||||
|
* __Vendor support__: Microsoft dropped support of IE 9 and 10 on 1/12/16, meaning they no longer provide security updates or technical support. Additionally, Microsoft dropped support for Windows 10 Mobile in December 2019.
|
||||||
|
* __Usage statistics__: We looked at usage trends for IE 9 and 10 (as well as IE Mobile) from various sources and all indicated that usage percentages were extremely small (fractions of 1%).
|
||||||
|
* __Feedback from partners__: We also reached out to some of our Angular customers and none expressed concern about dropping IE 9, 10, nor IE Mobile support.
|
||||||
|
|
||||||
|
|
||||||
|
{@a wrapped-value}
|
||||||
|
### `WrappedValue`
|
||||||
|
|
||||||
|
The purpose of `WrappedValue` is to allow the same object instance to be treated as different for the purposes of change detection.
|
||||||
|
It is commonly used with the `async` pipe in the case where the `Observable` produces the same instance of the value.
|
||||||
|
|
||||||
|
Given that this use case is relatively rare and special handling impacts application performance, we have deprecated it in v10.
|
||||||
|
No replacement is planned for this deprecation.
|
||||||
|
|
||||||
|
If you rely on the behavior that the same object instance should cause change detection, you have two options:
|
||||||
|
- Clone the resulting value so that it has a new identity.
|
||||||
|
- Explicitly call [`ChangeDetectorRef.detectChanges()`](api/core/ChangeDetectorRef#detectchanges) to force the update.
|
||||||
|
|
||||||
|
{@a deprecated-cli-flags}
|
||||||
|
## Deprecated CLI APIs and Options
|
||||||
|
|
||||||
|
This section contains a complete list all of the currently deprecated CLI flags.
|
||||||
|
|
||||||
|
### @angular-devkit/build-angular
|
||||||
|
|
||||||
|
| API/Option | May be removed in | Notes |
|
||||||
|
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||||
|
| `i18nFile` | <!--v9--> v11 | Specified in the project locale configuration in version 9 and later. |
|
||||||
|
| `i18nFormat` | <!--v9--> v11 | Format is now automatically detected. |
|
||||||
|
| `i18nLocale` | <!--v9--> v11 | New [localization option](/guide/i18n#localize-config) in version 9 and later. |
|
||||||
|
| `lazyModules` | <!--v9--> v11 | Used with deprecated SystemJsNgModuleLoader. |
|
||||||
|
| `rebaseRootRelativeCssUrls` | <!--v8--> v11 | Intended only to assist with specific migration issues. |
|
||||||
|
| `scripts[].lazy` | <!--v8--> v11 | Renamed to `scripts[].inject`. |
|
||||||
|
| `styles[].lazy` | <!--v8--> v11 | Renamed to `styles[].inject`. |
|
||||||
|
| `i18nFormat` | <!--v9--> v11 | Renamed to `format` to simplify the user experience. |
|
||||||
|
| `i18nLocale` | <!--v9--> v11 | Redundant with project’s source locale. |
|
||||||
|
| `scripts[].lazy` | <!--v8--> v11 | Renamed to `scripts[].inject`. |
|
||||||
|
| `styles[].lazy` | <!--v8--> v11 | Renamed to `styles[].inject`. |
|
||||||
|
| `i18nFile` | <!--v9--> v11 | Specified in the project locale configuration in version 9 and later. |
|
||||||
|
| `i18nFormat` | <!--v9--> v11 | Format is now automatically detected. |
|
||||||
|
| `i18nLocale` | <!--v9--> v11 | New [localization option](/guide/i18n#localize-config) in version 9 and later. |
|
||||||
|
| `lazyModules` | <!--v9--> v11 | Used with deprecated SystemJsNgModuleLoader. |
|
||||||
|
|
||||||
|
### @angular-devkit/core
|
||||||
|
|
||||||
|
| API/Option | May be removed in | Notes |
|
||||||
|
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||||
|
| `ModuleNotFoundException` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||||
|
| `resolve` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||||
|
| `setResolveHook` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||||
|
| `ResolveOptions` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Not Yarn PnP compatible and not used in the Angular CLI. Use Node.js [require.resolve](https://nodejs.org/api/modules.html#modules_require_resolve_request_options).|
|
||||||
|
| `terminal` | <!--v8--> v10 | Unused implementation of terminal codes (color). |
|
||||||
|
| `isObservable` | <!--v8--> v10 | Not used within projects. Used with Tooling API only. Use `isObservable` function from the `rxjs` package.|
|
||||||
|
|
||||||
|
### @ngtools/webpack
|
||||||
|
|
||||||
|
| API/Option | May be removed in | Notes |
|
||||||
|
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||||
|
| `discoverLazyRoutes` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
|
||||||
|
| `additionalLazyModules` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
|
||||||
|
| `additionalLazyModuleResources` | <!--v9--> TBD | Used with deprecated SystemJsNgModuleLoader. |
|
||||||
|
|
||||||
|
### @schematics/angular
|
||||||
|
|
||||||
|
| API/Option | May be removed in | Notes |
|
||||||
|
| ------------------------------- | ----------------- |-------------------------------------------------------------------------------- |
|
||||||
|
| `entryComponent` | <!--v9--> TBD | No longer needed with Ivy. |
|
||||||
|
|
||||||
|
{@a removed}
|
||||||
|
## Removed APIs
|
||||||
|
|
||||||
|
The following APIs have been removed starting with version 10.0.0*:
|
||||||
|
|
||||||
|
| Package | API | Replacement | Notes |
|
||||||
|
| ---------------- | -------------- | ----------- | ----- |
|
||||||
|
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
||||||
|
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
||||||
|
| `@angular/core` | Style Sanitization | no action needed | See [style sanitization API removal](#style-sanitization) for more info
|
||||||
|
|
||||||
|
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
||||||
|
|
||||||
|
|
||||||
{@a esm5-fesm5}
|
{@a esm5-fesm5}
|
||||||
### `esm5` and `fesm5` code formats in @angular/* npm packages
|
### `esm5` and `fesm5` code formats in @angular/* npm packages
|
||||||
|
|
||||||
As of Angular v8, the CLI primarily consumes the `fesm2015` variant of the code distributed via `@angular/*` npm packages.
|
As of Angular v8, the CLI primarily consumes the `fesm2015` variant of the code distributed via `@angular/*` npm packages.
|
||||||
This renders the `esm5` and `fesm5` distributions obsolete and unnecessary, adding bloat to the package size and slowing down npm installations.
|
This renders the `esm5` and `fesm5` distributions obsolete and unnecessary, adding bloat to the package size and slowing down npm installations.
|
||||||
|
|
||||||
The future removal of this distribution will have no impact on CLI users, unless they modified their build configuration to explicitly consume these code distributions.
|
This removal has no impact on CLI users, unless they modified their build configuration to explicitly consume these code distributions.
|
||||||
|
|
||||||
Any application still relying on the `esm5` and `fesm5` as the input to its build system will need to ensure that the build pipeline is capable of accepting JavaScript code conforming to ECMAScript 2015 (ES2015) language specification.
|
Any application still relying on the `esm5` and `fesm5` as the input to its build system will need to ensure that the build pipeline is capable of accepting JavaScript code conforming to ECMAScript 2015 (ES2015) language specification.
|
||||||
|
|
||||||
@ -528,7 +568,7 @@ Note that this change doesn't make existing libraries distributed in this format
|
|||||||
The CLI will fall back and consume libraries in less desirable formats if others are not available.
|
The CLI will fall back and consume libraries in less desirable formats if others are not available.
|
||||||
However, we do recommend that libraries ship their code in ES2015 format in order to make builds faster and build output smaller.
|
However, we do recommend that libraries ship their code in ES2015 format in order to make builds faster and build output smaller.
|
||||||
|
|
||||||
In practical terms, the `package.json` of all `@angular` packages will change in the following way:
|
In practical terms, the `package.json` of all `@angular` packages has changed in the following way:
|
||||||
|
|
||||||
**Before**:
|
**Before**:
|
||||||
```
|
```
|
||||||
@ -562,92 +602,18 @@ In practical terms, the `package.json` of all `@angular` packages will change in
|
|||||||
|
|
||||||
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
For more information about the npm package format, see the [Angular Package Format spec](https://goo.gl/jB3GVv).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a removed}
|
{@a removed}
|
||||||
## Removed APIs
|
## Removed APIs
|
||||||
|
|
||||||
The following APIs have been removed starting with version 9.0.0*:
|
The following APIs have been removed starting with version 10.0.0*:
|
||||||
|
|
||||||
| Package | API | Replacement | Notes |
|
| Package | API | Replacement | Notes |
|
||||||
| ---------------- | -------------- | ----------- | ----- |
|
| ---------------- | -------------- | ----------- | ----- |
|
||||||
| `@angular/core` | [`Renderer`](https://v8.angular.io/api/core/Renderer) | [`Renderer2`](https://angular.io/api/core/Renderer2) | [Migration guide](guide/migration-renderer) |
|
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
||||||
| `@angular/core` | [`RootRenderer`](https://v8.angular.io/api/core/RootRenderer) | [`RendererFactory2`](https://angular.io/api/core/RendererFactory2) | none |
|
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
||||||
| `@angular/core` | [`RenderComponentType`](https://v8.angular.io/api/core/RenderComponentType) | [`RendererType2`](https://angular.io/api/core/RendererType2) | none |
|
|
||||||
| `@angular/core` | [`WtfScopeFn`](https://v8.angular.io/api/core/WtfScopeFn) | none | v8 | See [Web Tracing Framework](#wtf) |
|
|
||||||
| `@angular/core` | [`wtfCreateScope`](https://v8.angular.io/api/core/wtfCreateScope) | none | v8 | See [Web Tracing Framework](#wtf) |
|
|
||||||
| `@angular/core` | [`wtfStartTimeRange`](https://v8.angular.io/api/core/wtfStartTimeRange) | none | v8 | See [Web Tracing Framework](#wtf) |
|
|
||||||
| `@angular/core` | [`wtfEndTimeRange`](https://v8.angular.io/api/core/wtfEndTimeRange) | none | v8 | See [Web Tracing Framework](#wtf) |
|
|
||||||
| `@angular/core` | [`wtfLeave`](https://v8.angular.io/api/core/wtfLeave) | none | v8 | See [Web Tracing Framework](#wtf) |
|
|
||||||
| `@angular/common` | `DeprecatedI18NPipesModule` | [`CommonModule`](api/common/CommonModule#pipes) | none |
|
|
||||||
| `@angular/common` | `DeprecatedCurrencyPipe` | [`CurrencyPipe`](api/common/CurrencyPipe) | none |
|
|
||||||
| `@angular/common` | `DeprecatedDatePipe` | [`DatePipe`](api/common/DatePipe) | none |
|
|
||||||
| `@angular/common` | `DeprecatedDecimalPipe` | [`DecimalPipe`](api/common/DecimalPipe) | none |
|
|
||||||
| `@angular/common` | `DeprecatedPercentPipe` | [`PercentPipe`](api/common/PercentPipe) | none |
|
|
||||||
| `@angular/forms` | [`NgFormSelectorWarning`](https://v8.angular.io/api/forms/NgFormSelectorWarning) | none | none |
|
|
||||||
| `@angular/forms` | `ngForm` element selector | `ng-form` element selector | none |
|
|
||||||
| `@angular/service-worker` | `versionedFiles` | `files` | In the service worker configuration file `ngsw-config.json`, replace `versionedFiles` with `files`. See [Service Worker Configuration](guide/service-worker-config#assetgroups). |
|
|
||||||
|
|
||||||
*To see APIs removed in version 8, check out this guide on the [version 8 docs site](https://v8.angular.io/guide/deprecations#removed).
|
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
||||||
|
|
||||||
|
{@a style-sanitization}
|
||||||
<!-- The following anchor is used by redirects from the removed API pages. Do not change or remove. -->
|
### Style Sanitization for `[style]` and `[style.prop]` bindings
|
||||||
{@a http}
|
Angular used to sanitize `[style]` and `[style.prop]` bindings to prevent malicious code from being inserted through `javascript:` expressions in CSS `url()` entries. However, most modern browsers no longer support the usage of these expressions, so sanitization was only maintained for the sake of IE 6 and 7. Given that Angular does not support either IE 6 or 7 and sanitization has a performance cost, we will no longer sanitize style bindings as of version 10 of Angular.
|
||||||
### @angular/http
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Deprecation announced in version 5
|
|
||||||
https://blog.angular.io/version-5-0-0-of-angular-now-available-37e414935ced)
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
The entire [`@angular/http`](http://v7.angular.io/api/http) package has been removed. Use [`@angular/common/http`](api/common/http) instead.
|
|
||||||
|
|
||||||
The new API is a smaller, easier, and more powerful way to make HTTP requests in Angular.
|
|
||||||
The new API simplifies the default ergonomics: There is no need to map by invoking the `.json()` method.
|
|
||||||
It also supports typed return values and interceptors.
|
|
||||||
|
|
||||||
To update your apps:
|
|
||||||
* Replace `HttpModule` with [`HttpClientModule`](api/common/http/HttpClientModule) (from [`@angular/common/http`](api/common/http)) in each of your modules.
|
|
||||||
* Replace the `Http` service with the [`HttpClient`](api/common/http/HttpClient) service.
|
|
||||||
* Remove any `map(res => res.json())` calls. They are no longer needed.
|
|
||||||
|
|
||||||
For more information about using `@angular/common/http`, see the [HttpClient guide](guide/http "HTTP Client guide").
|
|
||||||
|
|
||||||
|
|
||||||
| `@angular/http` | Closest replacement in `@angular/common/http` |
|
|
||||||
| ------------- | ------------------------------------------- |
|
|
||||||
| `BaseRequestOptions` | [`HttpRequest`](/api/common/http/HttpRequest) |
|
|
||||||
| `BaseResponseOptions` | [`HttpResponse`](/api/common/http/HttpResponse) |
|
|
||||||
| `BrowserXhr` | |
|
|
||||||
| `Connection` | [`HttpBackend`](/api/common/http/HttpBackend) |
|
|
||||||
| `ConnectionBackend` | [`HttpBackend`](/api/common/http/HttpBackend) |
|
|
||||||
| `CookieXSRFStrategy` | [`HttpClientXsrfModule`](/api/common/http/HttpClientXsrfModule) |
|
|
||||||
| `Headers` | [`HttpHeaders`](/api/common/http/HttpHeaders) |
|
|
||||||
| `Http` | [`HttpClient`](/api/common/http/HttpClient) |
|
|
||||||
| `HttpModule` | [`HttpClientModule`](/api/common/http/HttpClientModule) |
|
|
||||||
| `Jsonp` | [`HttpClient`](/api/common/http/HttpClient) |
|
|
||||||
| `JSONPBackend` | [`JsonpClientBackend`](/api/common/http/JsonpClientBackend) |
|
|
||||||
| `JSONPConnection` | [`JsonpClientBackend`](/api/common/http/JsonpClientBackend) |
|
|
||||||
| `JsonpModule` | [`HttpClientJsonpModule`](/api/common/http/HttpClientJsonpModule) |
|
|
||||||
| `QueryEncoder` | [`HttpUrlEncodingCodec`](/api/common/http/HttpUrlEncodingCodec) |
|
|
||||||
| `ReadyState` | [`HttpBackend`](/api/common/http/HttpBackend) |
|
|
||||||
| `Request` | [`HttpRequest`](/api/common/http/HttpRequest) |
|
|
||||||
| `RequestMethod` | [`HttpClient`](/api/common/http/HttpClient) |
|
|
||||||
| `RequestOptions` | [`HttpRequest`](/api/common/http/HttpRequest) |
|
|
||||||
| `RequestOptionsArgs` | [`HttpRequest`](/api/common/http/HttpRequest) |
|
|
||||||
| `Response` | [`HttpResponse`](/api/common/http/HttpResponse) |
|
|
||||||
| `ResponseContentType` | [`HttpClient`](/api/common/http/HttpClient) |
|
|
||||||
| `ResponseOptions` | [`HttpResponse`](/api/common/http/HttpResponse) |
|
|
||||||
| `ResponseOptionsArgs` | [`HttpResponse`](/api/common/http/HttpResponse) |
|
|
||||||
| `ResponseType` | [`HttpClient`](/api/common/http/HttpClient) |
|
|
||||||
| `URLSearchParams` | [`HttpParams`](/api/common/http/HttpParams) |
|
|
||||||
| `XHRBackend` | [`HttpXhrBackend`](/api/common/http/HttpXhrBackend) |
|
|
||||||
| `XHRConnection` | [`HttpXhrBackend`](/api/common/http/HttpXhrBackend) |
|
|
||||||
| `XSRFStrategy` | [`HttpClientXsrfModule`](/api/common/http/HttpClientXsrfModule) |
|
|
||||||
|
|
||||||
|
|
||||||
| `@angular/http/testing` | Closest replacement in `@angular/common/http/testing` |
|
|
||||||
| --------------------- | ------------------------------------------- |
|
|
||||||
| `MockBackend` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
|
|
||||||
| `MockConnection` | [`HttpTestingController`](/api/common/http/testing/HttpTestingController) |
|
|
||||||
|
@ -931,7 +931,7 @@ If you do, be sure to set the `id` attribute - not the `name` attribute! The doc
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Alerts and Calllouts
|
## Alerts and Callouts
|
||||||
|
|
||||||
Alerts and callouts present warnings, extra detail or references to other pages. They can also be used to provide commentary that _enriches_ the reader's understanding of the content being presented.
|
Alerts and callouts present warnings, extra detail or references to other pages. They can also be used to provide commentary that _enriches_ the reader's understanding of the content being presented.
|
||||||
|
|
||||||
|
@ -1,42 +1,53 @@
|
|||||||
# Dynamic forms
|
# Building dynamic forms
|
||||||
|
|
||||||
{@a top}
|
Many forms, such as questionaires, can be very similar to one another in format and intent.
|
||||||
|
To make it faster and easier to generate different versions of such a form,
|
||||||
|
you can create a *dynamic form template* based on metadata that describes the business object model.
|
||||||
|
You can then use the template to generate new forms automatically, according to changes in the data model.
|
||||||
|
|
||||||
Building handcrafted forms can be costly and time-consuming,
|
The technique is particularly useful when you have a type of form whose content must
|
||||||
especially if you need a great number of them, they're similar to each other, and they change frequently
|
change frequently to meet rapidly changing business and regulatory requirements.
|
||||||
to meet rapidly changing business and regulatory requirements.
|
A typical use case is a questionaire. You might need to get input from users in different contexts.
|
||||||
|
The format and style of the forms a user sees should remain constant, while the actual questions you need to ask vary with the context.
|
||||||
|
|
||||||
It may be more economical to create the forms dynamically, based on
|
In this tutorial you will build a dynamic form that presents a basic questionaire.
|
||||||
metadata that describes the business object model.
|
You will build an online application for heroes seeking employment.
|
||||||
|
The agency is constantly tinkering with the application process, but by using the dynamic form
|
||||||
|
you can create the new forms on the fly without changing the application code.
|
||||||
|
|
||||||
This cookbook shows you how to use `formGroup` to dynamically
|
The tutorial walks you through the following steps.
|
||||||
render a simple form with different control types and validation.
|
|
||||||
It's a primitive start.
|
|
||||||
It might evolve to support a much richer variety of questions, more graceful rendering, and superior user experience.
|
|
||||||
All such greatness has humble beginnings.
|
|
||||||
|
|
||||||
The example in this cookbook is a dynamic form to build an
|
1. Enable reactive forms for a project.
|
||||||
online application experience for heroes seeking employment.
|
2. Establish a data model to represent form controls.
|
||||||
The agency is constantly tinkering with the application process.
|
3. Populate the model with sample data.
|
||||||
You can create the forms on the fly *without changing the application code*.
|
4. Develop a component to create form controls dynamically.
|
||||||
{@a toc}
|
|
||||||
|
The form you create uses input validation and styling to improve the user experience.
|
||||||
|
It has a Submit button that is only enabled when all user input is valid, and flags invalid input with color coding and error messages.
|
||||||
|
|
||||||
|
The basic version can evolve to support a richer variety of questions, more graceful rendering, and superior user experience.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
See the <live-example name="dynamic-form"></live-example>.
|
See the <live-example name="dynamic-form"></live-example>.
|
||||||
|
|
||||||
{@a bootstrap}
|
</div>
|
||||||
|
|
||||||
## Bootstrap
|
## Prerequisites
|
||||||
|
|
||||||
Start by creating an `NgModule` called `AppModule`.
|
Before doing this tutorial, you should have a basic understanding to the following.
|
||||||
|
|
||||||
This cookbook uses [reactive forms](guide/reactive-forms).
|
* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming.
|
||||||
|
|
||||||
Reactive forms belongs to a different `NgModule` called `ReactiveFormsModule`,
|
* Fundamental concepts of [Angular app design](guide/architecture "Introduction to Angular app-design concepts").
|
||||||
so in order to access any reactive forms directives, you have to import
|
|
||||||
`ReactiveFormsModule` from the `@angular/forms` library.
|
|
||||||
|
|
||||||
Bootstrap the `AppModule` in `main.ts`.
|
* Basic knowledge of [reactive forms](guide/reactive-forms "Reactive forms guide").
|
||||||
|
|
||||||
|
## Enable reactive forms for your project
|
||||||
|
|
||||||
|
Dynamic forms are based on reactive forms. To give the application access reactive forms directives, the [root module](guide/bootstrapping "Learn about bootstrapping an app from the root module.") imports `ReactiveFormsModule` from the `@angular/forms` library.
|
||||||
|
|
||||||
|
The following code from the example shows the setup in the root module.
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
|
|
||||||
@ -50,79 +61,56 @@ Bootstrap the `AppModule` in `main.ts`.
|
|||||||
|
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
|
|
||||||
{@a object-model}
|
{@a object-model}
|
||||||
|
|
||||||
## Question model
|
## Create a form object model
|
||||||
|
|
||||||
The next step is to define an object model that can describe all scenarios needed by the form functionality.
|
A dynamic form requires an object model that can describe all scenarios needed by the form functionality.
|
||||||
The hero application process involves a form with a lot of questions.
|
The example hero-application form is a set of questions—that is, each control in the form must ask a question and accept an answer.
|
||||||
The _question_ is the most fundamental object in the model.
|
|
||||||
|
|
||||||
The following `QuestionBase` is a fundamental question class.
|
The data model for this type of form must represent a question.
|
||||||
|
The example includes the `DynamicFormQuestionComponent`, which defines a question as the fundamental object in the model.
|
||||||
|
|
||||||
|
The following `QuestionBase` is a base class for a set of controls that can represent the question and its answer in the form.
|
||||||
|
|
||||||
<code-example path="dynamic-form/src/app/question-base.ts" header="src/app/question-base.ts">
|
<code-example path="dynamic-form/src/app/question-base.ts" header="src/app/question-base.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
### Define control classes
|
||||||
|
|
||||||
|
From this base, the example derives two new classes, `TextboxQuestion` and `DropdownQuestion`,
|
||||||
|
that represent different control types.
|
||||||
|
When you create the form template in the next step, you will instantiate these specific question types in order to render the appropriate controls dynamically.
|
||||||
|
|
||||||
From this base you can derive two new classes in `TextboxQuestion` and `DropdownQuestion`
|
* The `TextboxQuestion` control type presents a question and allows users to enter input.
|
||||||
that represent textbox and dropdown questions.
|
|
||||||
The idea is that the form will be bound to specific question types and render the
|
|
||||||
appropriate controls dynamically.
|
|
||||||
|
|
||||||
`TextboxQuestion` supports multiple HTML5 types such as text, email, and url
|
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example>
|
||||||
via the `type` property.
|
|
||||||
|
|
||||||
|
The `TextboxQuestion` control type will be represented in a form template using an `<input>` element.
|
||||||
|
The `type` attribute of the element will be defined based on the `type` field specified in the `options` argument (for example `text`, `email`, `url`).
|
||||||
|
|
||||||
<code-example path="dynamic-form/src/app/question-textbox.ts" header="src/app/question-textbox.ts"></code-example>
|
* The `DropdownQuestion` control presents a list of choices in a select box.
|
||||||
|
|
||||||
|
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example>
|
||||||
|
|
||||||
|
### Compose form groups
|
||||||
|
|
||||||
`DropdownQuestion` presents a list of choices in a select box.
|
A dynamic form uses a service to create grouped sets of input controls, based on the form model.
|
||||||
|
The following `QuestionControlService` collects a set of `FormGroup` instances that consume the metadata from the question model. You can specify default values and validation rules.
|
||||||
|
|
||||||
<code-example path="dynamic-form/src/app/question-dropdown.ts" header="src/app/question-dropdown.ts"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Next is `QuestionControlService`, a simple service for transforming the questions to a `FormGroup`.
|
|
||||||
In a nutshell, the form group consumes the metadata from the question model and
|
|
||||||
allows you to specify default values and validation rules.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example>
|
<code-example path="dynamic-form/src/app/question-control.service.ts" header="src/app/question-control.service.ts"></code-example>
|
||||||
|
|
||||||
{@a form-component}
|
{@a form-component}
|
||||||
|
|
||||||
## Question form components
|
## Compose dynamic form contents
|
||||||
Now that you have defined the complete model you are ready
|
|
||||||
to create components to represent the dynamic form.
|
|
||||||
|
|
||||||
|
The dynamic form itself will be represented by a container component, which you will add in a later step.
|
||||||
|
Each question is represented in the form component's template by an `<app-question>` tag, which matches an instance of `DynamicFormQuestionComponent`.
|
||||||
|
|
||||||
`DynamicFormComponent` is the entry point and the main container for the form.
|
The `DynamicFormQuestionComponent` is responsible for rendering the details of an individual question based on values in the data-bound question object.
|
||||||
|
The form relies on a [`[formGroup]` directive](api/forms/FormGroupDirective "API reference") to connect the template HTML to the underlying control objects.
|
||||||
<code-tabs>
|
The `DynamicFormQuestionComponent` creates form groups and populates them with controls defined in the question model, specifying display and validation rules.
|
||||||
|
|
||||||
<code-pane header="dynamic-form.component.html" path="dynamic-form/src/app/dynamic-form.component.html">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
<code-pane header="dynamic-form.component.ts" path="dynamic-form/src/app/dynamic-form.component.ts">
|
|
||||||
|
|
||||||
</code-pane>
|
|
||||||
|
|
||||||
</code-tabs>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
It presents a list of questions, each bound to a `<app-question>` component element.
|
|
||||||
The `<app-question>` tag matches the `DynamicFormQuestionComponent`,
|
|
||||||
the component responsible for rendering the details of each _individual_
|
|
||||||
question based on values in the data-bound question object.
|
|
||||||
|
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
|
|
||||||
@ -136,70 +124,88 @@ question based on values in the data-bound question object.
|
|||||||
|
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
|
The goal of the `DynamicFormQuestionComponent` is to present question types defined in your model.
|
||||||
|
|
||||||
Notice this component can present any type of question in your model.
|
|
||||||
You only have two types of questions at this point but you can imagine many more.
|
You only have two types of questions at this point but you can imagine many more.
|
||||||
The `ngSwitch` determines which type of question to display.
|
The `ngSwitch` statement in the template determines which type of question to display.
|
||||||
|
The switch uses directives with the [`formControlName`](api/forms/FormControlName "FormControlName directive API reference") and [`formGroup`](api/forms/FormGroupDirective "FormGroupDirective API reference") selectors. Both directives are defined in `ReactiveFormsModule`.
|
||||||
|
|
||||||
In both components you'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`. The templates can access these directives
|
|
||||||
directly since you imported `ReactiveFormsModule` from `AppModule`.
|
|
||||||
{@a questionnaire-data}
|
{@a questionnaire-data}
|
||||||
|
|
||||||
## Questionnaire data
|
### Supply data
|
||||||
|
|
||||||
`DynamicFormComponent` expects the list of questions in the form of an array bound to `@Input() questions`.
|
Another service is needed to supply a specific set of questions from which to build an individual form.
|
||||||
|
For this exercise you will create the `QuestionService` to supply this array of questions from the hard-coded sample data.
|
||||||
|
In a real-world app, the service might fetch data from a backend system.
|
||||||
|
The key point, however, is that you control the hero job-application questions entirely through the objects returned from `QuestionService`.
|
||||||
|
To maintain the questionnaire as requirements change, you only need to add, update, and remove objects from the `questions` array.
|
||||||
|
|
||||||
The set of questions you've defined for the job application is returned from the `QuestionService`.
|
|
||||||
In a real app you'd retrieve these questions from storage.
|
|
||||||
|
|
||||||
The key point is that you control the hero job application questions
|
|
||||||
entirely through the objects returned from `QuestionService`.
|
|
||||||
Questionnaire maintenance is a simple matter of adding, updating,
|
|
||||||
and removing objects from the `questions` array.
|
|
||||||
|
|
||||||
|
The `QuestionService` supplies a set of questions in the form of an array bound to `@Input()` questions.
|
||||||
|
|
||||||
<code-example path="dynamic-form/src/app/question.service.ts" header="src/app/question.service.ts">
|
<code-example path="dynamic-form/src/app/question.service.ts" header="src/app/question.service.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
{@a dynamic-template}
|
||||||
|
|
||||||
Finally, display an instance of the form in the `AppComponent` shell.
|
## Create a dynamic form template
|
||||||
|
|
||||||
|
The `DynamicFormComponent` component is the entry point and the main container for the form, which is represented using the `<app-dynamic-form>` in a template.
|
||||||
|
|
||||||
|
The `DynamicFormComponent` component presents a list of questions by binding each one to an `<app-question>` element that matches the `DynamicFormQuestionComponent`.
|
||||||
|
|
||||||
|
<code-tabs>
|
||||||
|
|
||||||
|
<code-pane header="dynamic-form.component.html" path="dynamic-form/src/app/dynamic-form.component.html">
|
||||||
|
|
||||||
|
</code-pane>
|
||||||
|
|
||||||
|
<code-pane header="dynamic-form.component.ts" path="dynamic-form/src/app/dynamic-form.component.ts">
|
||||||
|
|
||||||
|
</code-pane>
|
||||||
|
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
|
### Display the form
|
||||||
|
|
||||||
|
To display an instance of the dynamic form, the `AppComponent` shell template passes the `questions` array returned by the `QuestionService` to the form container component, `<app-dynamic-form>`.
|
||||||
|
|
||||||
<code-example path="dynamic-form/src/app/app.component.ts" header="app.component.ts">
|
<code-example path="dynamic-form/src/app/app.component.ts" header="app.component.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
{@a dynamic-template}
|
The example provides a model for a job application for heroes, but there are
|
||||||
|
no references to any specific hero question other than the objects returned by `QuestionService`.
|
||||||
## Dynamic Template
|
This separation of model and data allows you to repurpose the components for any type of survey
|
||||||
Although in this example you're modelling a job application for heroes, there are
|
|
||||||
no references to any specific hero question
|
|
||||||
outside the objects returned by `QuestionService`.
|
|
||||||
|
|
||||||
This is very important since it allows you to repurpose the components for any type of survey
|
|
||||||
as long as it's compatible with the *question* object model.
|
as long as it's compatible with the *question* object model.
|
||||||
The key is the dynamic data binding of metadata used to render the form
|
|
||||||
|
### Ensuring valid data
|
||||||
|
|
||||||
|
The form template uses dynamic data binding of metadata to render the form
|
||||||
without making any hardcoded assumptions about specific questions.
|
without making any hardcoded assumptions about specific questions.
|
||||||
In addition to control metadata, you are also adding validation dynamically.
|
It adds both control metadata and validation criteria dynamically.
|
||||||
|
|
||||||
The *Save* button is disabled until the form is in a valid state.
|
To ensure valid input, the *Save* button is disabled until the form is in a valid state.
|
||||||
When the form is valid, you can click *Save* and the app renders the current form values as JSON.
|
When the form is valid, you can click *Save* and the app renders the current form values as JSON.
|
||||||
This proves that any user input is bound back to the data model.
|
|
||||||
Saving and retrieving the data is an exercise for another time.
|
|
||||||
|
|
||||||
|
The following figure shows the final form.
|
||||||
The final form looks like this:
|
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form">
|
<img src="generated/images/guide/dynamic-form/dynamic-form.png" alt="Dynamic-Form">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
|
||||||
[Back to top](guide/dynamic-form#top)
|
* **Different types of forms and control collection**
|
||||||
|
|
||||||
|
This tutorial shows how to build a a questionaire, which is just one kind of dynamic form.
|
||||||
|
The example uses `FormGroup` to collect a set of controls.
|
||||||
|
For an example of a different type of dynamic form, see the section [Creating dynamic forms](guide/reactive-forms#creating-dynamic-forms "Create dynamic forms with arrays") in the Reactive Forms guide.
|
||||||
|
That example also shows how to use `FormArray` instead of `FormGroup` to collect a set of controls.
|
||||||
|
|
||||||
|
* **Validating user input**
|
||||||
|
|
||||||
|
The section [Validating form input](guide/reactive-forms#validating-form-input "Basic input validation") introduces the basics of how input validation works in reactive forms.
|
||||||
|
|
||||||
|
The [Form validation guide](guide/form-validation "Form validation guide") covers the topic in more depth.
|
||||||
|
@ -117,7 +117,7 @@ The recently-developed [custom elements](https://developer.mozilla.org/en-US/doc
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's `tsconfig.json`. As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
|
In browsers that support Custom Elements natively, the specification requires developers use ES2015 classes to define Custom Elements - developers can opt-in to this by setting the `target: "es2015"` property in their project's [TypeScript configuration file](/guide/typescript-configuration). As Custom Element and ES2015 support may not be available in all browsers, developers can instead choose to use a polyfill to support older browsers and ES5 code.
|
||||||
|
|
||||||
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --name=*your_project_name*`.
|
Use the [Angular CLI](cli) to automatically set up your project with the correct polyfill: `ng add @angular/elements --name=*your_project_name*`.
|
||||||
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
|
- For more information about polyfills, see [polyfill documentation](https://www.webcomponents.org/polyfills).
|
||||||
|
@ -40,7 +40,8 @@ The top level of the workspace contains workspace-wide configuration files, conf
|
|||||||
| `package-lock.json` | Provides version information for all packages installed into `node_modules` by the npm client. See [npm documentation](https://docs.npmjs.com/files/package-lock.json) for details. If you use the yarn client, this file will be [yarn.lock](https://yarnpkg.com/lang/en/docs/yarn-lock/) instead. |
|
| `package-lock.json` | Provides version information for all packages installed into `node_modules` by the npm client. See [npm documentation](https://docs.npmjs.com/files/package-lock.json) for details. If you use the yarn client, this file will be [yarn.lock](https://yarnpkg.com/lang/en/docs/yarn-lock/) instead. |
|
||||||
| `src/` | Source files for the root-level application project. |
|
| `src/` | Source files for the root-level application project. |
|
||||||
| `node_modules/` | Provides [npm packages](guide/npm-packages) to the entire workspace. Workspace-wide `node_modules` dependencies are visible to all projects. |
|
| `node_modules/` | Provides [npm packages](guide/npm-packages) to the entire workspace. Workspace-wide `node_modules` dependencies are visible to all projects. |
|
||||||
| `tsconfig.json` | Default [TypeScript](https://www.typescriptlang.org/) configuration for projects in the workspace. |
|
| `tsconfig.json` | The `tsconfig.json` file is a ["Solution Style"](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#support-for-solution-style-tsconfigjson-files) TypeScript configuration file. Code editors and TypeScript’s language server use this file to improve development experience. Compilers do not use this file. |
|
||||||
|
| `tsconfig.base.json` | The base [TypeScript](https://www.typescriptlang.org/) configuration for projects in the workspace. All other configuration files inherit from this base file. For more information, see the [Configuration inheritance with extends](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#configuration-inheritance-with-extends) section of the TypeScript documentation.|
|
||||||
| `tslint.json` | Default [TSLint](https://palantir.github.io/tslint/) configuration for projects in the workspace. |
|
| `tslint.json` | Default [TSLint](https://palantir.github.io/tslint/) configuration for projects in the workspace. |
|
||||||
|
|
||||||
|
|
||||||
@ -72,11 +73,17 @@ Files at the top level of `src/` support testing and running your application. S
|
|||||||
| `environments/` | Contains build configuration options for particular target environments. By default there is an unnamed standard development environment and a production ("prod") environment. You can define additional target environment configurations. |
|
| `environments/` | Contains build configuration options for particular target environments. By default there is an unnamed standard development environment and a production ("prod") environment. You can define additional target environment configurations. |
|
||||||
| `favicon.ico` | An icon to use for this application in the bookmark bar. |
|
| `favicon.ico` | An icon to use for this application in the bookmark bar. |
|
||||||
| `index.html` | The main HTML page that is served when someone visits your site. The CLI automatically adds all JavaScript and CSS files when building your app, so you typically don't need to add any `<script>` or` <link>` tags here manually. |
|
| `index.html` | The main HTML page that is served when someone visits your site. The CLI automatically adds all JavaScript and CSS files when building your app, so you typically don't need to add any `<script>` or` <link>` tags here manually. |
|
||||||
| `main.ts` | The main entry point for your application. Compiles the application with the [JIT compiler](https://angular.io/guide/glossary#jit) and bootstraps the application's root module (AppModule) to run in the browser. You can also use the [AOT compiler](https://angular.io/guide/aot-compiler) without changing any code by appending the `--aot` flag to the CLI `build` and `serve` commands. |
|
| `main.ts` | The main entry point for your application. Compiles the application with the [JIT compiler](guide/glossary#jit) and bootstraps the application's root module (AppModule) to run in the browser. You can also use the [AOT compiler](guide/aot-compiler) without changing any code by appending the `--aot` flag to the CLI `build` and `serve` commands. |
|
||||||
| `polyfills.ts` | Provides polyfill scripts for browser support. |
|
| `polyfills.ts` | Provides polyfill scripts for browser support. |
|
||||||
| `styles.sass` | Lists CSS files that supply styles for a project. The extension reflects the style preprocessor you have configured for the project. |
|
| `styles.sass` | Lists CSS files that supply styles for a project. The extension reflects the style preprocessor you have configured for the project. |
|
||||||
| `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. |
|
| `test.ts` | The main entry point for your unit tests, with some Angular-specific configuration. You don't typically need to edit this file. |
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
If you create an application using Angular's strict mode, you will also have an additional `package.json` file in the `src/app` directory. For more information, see [Strict mode](/guide/strict-mode).
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{@a app-src}
|
{@a app-src}
|
||||||
|
|
||||||
Inside the `src/` folder, the `app/` folder contains your project's logic and data.
|
Inside the `src/` folder, the `app/` folder contains your project's logic and data.
|
||||||
@ -89,17 +96,18 @@ Angular components, templates, and styles go here.
|
|||||||
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
| `app/app.component.css` | Defines the base CSS stylesheet for the root `AppComponent`. |
|
||||||
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
| `app/app.component.spec.ts` | Defines a unit test for the root `AppComponent`. |
|
||||||
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. |
|
| `app/app.module.ts` | Defines the root module, named `AppModule`, that tells Angular how to assemble the application. Initially declares only the `AppComponent`. As you add more components to the app, they must be declared here. |
|
||||||
|
| `app/package.json` | This file is generated only in applications created using `--strict` mode. This file is not used by package managers. It is used to tell the tools and bundlers whether the code under this directory is free of non-local [side-effects](guide/strict-mode#side-effect). |
|
||||||
|
|
||||||
### Application configuration files
|
### Application configuration files
|
||||||
|
|
||||||
The application-specific configuration files for the root application reside at the workspace root level.
|
The application-specific configuration files for the root application reside at the workspace root level.
|
||||||
For a multi-project workspace, project-specific configuration files are in the project root, under `projects/project-name/`.
|
For a multi-project workspace, project-specific configuration files are in the project root, under `projects/project-name/`.
|
||||||
|
|
||||||
Project-specific [TypeScript](https://www.typescriptlang.org/) configuration files inherit from the workspace-wide `tsconfig.json`, and project-specific [TSLint](https://palantir.github.io/tslint/) configuration files inherit from the workspace-wide `tslint.json`.
|
Project-specific [TypeScript](https://www.typescriptlang.org/) configuration files inherit from the workspace-wide `tsconfig.base.json`, and project-specific [TSLint](https://palantir.github.io/tslint/) configuration files inherit from the workspace-wide `tslint.json`.
|
||||||
|
|
||||||
| APPLICATION-SPECIFIC CONFIG FILES | PURPOSE |
|
| APPLICATION-SPECIFIC CONFIG FILES | PURPOSE |
|
||||||
| :--------------------- | :------------------------------------------|
|
| :--------------------- | :------------------------------------------|
|
||||||
| `browserslist` | Configures sharing of target browsers and Node.js versions among various front-end tools. See [Browserslist on GitHub](https://github.com/browserslist/browserslist) for more information. |
|
| `.browserslistrc` | Configures sharing of target browsers and Node.js versions among various front-end tools. See [Browserslist on GitHub](https://github.com/browserslist/browserslist) for more information. |
|
||||||
| `karma.conf.js` | Application-specific [Karma](https://karma-runner.github.io/2.0/config/configuration-file.html) configuration. |
|
| `karma.conf.js` | Application-specific [Karma](https://karma-runner.github.io/2.0/config/configuration-file.html) configuration. |
|
||||||
| `tsconfig.app.json` | Application-specific [TypeScript](https://www.typescriptlang.org/) configuration, including TypeScript and Angular template compiler options. See [TypeScript Configuration](guide/typescript-configuration) and [Angular Compiler Options](guide/angular-compiler-options). |
|
| `tsconfig.app.json` | Application-specific [TypeScript](https://www.typescriptlang.org/) configuration, including TypeScript and Angular template compiler options. See [TypeScript Configuration](guide/typescript-configuration) and [Angular Compiler Options](guide/angular-compiler-options). |
|
||||||
| `tsconfig.spec.json` | [TypeScript](https://www.typescriptlang.org/) configuration for the application tests. See [TypeScript Configuration](guide/typescript-configuration). |
|
| `tsconfig.spec.json` | [TypeScript](https://www.typescriptlang.org/) configuration for the application tests. See [TypeScript Configuration](guide/typescript-configuration). |
|
||||||
@ -165,7 +173,7 @@ my-workspace/
|
|||||||
|
|
||||||
## Library project files
|
## Library project files
|
||||||
|
|
||||||
When you generate a library using the CLI (with a command such as `ng generate library my-lib`), the generated files go into the projects/ folder of the workspace. For more information about creating your own libraries, see [Creating Libraries](https://angular.io/guide/creating-libraries).
|
When you generate a library using the CLI (with a command such as `ng generate library my-lib`), the generated files go into the projects/ folder of the workspace. For more information about creating your own libraries, see [Creating Libraries](guide/creating-libraries).
|
||||||
|
|
||||||
Libraries (unlike applications and their associated e2e projects) have their own `package.json` configuration files.
|
Libraries (unlike applications and their associated e2e projects) have their own `package.json` configuration files.
|
||||||
|
|
||||||
|
@ -1,132 +1,131 @@
|
|||||||
# Form validation
|
# Validating form input
|
||||||
|
|
||||||
Improve overall data quality by validating user input for accuracy and completeness.
|
You can improve overall data quality by validating user input for accuracy and completeness.
|
||||||
|
This page shows how to validate user input from the UI and display useful validation messages,
|
||||||
|
in both reactive and template-driven forms.
|
||||||
|
|
||||||
This page shows how to validate user input in the UI and display useful validation messages
|
**Prerequisites**
|
||||||
using both reactive and template-driven forms. It assumes some basic knowledge of the two
|
|
||||||
forms modules.
|
Before reading about form validation, you should have a basic understanding of the following.
|
||||||
|
|
||||||
|
* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming.
|
||||||
|
|
||||||
|
* Fundamental concepts of [Angular app design](guide/architecture "Introduction to Angular app-design concepts").
|
||||||
|
|
||||||
|
* The [two types of forms that Angular supports](guide/forms-overview "Introduction to Angular forms").
|
||||||
|
|
||||||
|
* Basics of either [Template-driven Forms](guide/forms "Template-driven forms guide") or [Reactive Forms](guide/reactive-forms "Reactive forms guide").
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
For the sample app that this page describes, see the <live-example></live-example>.
|
Get the complete example code for the reactive and template-driven forms used here to illustrate form validation.
|
||||||
|
Run the <live-example></live-example>.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
{@a template-driven-validation}
|
||||||
|
|
||||||
If you're new to forms, start by reviewing the [Forms](guide/forms) and
|
## Validating input in template-driven forms
|
||||||
[Reactive Forms](guide/reactive-forms) guides.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
## Template-driven validation
|
|
||||||
|
|
||||||
To add validation to a template-driven form, you add the same validation attributes as you
|
To add validation to a template-driven form, you add the same validation attributes as you
|
||||||
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
|
would with [native HTML form validation](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation).
|
||||||
Angular uses directives to match these attributes with validator functions in the framework.
|
Angular uses directives to match these attributes with validator functions in the framework.
|
||||||
|
|
||||||
Every time the value of a form control changes, Angular runs validation and generates
|
Every time the value of a form control changes, Angular runs validation and generates
|
||||||
either a list of validation errors, which results in an INVALID status, or null, which results in a VALID status.
|
either a list of validation errors that results in an INVALID status, or null, which results in a VALID status.
|
||||||
|
|
||||||
You can then inspect the control's state by exporting `ngModel` to a local template variable.
|
You can then inspect the control's state by exporting `ngModel` to a local template variable.
|
||||||
The following example exports `NgModel` into a variable called `name`:
|
The following example exports `NgModel` into a variable called `name`:
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)"></code-example>
|
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-with-error-msg" header="template/hero-form-template.component.html (name)"></code-example>
|
||||||
|
|
||||||
|
Notice the following features illustrated by the example.
|
||||||
Note the following:
|
|
||||||
|
|
||||||
* The `<input>` element carries the HTML validation attributes: `required` and `minlength`. It
|
* The `<input>` element carries the HTML validation attributes: `required` and `minlength`. It
|
||||||
also carries a custom validator directive, `forbiddenName`. For more
|
also carries a custom validator directive, `forbiddenName`. For more
|
||||||
information, see [Custom validators](guide/form-validation#custom-validators) section.
|
information, see the [Custom validators](#custom-validators) section.
|
||||||
|
|
||||||
* `#name="ngModel"` exports `NgModel` into a local variable called `name`. `NgModel` mirrors many of the properties of its underlying
|
* `#name="ngModel"` exports `NgModel` into a local variable called `name`. `NgModel` mirrors many of the properties of its underlying
|
||||||
`FormControl` instance, so you can use this in the template to check for control states such as `valid` and `dirty`. For a full list of control properties, see the [AbstractControl](api/forms/AbstractControl)
|
`FormControl` instance, so you can use this in the template to check for control states such as `valid` and `dirty`. For a full list of control properties, see the [AbstractControl](api/forms/AbstractControl)
|
||||||
API reference.
|
API reference.
|
||||||
|
|
||||||
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
|
* The `*ngIf` on the `<div>` element reveals a set of nested message `divs`
|
||||||
but only if the `name` is invalid and the control is either `dirty` or `touched`.
|
but only if the `name` is invalid and the control is either `dirty` or `touched`.
|
||||||
|
|
||||||
* Each nested `<div>` can present a custom message for one of the possible validation errors.
|
* Each nested `<div>` can present a custom message for one of the possible validation errors.
|
||||||
There are messages for `required`, `minlength`, and `forbiddenName`.
|
There are messages for `required`, `minlength`, and `forbiddenName`.
|
||||||
|
|
||||||
|
{@a dirty-or-touched}
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
To prevent the validator from displaying errors before the user has a chance to edit the form, you should check for either the `dirty` or `touched` states in a control.
|
||||||
|
|
||||||
|
* When the user changes the value in the watched field, the control is marked as "dirty".
|
||||||
#### Why check _dirty_ and _touched_?
|
* When the user blurs the form control element, the control is marked as "touched".
|
||||||
|
|
||||||
You may not want your application to display errors before the user has a chance to edit the form.
|
|
||||||
The checks for `dirty` and `touched` prevent errors from showing until the user
|
|
||||||
does one of two things: changes the value,
|
|
||||||
turning the control dirty; or blurs the form control element, setting the control to touched.
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Reactive form validation
|
{@a reactive-form-validation}
|
||||||
|
|
||||||
In a reactive form, the source of truth is the component class. Instead of adding validators through attributes in the template, you add validator functions directly to the form control model in the component class. Angular then calls these functions whenever the value of the control changes.
|
## Validating input in reactive forms
|
||||||
|
|
||||||
|
In a reactive form, the source of truth is the component class.
|
||||||
|
Instead of adding validators through attributes in the template, you add validator functions directly to the form control model in the component class.
|
||||||
|
Angular then calls these functions whenever the value of the control changes.
|
||||||
|
|
||||||
### Validator functions
|
### Validator functions
|
||||||
|
|
||||||
There are two types of validator functions: sync validators and async validators.
|
Validator functions can be either synchronous or asynchronous.
|
||||||
|
|
||||||
* **Sync validators**: functions that take a control instance and immediately return either a set of validation errors or `null`. You can pass these in as the second argument when you instantiate a `FormControl`.
|
* **Sync validators**: Synchronous functions that take a control instance and immediately return either a set of validation errors or `null`. You can pass these in as the second argument when you instantiate a `FormControl`.
|
||||||
|
|
||||||
* **Async validators**: functions that take a control instance and return a Promise
|
* **Async validators**: Asynchronous functions that take a control instance and return a Promise
|
||||||
or Observable that later emits a set of validation errors or `null`. You can
|
or Observable that later emits a set of validation errors or `null`. You can
|
||||||
pass these in as the third argument when you instantiate a `FormControl`.
|
pass these in as the third argument when you instantiate a `FormControl`.
|
||||||
|
|
||||||
Note: for performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.
|
For performance reasons, Angular only runs async validators if all sync validators pass. Each must complete before errors are set.
|
||||||
|
|
||||||
### Built-in validators
|
### Built-in validator functions
|
||||||
|
|
||||||
You can choose to [write your own validator functions](guide/form-validation#custom-validators), or you can use some of
|
You can choose to [write your own validator functions](#custom-validators), or you can use some of Angular's built-in validators.
|
||||||
Angular's built-in validators.
|
|
||||||
|
|
||||||
The same built-in validators that are available as attributes in template-driven forms, such as `required` and `minlength`, are all available to use as functions from the `Validators` class. For a full list of built-in validators, see the [Validators](api/forms/Validators) API reference.
|
The same built-in validators that are available as attributes in template-driven forms, such as `required` and `minlength`, are all available to use as functions from the `Validators` class.
|
||||||
|
For a full list of built-in validators, see the [Validators](api/forms/Validators) API reference.
|
||||||
|
|
||||||
To update the hero form to be a reactive form, you can use some of the same
|
To update the hero form to be a reactive form, you can use some of the same
|
||||||
built-in validators—this time, in function form. See below:
|
built-in validators—this time, in function form, as in the following example.
|
||||||
|
|
||||||
{@a reactive-component-class}
|
{@a reactive-component-class}
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
|
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
|
||||||
|
|
||||||
Note that:
|
In this example, the `name` control sets up two built-in validators—`Validators.required` and `Validators.minLength(4)`—and one custom validator, `forbiddenNameValidator`. (For more details see [custom validators](#custom-validators) below.)
|
||||||
|
|
||||||
* The name control sets up two built-in validators—`Validators.required` and `Validators.minLength(4)`—and one custom validator, `forbiddenNameValidator`. For more details see the [Custom validators](guide/form-validation#custom-validators) section in this guide.
|
All of these validators are synchronous, so they are passed as the second argument. Notice that you can support multiple validators by passing the functions in as an array.
|
||||||
* As these validators are all sync validators, you pass them in as the second argument.
|
|
||||||
* Support multiple validators by passing the functions in as an array.
|
|
||||||
* This example adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthands
|
|
||||||
for the template.
|
|
||||||
|
|
||||||
|
This example also adds a few getter methods. In a reactive form, you can always access any form control through the `get` method on its parent group, but sometimes it's useful to define getters as shorthand for the template.
|
||||||
|
|
||||||
If you look at the template for the name input again, it is fairly similar to the template-driven example.
|
If you look at the template for the `name` input again, it is fairly similar to the template-driven example.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)"></code-example>
|
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="name-with-error-msg" header="reactive/hero-form-reactive.component.html (name with error msg)"></code-example>
|
||||||
|
|
||||||
Key takeaways:
|
This form differs from the template-driven version in that it no longer exports any directives. Instead, it uses the `name` getter defined in the component class.
|
||||||
|
|
||||||
* The form no longer exports any directives, and instead uses the `name` getter defined in
|
Notice that the `required` attribute is still present in the template. Although it's not necessary for validation, it should be retained to for accessibility purposes.
|
||||||
the component class.
|
|
||||||
* The `required` attribute is still present. While it's not necessary for validation purposes,
|
|
||||||
you may want to keep it in your template for CSS styling or accessibility reasons.
|
|
||||||
|
|
||||||
|
{@a custom-validators}
|
||||||
|
|
||||||
## Custom validators
|
## Defining custom validators
|
||||||
|
|
||||||
Since the built-in validators won't always match the exact use case of your application, sometimes you'll want to create a custom validator.
|
The built-in validators don't always match the exact use case of your application, so you sometimes need to create a custom validator.
|
||||||
|
|
||||||
Consider the `forbiddenNameValidator` function from previous
|
Consider the `forbiddenNameValidator` function from previous [reactive-form examples](#reactive-component-class).
|
||||||
[examples](guide/form-validation#reactive-component-class) in
|
Here's what the definition of that function looks like.
|
||||||
this guide. Here's what the definition of that function looks like:
|
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)"></code-example>
|
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="custom-validator" header="shared/forbidden-name.directive.ts (forbiddenNameValidator)"></code-example>
|
||||||
|
|
||||||
The function is actually a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function.
|
The function is a factory that takes a regular expression to detect a _specific_ forbidden name and returns a validator function.
|
||||||
|
|
||||||
In this sample, the forbidden name is "bob", so the validator will reject any hero name containing "bob".
|
In this sample, the forbidden name is "bob", so the validator will reject any hero name containing "bob".
|
||||||
Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
|
Elsewhere it could reject "alice" or any name that the configuring regular expression matches.
|
||||||
@ -137,55 +136,57 @@ null if the control value is valid _or_ a validation error object.
|
|||||||
The validation error object typically has a property whose name is the validation key, `'forbiddenName'`,
|
The validation error object typically has a property whose name is the validation key, `'forbiddenName'`,
|
||||||
and whose value is an arbitrary dictionary of values that you could insert into an error message, `{name}`.
|
and whose value is an arbitrary dictionary of values that you could insert into an error message, `{name}`.
|
||||||
|
|
||||||
Custom async validators are similar to sync validators, but they must instead return a Promise or Observable
|
Custom async validators are similar to sync validators, but they must instead return a Promise or observable that later emits null or a validation error object.
|
||||||
that later emits null or a validation error object. In the case of an Observable, the Observable must complete,
|
In the case of an observable, the observable must complete, at which point the form uses the last value emitted for validation.
|
||||||
at which point the form uses the last value emitted for validation.
|
|
||||||
|
|
||||||
### Adding to reactive forms
|
{@a adding-to-reactive-forms}
|
||||||
|
|
||||||
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
|
### Adding custom validators to reactive forms
|
||||||
to the `FormControl`.
|
|
||||||
|
In reactive forms, add a custom validator by passing the function directly to the `FormControl`.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
|
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" header="reactive/hero-form-reactive.component.ts (validator functions)"></code-example>
|
||||||
|
|
||||||
### Adding to template-driven forms
|
{@a adding-to-template-driven-forms}
|
||||||
|
|
||||||
In template-driven forms, you don't have direct access to the `FormControl` instance, so you can't pass the
|
### Adding custom validators to template-driven forms
|
||||||
validator in like you can for reactive forms. Instead, you need to add a directive to the template.
|
|
||||||
|
|
||||||
The corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `forbiddenNameValidator`.
|
In template-driven forms, add a directive to the template, where the directive wraps the validator function.
|
||||||
|
For example, the corresponding `ForbiddenValidatorDirective` serves as a wrapper around the `forbiddenNameValidator`.
|
||||||
|
|
||||||
Angular recognizes the directive's role in the validation process because the directive registers itself
|
Angular recognizes the directive's role in the validation process because the directive registers itself with the `NG_VALIDATORS` provider, as shown in the following example.
|
||||||
with the `NG_VALIDATORS` provider, a provider with an extensible collection of validators.
|
`NG_VALIDATORS` is a predefined provider with an extensible collection of validators.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)"></code-example>
|
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive-providers" header="shared/forbidden-name.directive.ts (providers)"></code-example>
|
||||||
|
|
||||||
The directive class then implements the `Validator` interface, so that it can easily integrate
|
The directive class then implements the `Validator` interface, so that it can easily integrate
|
||||||
with Angular forms. Here is the rest of the directive to help you get an idea of how it all
|
with Angular forms.
|
||||||
comes together:
|
Here is the rest of the directive to help you get an idea of how it all
|
||||||
|
comes together.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive" header="shared/forbidden-name.directive.ts (directive)">
|
<code-example path="form-validation/src/app/shared/forbidden-name.directive.ts" region="directive" header="shared/forbidden-name.directive.ts (directive)">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector, `appForbiddenName`, to any input element to activate it. For example:
|
Once the `ForbiddenValidatorDirective` is ready, you can add its selector, `appForbiddenName`, to any input element to activate it.
|
||||||
|
For example:
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)"></code-example>
|
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="name-input" header="template/hero-form-template.component.html (forbidden-name-input)"></code-example>
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
You may have noticed that the custom validation directive is instantiated with `useExisting`
|
Notice that the custom validation directive is instantiated with `useExisting` rather than `useClass`. The registered validator must be _this instance_ of
|
||||||
rather than `useClass`. The registered validator must be _this instance_ of
|
|
||||||
the `ForbiddenValidatorDirective`—the instance in the form with
|
the `ForbiddenValidatorDirective`—the instance in the form with
|
||||||
its `forbiddenName` property bound to “bob". If you were to replace
|
its `forbiddenName` property bound to “bob".
|
||||||
`useExisting` with `useClass`, then you’d be registering a new class instance, one that
|
|
||||||
doesn’t have a `forbiddenName`.
|
If you were to replace `useExisting` with `useClass`, then you’d be registering a new class instance, one that doesn’t have a `forbiddenName`.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Control status CSS classes
|
## Control status CSS classes
|
||||||
|
|
||||||
Like in AngularJS, Angular automatically mirrors many control properties onto the form control element as CSS classes. You can use these classes to style form control elements according to the state of the form. The following classes are currently supported:
|
Angular automatically mirrors many control properties onto the form control element as CSS classes. You can use these classes to style form control elements according to the state of the form.
|
||||||
|
The following classes are currently supported.
|
||||||
|
|
||||||
* `.ng-valid`
|
* `.ng-valid`
|
||||||
* `.ng-invalid`
|
* `.ng-invalid`
|
||||||
@ -195,25 +196,27 @@ Like in AngularJS, Angular automatically mirrors many control properties onto th
|
|||||||
* `.ng-untouched`
|
* `.ng-untouched`
|
||||||
* `.ng-touched`
|
* `.ng-touched`
|
||||||
|
|
||||||
The hero form uses the `.ng-valid` and `.ng-invalid` classes to
|
In the following example, the hero form uses the `.ng-valid` and `.ng-invalid` classes to
|
||||||
set the color of each form control's border.
|
set the color of each form control's border.
|
||||||
|
|
||||||
<code-example path="form-validation/src/assets/forms.css" header="forms.css (status classes)">
|
<code-example path="form-validation/src/assets/forms.css" header="forms.css (status classes)">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
## Cross field validation
|
## Cross-field validation
|
||||||
This section shows how to perform cross field validation. It assumes some basic knowledge of creating custom validators.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
A cross-field validator is a [custom validator](#custom-validators "Read about custom validators") that compares the values of different fields in a form and accepts or rejects them in combination.
|
||||||
|
For example, you might have a form that offers mutually incompatible options, so that if the user can choose A or B, but not both.
|
||||||
|
Some field values might also depend on others; a user might be allowed to choose B only if A is also chosen.
|
||||||
|
|
||||||
If you haven't created custom validators before, start by reviewing the [custom validators section](guide/form-validation#custom-validators).
|
The following cross validation examples show how to do the following:
|
||||||
|
|
||||||
</div>
|
* Validate reactive or template-based form input based on the values of two sibling controls,
|
||||||
|
* Show a descriptive error message after the user interacted with the form and the validation failed.
|
||||||
|
|
||||||
In the following section, we will make sure that our heroes do not reveal their true identities by filling out the Hero Form. We will do that by validating that the hero names and alter egos do not match.
|
The examples use cross-validation to ensure that heroes do not reveal their true identities by filling out the Hero Form. The validators do this by checking that the hero names and alter egos do not match.
|
||||||
|
|
||||||
### Adding to reactive forms
|
### Adding cross-validation to reactive forms
|
||||||
|
|
||||||
The form has the following structure:
|
The form has the following structure:
|
||||||
|
|
||||||
@ -225,7 +228,9 @@ const heroForm = new FormGroup({
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Notice that the name and alterEgo are sibling controls. To evaluate both controls in a single custom validator, we should perform the validation in a common ancestor control: the `FormGroup`. That way, we can query the `FormGroup` for the child controls which will allow us to compare their values.
|
Notice that the `name` and `alterEgo` are sibling controls.
|
||||||
|
To evaluate both controls in a single custom validator, you must perform the validation in a common ancestor control: the `FormGroup`.
|
||||||
|
You query the `FormGroup` for its child controls so that you can compare their values.
|
||||||
|
|
||||||
To add a validator to the `FormGroup`, pass the new validator in as the second argument on creation.
|
To add a validator to the `FormGroup`, pass the new validator in as the second argument on creation.
|
||||||
|
|
||||||
@ -237,74 +242,73 @@ const heroForm = new FormGroup({
|
|||||||
}, { validators: identityRevealedValidator });
|
}, { validators: identityRevealedValidator });
|
||||||
```
|
```
|
||||||
|
|
||||||
The validator code is as follows:
|
The validator code is as follows.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts"></code-example>
|
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" header="shared/identity-revealed.directive.ts"></code-example>
|
||||||
|
|
||||||
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
|
The `identity` validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
|
||||||
|
|
||||||
First we retrieve the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method. Then we simply compare the values of the `name` and `alterEgo` controls.
|
The validator retrieves the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method, then compares the values of the `name` and `alterEgo` controls.
|
||||||
|
|
||||||
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
|
If the values do not match, the hero's identity remains secret, both are valid, and the validator returns null.
|
||||||
|
If they do match, the hero's identity is revealed and the validator must mark the form as invalid by returning an error object.
|
||||||
|
|
||||||
|
To provide better user experience, the template shows an appropriate error message when the form is invalid.
|
||||||
|
|
||||||
Next, to provide better user experience, we show an appropriate error message when the form is invalid.
|
|
||||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html"></code-example>
|
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" header="reactive/hero-form-template.component.html"></code-example>
|
||||||
|
|
||||||
Note that we check if:
|
This `*ngIf` displays the error if the `FormGroup` has the cross validation error returned by the `identityRevealed` validator, but only if the user has finished [interacting with the form](#dirty-or-touched).
|
||||||
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
|
|
||||||
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
|
|
||||||
|
|
||||||
### Adding to template driven forms
|
### Adding cross-validation to template-driven forms
|
||||||
First we must create a directive that will wrap the validator function. We provide it as the validator using the `NG_VALIDATORS` token. If you are not sure why, or you do not fully understand the syntax, revisit the previous [section](guide/form-validation#adding-to-template-driven-forms).
|
|
||||||
|
For a template-driven form, you must create a directive to wrap the validator function.
|
||||||
|
You provide that directive as the validator using the [`NG_VALIDATORS` token](#adding-to-template-driven-forms "Read about providing validators"), as shown in the following example.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts"></code-example>
|
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" header="shared/identity-revealed.directive.ts"></code-example>
|
||||||
|
|
||||||
Next, we have to add the directive to the html template. Since the validator must be registered at the highest level in the form, we put the directive on the `form` tag.
|
You must add the new directive to the HTML template.
|
||||||
|
Because the validator must be registered at the highest level in the form, the following template puts the directive on the `form` tag.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html"></code-example>
|
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" header="template/hero-form-template.component.html"></code-example>
|
||||||
|
|
||||||
To provide better user experience, we show an appropriate error message when the form is invalid.
|
To provide better user experience, we show an appropriate error message when the form is invalid.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html"></code-example>
|
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" header="template/hero-form-template.component.html"></code-example>
|
||||||
|
|
||||||
Note that we check if:
|
This is the same in both template-driven and reactive forms.
|
||||||
- the form has the cross validation error returned by the `identityRevealed` validator,
|
|
||||||
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
|
|
||||||
|
|
||||||
This completes the cross validation example. We managed to:
|
## Creating asynchronous validators
|
||||||
- validate the form based on the values of two sibling controls,
|
|
||||||
- show a descriptive error message after the user interacted with the form and the validation failed.
|
|
||||||
|
|
||||||
## Async Validation
|
Asynchronous validators implement the `AsyncValidatorFn` and `AsyncValidator` interfaces.
|
||||||
This section shows how to create asynchronous validators. It assumes some basic knowledge of creating [custom validators](guide/form-validation#custom-validators).
|
These are very similar to their synchronous counterparts, with the following differences.
|
||||||
|
|
||||||
### The Basics
|
* The `validate()` functions must return a Promise or an observable,
|
||||||
Just like synchronous validators have the `ValidatorFn` and `Validator` interfaces, asynchronous validators have their own counterparts: `AsyncValidatorFn` and `AsyncValidator`.
|
* The observable returned must be finite, meaning it must complete at some point.
|
||||||
|
To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as `first`, `last`, `take`, or `takeUntil`.
|
||||||
|
|
||||||
They are very similar with the only difference being:
|
Asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful.
|
||||||
|
This check allows forms to avoid potentially expensive async validation processes (such as an HTTP request) if the more basic validation methods have already found invalid input.
|
||||||
|
|
||||||
* They must return a Promise or an Observable,
|
After asynchronous validation begins, the form control enters a `pending` state. You can inspect the control's `pending` property and use it to give visual feedback about the ongoing validation operation.
|
||||||
* The observable returned must be finite, meaning it must complete at some point. To convert an infinite observable into a finite one, pipe the observable through a filtering operator such as `first`, `last`, `take`, or `takeUntil`.
|
|
||||||
|
|
||||||
It is important to note that the asynchronous validation happens after the synchronous validation, and is performed only if the synchronous validation is successful. This check allows forms to avoid potentially expensive async validation processes such as an HTTP request if more basic validation methods fail.
|
A common UI pattern is to show a spinner while the async validation is being performed. The following example shows how to achieve this in a template-driven form.
|
||||||
|
|
||||||
After asynchronous validation begins, the form control enters a `pending` state. You can inspect the control's `pending` property and use it to give visual feedback about the ongoing validation.
|
|
||||||
|
|
||||||
A common UI pattern is to show a spinner while the async validation is being performed. The following example presents how to achieve this with template-driven forms:
|
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<input [(ngModel)]="name" #model="ngModel" appSomeAsyncValidator>
|
<input [(ngModel)]="name" #model="ngModel" appSomeAsyncValidator>
|
||||||
<app-spinner *ngIf="model.pending"></app-spinner>
|
<app-spinner *ngIf="model.pending"></app-spinner>
|
||||||
```
|
```
|
||||||
|
|
||||||
### Implementing Custom Async Validator
|
### Implementing a custom async validator
|
||||||
In the following section, validation is performed asynchronously to ensure that our heroes pick an alter ego that is not already taken. New heroes are constantly enlisting and old heroes are leaving the service. That means that we do not have the list of available alter egos ahead of time.
|
|
||||||
|
|
||||||
To validate the potential alter ego, we need to consult a central database of all currently enlisted heroes. The process is asynchronous, so we need a special validator for that.
|
In the following example, an async validator ensures that heroes pick an alter ego that is not already taken.
|
||||||
|
New heroes are constantly enlisting and old heroes are leaving the service, so the list of available alter egos cannot be retrieved ahead of time.
|
||||||
|
To validate the potential alter ego entry, the validator must initiate an asynchronous operation to consult a central database of all currently enlisted heroes.
|
||||||
|
|
||||||
Let's start by creating the validator class.
|
The following code create the validator class, `UniqueAlterEgoValidator`, which implements the `AsyncValidator` interface.
|
||||||
|
|
||||||
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></code-example>
|
<code-example path="form-validation/src/app/shared/alter-ego.directive.ts" region="async-validator"></code-example>
|
||||||
|
|
||||||
As you can see, the `UniqueAlterEgoValidator` class implements the `AsyncValidator` interface. In the constructor, we inject the `HeroesService` that has the following interface:
|
The constructor injects the `HeroesService`, which defines the following interface.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
interface HeroesService {
|
interface HeroesService {
|
||||||
@ -312,29 +316,37 @@ interface HeroesService {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
In a real world application, the `HeroesService` is responsible for making an HTTP request to the hero database to check if the alter ego is available. From the validator's point of view, the actual implementation of the service is not important, so we can just code against the `HeroesService` interface.
|
In a real world application, the `HeroesService` would be responsible for making an HTTP request to the hero database to check if the alter ego is available.
|
||||||
|
From the validator's point of view, the actual implementation of the service is not important, so the example can just code against the `HeroesService` interface.
|
||||||
|
|
||||||
As the validation begins, the `UniqueAlterEgoValidator` delegates to the `HeroesService` `isAlterEgoTaken()` method with the current control value. At this point the control is marked as `pending` and remains in this state until the observable chain returned from the `validate()` method completes.
|
As the validation begins, the `UniqueAlterEgoValidator` delegates to the `HeroesService` `isAlterEgoTaken()` method with the current control value.
|
||||||
|
At this point the control is marked as `pending` and remains in this state until the observable chain returned from the `validate()` method completes.
|
||||||
|
|
||||||
The `isAlterEgoTaken()` method dispatches an HTTP request that checks if the alter ego is available, and returns `Observable<boolean>` as the result. We pipe the response through the `map` operator and transform it into a validation result. As always, we return `null` if the form is valid, and `ValidationErrors` if it is not. We make sure to handle any potential errors with the `catchError` operator.
|
The `isAlterEgoTaken()` method dispatches an HTTP request that checks if the alter ego is available, and returns `Observable<boolean>` as the result.
|
||||||
|
The `validate()` method pipes the response through the `map` operator and transforms it into a validation result.
|
||||||
|
|
||||||
Here we decided that `isAlterEgoTaken()` error is treated as a successful validation, because failure to make a validation request does not necessarily mean that the alter ego is invalid. You could handle the error differently and return the `ValidationError` object instead.
|
The method then, like any validator, returns `null` if the form is valid, and `ValidationErrors` if it is not.
|
||||||
|
This validator handles any potential errors with the `catchError` operator.
|
||||||
|
In this case, the validator treats the `isAlterEgoTaken()` error as a successful validation, because failure to make a validation request does not necessarily mean that the alter ego is invalid.
|
||||||
|
You could handle the error differently and return the `ValidationError` object instead.
|
||||||
|
|
||||||
After some time passes, the observable chain completes and the async validation is done. The `pending` flag is set to `false`, and the form validity is updated.
|
After some time passes, the observable chain completes and the asynchronous validation is done.
|
||||||
|
The `pending` flag is set to `false`, and the form validity is updated.
|
||||||
|
|
||||||
### Note on performance
|
### Optimizing performance of async validators
|
||||||
|
|
||||||
By default, all validators are run after every form value change. With synchronous validators, this will not likely have a noticeable impact on application performance. However, it's common for async validators to perform some kind of HTTP request to validate the control. Dispatching an HTTP request after every keystroke could put a strain on the backend API, and should be avoided if possible.
|
By default, all validators run after every form value change. With synchronous validators, this does not normally have a noticeable impact on application performance.
|
||||||
|
Async validators, however, commonly perform some kind of HTTP request to validate the control. Dispatching an HTTP request after every keystroke could put a strain on the backend API, and should be avoided if possible.
|
||||||
|
|
||||||
We can delay updating the form validity by changing the `updateOn` property from `change` (default) to `submit` or `blur`.
|
You can delay updating the form validity by changing the `updateOn` property from `change` (default) to `submit` or `blur`.
|
||||||
|
|
||||||
With template-driven forms:
|
With template-driven forms, set the property in the template.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
|
<input [(ngModel)]="name" [ngModelOptions]="{updateOn: 'blur'}">
|
||||||
```
|
```
|
||||||
|
|
||||||
With reactive forms:
|
With reactive forms, set the property in the `FormControl` instance.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
new FormControl('', {updateOn: 'blur'});
|
new FormControl('', {updateOn: 'blur'});
|
||||||
|
@ -4,22 +4,27 @@ Handling user input with forms is the cornerstone of many common applications. A
|
|||||||
|
|
||||||
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
|
Angular provides two different approaches to handling user input through forms: reactive and template-driven. Both capture user input events from the view, validate the user input, create a form model and data model to update, and provide a way to track changes.
|
||||||
|
|
||||||
Reactive and template-driven forms process and manage form data differently. Each offers different advantages.
|
|
||||||
|
|
||||||
**In general:**
|
|
||||||
|
|
||||||
* **Reactive forms** are more robust: they're more scalable, reusable, and testable. If forms are a key part of your application, or you're already using reactive patterns for building your application, use reactive forms.
|
|
||||||
* **Template-driven forms** are useful for adding a simple form to an app, such as an email list signup form. They're easy to add to an app, but they don't scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, use template-driven forms.
|
|
||||||
|
|
||||||
This guide provides information to help you decide which type of form works best for your situation. It introduces the common building blocks used by both approaches. It also summarizes the key differences between the two approaches, and demonstrates those differences in the context of setup, data flow, and testing.
|
This guide provides information to help you decide which type of form works best for your situation. It introduces the common building blocks used by both approaches. It also summarizes the key differences between the two approaches, and demonstrates those differences in the context of setup, data flow, and testing.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
## Prerequisites
|
||||||
|
|
||||||
**Note:** For complete information about each kind of form, see [Reactive Forms](guide/reactive-forms) and [Template-driven Forms](guide/forms).
|
This guide assumes that you have a basic understanding of the following.
|
||||||
|
|
||||||
</div>
|
* [TypeScript](https://www.typescriptlang.org/docs/home.html "The TypeScript language") and HTML5 programming.
|
||||||
|
|
||||||
## Key differences
|
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts.").
|
||||||
|
|
||||||
|
* The basics of [Angular template syntax](guide/architecture-components#template-syntax "Template syntax intro").
|
||||||
|
|
||||||
|
## Choosing an approach
|
||||||
|
|
||||||
|
Reactive forms and template-driven forms process and manage form data differently. Each approach offers different advantages.
|
||||||
|
|
||||||
|
* **Reactive forms** provide direct, explicit access to the underlying forms object model. Compared to template-driven forms, they are more robust: they're more scalable, reusable, and testable. If forms are a key part of your application, or you're already using reactive patterns for building your application, use reactive forms.
|
||||||
|
|
||||||
|
* **Template-driven forms** rely on directives in the template to create and manipulate the underlying object model. They are useful for adding a simple form to an app, such as an email list signup form. They're easy to add to an app, but they don't scale as well as reactive forms. If you have very basic form requirements and logic that can be managed solely in the template, template-driven forms could be a good fit.
|
||||||
|
|
||||||
|
### Key differences
|
||||||
|
|
||||||
The table below summarizes the key differences between reactive and template-driven forms.
|
The table below summarizes the key differences between reactive and template-driven forms.
|
||||||
|
|
||||||
@ -30,17 +35,33 @@ The table below summarizes the key differences between reactive and template-dri
|
|||||||
|
|
||||||
||Reactive|Template-driven|
|
||Reactive|Template-driven|
|
||||||
|--- |--- |--- |
|
|--- |--- |--- |
|
||||||
|Setup (form model)|More explicit, created in component class|Less explicit, created by directives|
|
|[Setup of form model](#setup) | Explicit, created in component class | Implicit, created by directives |
|
||||||
|Data model|Structured|Unstructured|
|
|[Data model](#data-flow-in-forms) | Structured and immutable | Unstructured and mutable |
|
||||||
|Predictability|Synchronous|Asynchronous|
|
|Predictability | Synchronous | Asynchronous |
|
||||||
|Form validation|Functions|Directives|
|
|[Form validation](#validation) | Functions | Directives |
|
||||||
|Mutability|Immutable|Mutable|
|
|
||||||
|Scalability|Low-level API access|Abstraction on top of APIs|
|
|
||||||
|
|
||||||
## Common foundation
|
### Scalability
|
||||||
|
|
||||||
Both reactive and template-driven forms share underlying building blocks.
|
If forms are a central part of your application, scalability is very important. Being able to reuse form models across components is critical.
|
||||||
|
|
||||||
|
Reactive forms are more scalable than template-driven forms. They provide direct access to the underlying form API, and synchronous access to the form data model, making creating large-scale forms easier.
|
||||||
|
Reactive forms require less setup for testing, and testing does not require deep understanding of change detection to properly test form updates and validation.
|
||||||
|
|
||||||
|
Template-driven forms focus on simple scenarios and are not as reusable.
|
||||||
|
They abstract away the underlying form API, and provide only asynchronous access to the form data model.
|
||||||
|
The abstraction of template-driven forms also affects testing.
|
||||||
|
Tests are deeply reliant on manual change detection execution to run properly, and require more setup.
|
||||||
|
|
||||||
|
{@a setup}
|
||||||
|
|
||||||
|
## Setting up the form model
|
||||||
|
|
||||||
|
Both reactive and template-driven forms track value changes between the form input elements that users interact with and the form data in your component model.
|
||||||
|
The two approaches share underlying building blocks, but differ in how you create and manage the common form-control instances.
|
||||||
|
|
||||||
|
### Common form foundation classes
|
||||||
|
|
||||||
|
Both reactive and template-driven forms are built on the following base classes.
|
||||||
|
|
||||||
* `FormControl` tracks the value and validation status of an individual form control.
|
* `FormControl` tracks the value and validation status of an individual form control.
|
||||||
|
|
||||||
@ -50,59 +71,59 @@ Both reactive and template-driven forms share underlying building blocks.
|
|||||||
|
|
||||||
* `ControlValueAccessor` creates a bridge between Angular `FormControl` instances and native DOM elements.
|
* `ControlValueAccessor` creates a bridge between Angular `FormControl` instances and native DOM elements.
|
||||||
|
|
||||||
See the [Form model setup](#setup-the-form-model) section below for an introduction to how these control instances are created and managed with reactive and template-driven forms. Further details are provided in the [data flow section](#data-flow-in-forms) of this guide.
|
|
||||||
|
|
||||||
{@a setup-the-form-model}
|
{@a setup-the-form-model}
|
||||||
|
|
||||||
## Form model setup
|
|
||||||
|
|
||||||
Reactive and template-driven forms both use a form model to track value changes between Angular forms and form input elements. The examples below show how the form model is defined and created.
|
|
||||||
|
|
||||||
### Setup in reactive forms
|
### Setup in reactive forms
|
||||||
|
|
||||||
Here's a component with an input field for a single control implemented using reactive forms.
|
With reactive forms, you define the form model directly in the component class.
|
||||||
|
The `[formControl]` directive links the explicitly created `FormControl` instance to a specific form element in the view, using an internal value accessor.
|
||||||
|
|
||||||
|
The following component implements an input field for a single control, using reactive forms. In this example, the form model is the `FormControl` instance.
|
||||||
|
|
||||||
<code-example path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts">
|
<code-example path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.ts">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
The source of truth provides the value and status of the form element at a given point in time. In reactive forms, the form model is the source of truth. In the example above, the form model is the `FormControl` instance.
|
Figure 1 shows how, in reactive forms, the form model is the source of truth; it provides the value and status of the form element at any given point in time, through the `[formControl]` directive on the input element.
|
||||||
|
|
||||||
|
**Figure 1.** *Direct access to forms model in a reactive form.*
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms-overview/key-diff-reactive-forms.png" alt="Reactive forms key differences">
|
<img src="generated/images/guide/forms-overview/key-diff-reactive-forms.png" alt="Reactive forms key differences">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
With reactive forms, the form model is explicitly defined in the component class. The reactive form directive (in this case, `FormControlDirective`) then links the existing `FormControl` instance to a specific form element in the view using a value accessor (`ControlValueAccessor` instance).
|
|
||||||
|
|
||||||
### Setup in template-driven forms
|
### Setup in template-driven forms
|
||||||
|
|
||||||
Here's the same component with an input field for a single control implemented using template-driven forms.
|
In template-driven forms, the form model is implicit, rather than explicit. The directive `NgModel` creates and manages a `FormControl` instance for a given form element.
|
||||||
|
|
||||||
|
The following component implements the same input field for a single control, using template-driven forms.
|
||||||
|
|
||||||
<code-example path="forms-overview/src/app/template/favorite-color/favorite-color.component.ts">
|
<code-example path="forms-overview/src/app/template/favorite-color/favorite-color.component.ts">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
In template-driven forms, the source of truth is the template.
|
In a template-driven form the source of truth is the template. You do not have direct programmatic access to the `FormControl` instance, as shown in Figure 2.
|
||||||
|
|
||||||
|
**Figure 2.** *Indirect access to forms model in a template-driven form.*
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms-overview/key-diff-td-forms.png" alt="Template-driven forms key differences">
|
<img src="generated/images/guide/forms-overview/key-diff-td-forms.png" alt="Template-driven forms key differences">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The abstraction of the form model promotes simplicity over structure. The template-driven form directive `NgModel` is responsible for creating and managing the `FormControl` instance for a given form element. It's less explicit, but you no longer have direct control over the form model.
|
|
||||||
|
|
||||||
{@a data-flow-in-forms}
|
{@a data-flow-in-forms}
|
||||||
|
|
||||||
## Data flow in forms
|
## Data flow in forms
|
||||||
|
|
||||||
When building forms in Angular, it's important to understand how the framework handles data flowing from the user or from programmatic changes. Reactive and template-driven forms follow two different strategies when handling form input. The data flow examples below begin with the favorite color input field example from above, and then show how changes to favorite color are handled in reactive forms compared to template-driven forms.
|
When an application contains a form, Angular must keep the view in sync with the component model and the component model in sync with the view.
|
||||||
|
As users change values and make selections through the view, the new values must be reflected in the data model.
|
||||||
|
Similarly, when the program logic changes values in the data model, those values must be reflected in the view.
|
||||||
|
|
||||||
|
Reactive and template-driven forms differ in how they handle data flowing from the user or from programmatic changes.
|
||||||
|
The following diagrams illustrate both kinds of data flow for each type of form, using the a favorite-color input field defined above.
|
||||||
|
|
||||||
### Data flow in reactive forms
|
### Data flow in reactive forms
|
||||||
|
|
||||||
As described above, in reactive forms each form element in the view is directly linked to a form model (`FormControl` instance). Updates from the view to the model and from the model to the view are synchronous and aren't dependent on the UI rendered. The diagrams below use the same favorite color example to demonstrate how data flows when an input field's value is changed from the view and then from the model.
|
In reactive forms each form element in the view is directly linked to the form model (a `FormControl` instance). Updates from the view to the model and from the model to the view are synchronous and do not depend on how the UI is rendered.
|
||||||
|
|
||||||
<div class="lightbox">
|
The view-to-model diagram shows how data flows when an input field's value is changed from the view through the following steps.
|
||||||
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-vtm.png" alt="Reactive forms data flow - view to model" width="100%">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The steps below outline the data flow from view to model.
|
|
||||||
|
|
||||||
1. The user types a value into the input element, in this case the favorite color *Blue*.
|
1. The user types a value into the input element, in this case the favorite color *Blue*.
|
||||||
1. The form input element emits an "input" event with the latest value.
|
1. The form input element emits an "input" event with the latest value.
|
||||||
@ -111,25 +132,25 @@ The steps below outline the data flow from view to model.
|
|||||||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-mtv.png" alt="Reactive forms data flow - model to view" width="100%">
|
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-vtm.png" alt="Reactive forms data flow - view to model">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The steps below outline the data flow from model to view.
|
The model-to-view diagram shows how a programmatic change to the model is propagated to the view through the following steps.
|
||||||
|
|
||||||
1. The user calls the `favoriteColorControl.setValue()` method, which updates the `FormControl` value.
|
1. The user calls the `favoriteColorControl.setValue()` method, which updates the `FormControl` value.
|
||||||
1. The `FormControl` instance emits the new value through the `valueChanges` observable.
|
1. The `FormControl` instance emits the new value through the `valueChanges` observable.
|
||||||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||||
1. The control value accessor on the form input element updates the element with the new value.
|
1. The control value accessor on the form input element updates the element with the new value.
|
||||||
|
|
||||||
### Data flow in template-driven forms
|
|
||||||
|
|
||||||
In template-driven forms, each form element is linked to a directive that manages the form model internally. The diagrams below use the same favorite color example to demonstrate how data flows when an input field's value is changed from the view and then from the model.
|
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms-overview/dataflow-td-forms-vtm.png" alt="Template-driven forms data flow - view to model" width="100%">
|
<img src="generated/images/guide/forms-overview/dataflow-reactive-forms-mtv.png" alt="Reactive forms data flow - model to view">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The steps below outline the data flow from view to model when the input value changes from *Red* to *Blue*.
|
### Data flow in template-driven forms
|
||||||
|
|
||||||
|
In template-driven forms, each form element is linked to a directive that manages the form model internally.
|
||||||
|
|
||||||
|
The view-to-model diagram shows how data flows when an input field's value is changed from the view through the following steps.
|
||||||
|
|
||||||
1. The user types *Blue* into the input element.
|
1. The user types *Blue* into the input element.
|
||||||
1. The input element emits an "input" event with the value *Blue*.
|
1. The input element emits an "input" event with the value *Blue*.
|
||||||
@ -141,10 +162,10 @@ The steps below outline the data flow from view to model when the input value ch
|
|||||||
is updated to the value emitted by the `ngModelChange` event (*Blue*).
|
is updated to the value emitted by the `ngModelChange` event (*Blue*).
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms-overview/dataflow-td-forms-mtv.png" alt="Template-driven forms data flow - model to view" width="100%">
|
<img src="generated/images/guide/forms-overview/dataflow-td-forms-vtm.png" alt="Template-driven forms data flow - view to model" width="100%">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The steps below outline the data flow from model to view when the `favoriteColor` changes from *Blue* to *Red*.
|
The model-to-view diagram shows how data flows from model to view when the `favoriteColor` changes from *Blue* to *Red*, through the following steps
|
||||||
|
|
||||||
1. The `favoriteColor` value is updated in the component.
|
1. The `favoriteColor` value is updated in the component.
|
||||||
1. Change detection begins.
|
1. Change detection begins.
|
||||||
@ -156,6 +177,30 @@ The steps below outline the data flow from model to view when the `favoriteColor
|
|||||||
1. Any subscribers to the `valueChanges` observable receive the new value.
|
1. Any subscribers to the `valueChanges` observable receive the new value.
|
||||||
1. The control value accessor updates the form input element in the view with the latest `favoriteColor` value.
|
1. The control value accessor updates the form input element in the view with the latest `favoriteColor` value.
|
||||||
|
|
||||||
|
<div class="lightbox">
|
||||||
|
<img src="generated/images/guide/forms-overview/dataflow-td-forms-mtv.png" alt="Template-driven forms data flow - model to view" width="100%">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### Mutability of the data model
|
||||||
|
|
||||||
|
The change-tracking method plays a role in the efficiency of your application.
|
||||||
|
|
||||||
|
* **Reactive forms** keep the data model pure by providing it as an immutable data structure.
|
||||||
|
Each time a change is triggered on the data model, the `FormControl` instance returns a new data model rather than updating the existing data model.
|
||||||
|
This gives you the ability to track unique changes to the data model through the control's observable.
|
||||||
|
Change detection is more efficient because it only needs to update on unique changes.
|
||||||
|
Because data updates follow reactive patterns, you can integrate with observable operators to transform data.
|
||||||
|
|
||||||
|
* **Template-driven** forms rely on mutability with two-way data binding to update the data model in the component as changes are made in the template.
|
||||||
|
Because there are no unique changes to track on the data model when using two-way data binding, change detection is less efficient at determining when updates are required.
|
||||||
|
|
||||||
|
The difference is demonstrated in the previous examples that use the favorite-color input element.
|
||||||
|
|
||||||
|
* With reactive forms, the **`FormControl` instance** always returns a new value when the control's value is updated.
|
||||||
|
|
||||||
|
* With template-driven forms, the **favorite color property** is always modified to its new value.
|
||||||
|
|
||||||
|
{@a validation}
|
||||||
## Form validation
|
## Form validation
|
||||||
|
|
||||||
Validation is an integral part of managing any set of forms. Whether you're checking for required fields or querying an external API for an existing username, Angular provides a set of built-in validators as well as the ability to create custom validators.
|
Validation is an integral part of managing any set of forms. Whether you're checking for required fields or querying an external API for an existing username, Angular provides a set of built-in validators as well as the ability to create custom validators.
|
||||||
@ -167,36 +212,37 @@ For more information, see [Form Validation](guide/form-validation).
|
|||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Testing plays a large part in complex applications and a simpler testing strategy is useful when validating that your forms function correctly. Reactive forms and template-driven forms have different levels of reliance on rendering the UI to perform assertions based on form control and form field changes. The following examples demonstrate the process of testing forms with reactive and template-driven forms.
|
Testing plays a large part in complex applications. A simpler testing strategy is useful when validating that your forms function correctly.
|
||||||
|
Reactive forms and template-driven forms have different levels of reliance on rendering the UI to perform assertions based on form control and form field changes.
|
||||||
|
The following examples demonstrate the process of testing forms with reactive and template-driven forms.
|
||||||
|
|
||||||
### Testing reactive forms
|
### Testing reactive forms
|
||||||
|
|
||||||
Reactive forms provide a relatively easy testing strategy because they provide synchronous access to the form and data models, and they can be tested without rendering the UI. In these tests, status and data are queried and manipulated through the control without interacting with the change detection cycle.
|
Reactive forms provide a relatively easy testing strategy because they provide synchronous access to the form and data models, and they can be tested without rendering the UI.
|
||||||
|
In these tests, status and data are queried and manipulated through the control without interacting with the change detection cycle.
|
||||||
|
|
||||||
The following tests use the favorite color components mentioned earlier to verify the data flows from view to model and model to view for a reactive form.
|
The following tests use the favorite-color components from previous examples to verify the view-to-model and model-to-view data flows for a reactive form.
|
||||||
|
|
||||||
The following test verifies the data flow from view to model.
|
**Verifying view-to-model data flow**
|
||||||
|
|
||||||
<code-example path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" region="view-to-model" header="Favorite color test - view to model">
|
The first example performs the following steps to verify the view-to-model data flow.
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Here are the steps performed in the view to model test.
|
|
||||||
|
|
||||||
1. Query the view for the form input element, and create a custom "input" event for the test.
|
1. Query the view for the form input element, and create a custom "input" event for the test.
|
||||||
1. Set the new value for the input to *Red*, and dispatch the "input" event on the form input element.
|
1. Set the new value for the input to *Red*, and dispatch the "input" event on the form input element.
|
||||||
1. Assert that the component's `favoriteColorControl` value matches the value from the input.
|
1. Assert that the component's `favoriteColorControl` value matches the value from the input.
|
||||||
|
|
||||||
The following test verifies the data flow from model to view.
|
<code-example path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" region="view-to-model" header="Favorite color test - view to model">
|
||||||
|
|
||||||
<code-example path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" region="model-to-view" header="Favorite color test - model to view">
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
Here are the steps performed in the model to view test.
|
The next example performs the following steps to verify the model-to-view data flow.
|
||||||
|
|
||||||
1. Use the `favoriteColorControl`, a `FormControl` instance, to set the new value.
|
1. Use the `favoriteColorControl`, a `FormControl` instance, to set the new value.
|
||||||
1. Query the view for the form input element.
|
1. Query the view for the form input element.
|
||||||
1. Assert that the new value set on the control matches the value in the input.
|
1. Assert that the new value set on the control matches the value in the input.
|
||||||
|
|
||||||
|
<code-example path="forms-overview/src/app/reactive/favorite-color/favorite-color.component.spec.ts" region="model-to-view" header="Favorite color test - model to view">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
### Testing template-driven forms
|
### Testing template-driven forms
|
||||||
|
|
||||||
Writing tests with template-driven forms requires a detailed knowledge of the change detection process and an understanding of how directives run on each cycle to ensure that elements are queried, tested, or changed at the correct time.
|
Writing tests with template-driven forms requires a detailed knowledge of the change detection process and an understanding of how directives run on each cycle to ensure that elements are queried, tested, or changed at the correct time.
|
||||||
@ -228,46 +274,17 @@ Here are the steps performed in the model to view test.
|
|||||||
1. Query the view for the form input element.
|
1. Query the view for the form input element.
|
||||||
1. Assert that the input value matches the value of the `favoriteColor` property in the component instance.
|
1. Assert that the input value matches the value of the `favoriteColor` property in the component instance.
|
||||||
|
|
||||||
## Mutability
|
|
||||||
|
|
||||||
The change tracking method plays a role in the efficiency of your application.
|
|
||||||
|
|
||||||
|
|
||||||
* **Reactive forms** keep the data model pure by providing it as an immutable data structure. Each time a change is triggered on the data model, the `FormControl` instance returns a new data model rather than updating the existing data model. This gives you the ability to track unique changes to the data model through the control's observable. This provides one way for change detection to be more efficient because it only needs to update on unique changes. It also follows reactive patterns that integrate with observable operators to transform data.
|
|
||||||
|
|
||||||
* **Template-driven** forms rely on mutability with two-way data binding to update the data model in the component as changes are made in the template. Because there are no unique changes to track on the data model when using two-way data binding, change detection is less efficient at determining when updates are required.
|
|
||||||
|
|
||||||
The difference is demonstrated in the examples above using the **favorite color** input element.
|
|
||||||
|
|
||||||
|
|
||||||
* With reactive forms, the **`FormControl` instance** always returns a new value when the control's value is updated.
|
|
||||||
|
|
||||||
* With template-driven forms, the **favorite color property** is always modified to its new value.
|
|
||||||
|
|
||||||
## Scalability
|
|
||||||
|
|
||||||
If forms are a central part of your application, scalability is very important. Being able to reuse form models across components is critical.
|
|
||||||
|
|
||||||
|
|
||||||
* **Reactive forms** provide access to low-level APIs and synchronous access to the form model, making creating large-scale forms easier.
|
|
||||||
|
|
||||||
* **Template-driven** forms focus on simple scenarios, are not as reusable, abstract away the low-level APIs, and provide asynchronous access to the form model. The abstraction with template-driven forms also surfaces in testing, where testing reactive forms requires less setup and no dependence on the change detection cycle when updating and validating the form and data models during testing.
|
|
||||||
|
|
||||||
## Final thoughts
|
|
||||||
|
|
||||||
Choosing a strategy begins with understanding the strengths and weaknesses of the options presented. Low-level API and form model access, predictability, mutability, straightforward validation and testing strategies, and scalability are all important considerations in choosing the infrastructure you use to build your forms in Angular. Template-driven forms are similar to patterns in AngularJS, but they have limitations given the criteria of many modern, large-scale Angular apps. Reactive forms minimize these limitations. Reactive forms integrate with reactive patterns already present in other areas of the Angular architecture, and complement those requirements well.
|
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
To learn more about reactive forms, see the following guides:
|
To learn more about reactive forms, see the following guides:
|
||||||
|
|
||||||
* [Reactive Forms](guide/reactive-forms)
|
* [Reactive forms](guide/reactive-forms)
|
||||||
* [Form Validation](guide/form-validation#reactive-form-validation)
|
* [Form validation](guide/form-validation#reactive-form-validation)
|
||||||
* [Dynamic Forms](guide/dynamic-form)
|
* [Dynamic forms](guide/dynamic-form)
|
||||||
|
|
||||||
To learn more about template-driven forms, see the following guides:
|
To learn more about template-driven forms, see the following guides:
|
||||||
|
|
||||||
* [Template-driven Forms](guide/forms#template-driven-forms)
|
* [Building a template-driven form](guide/forms) tutorial
|
||||||
* [Form Validation](guide/form-validation#template-driven-validation)
|
* [Form validation](guide/form-validation#template-driven-validation)
|
||||||
|
* `NgForm` directive API reference
|
||||||
|
@ -1,389 +1,234 @@
|
|||||||
# Template-driven forms
|
# Building a template-driven form
|
||||||
|
|
||||||
Forms are the mainstay of business applications.
|
|
||||||
You use forms to log in, submit a help request, place an order, book a flight,
|
|
||||||
schedule a meeting, and perform countless other data-entry tasks.
|
|
||||||
|
|
||||||
In developing a form, it's important to create a data-entry experience that guides the
|
|
||||||
user efficiently and effectively through the workflow.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
For the sample app that this page describes, see the <live-example></live-example>.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Introduction to Template-driven forms
|
|
||||||
|
|
||||||
Developing forms requires design skills (which are out of scope for this page), as well as framework support for
|
|
||||||
*two-way data binding, change tracking, validation, and error handling*,
|
|
||||||
which you'll learn about on this page.
|
|
||||||
|
|
||||||
This page shows you how to build a simple form from scratch. Along the way you'll learn how to:
|
|
||||||
|
|
||||||
* Build an Angular form with a component and template.
|
|
||||||
* Use `ngModel` to create two-way data bindings for reading and writing input-control values.
|
|
||||||
* Track state changes and the validity of form controls.
|
|
||||||
* Provide visual feedback using special CSS classes that track the state of the controls.
|
|
||||||
* Display validation errors to users and enable/disable form controls.
|
|
||||||
* Share information across HTML elements using template reference variables.
|
|
||||||
|
|
||||||
{@a template-driven}
|
{@a template-driven}
|
||||||
|
|
||||||
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
|
This tutorial shows you how to create a template-driven form whose control elements are bound to data properties, with input validation to maintain data integrity and styling to improve the user experience.
|
||||||
the form-specific directives and techniques described in this page.
|
|
||||||
|
Template-driven forms use [two-way data binding](guide/architecture-components#data-binding "Intro to 2-way data binding") to update the data model in the component as changes are made in the template and vice versa.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
You can also use a reactive (or model-driven) approach to build forms.
|
Angular supports two design approaches for interactive forms. You can build forms by writing templates using Angular [template syntax and directives](guide/glossary#template "Definition of template terms") with the form-specific directives and techniques described in this tutorial, or you can use a reactive (or model-driven) approach to build forms.
|
||||||
However, this page focuses on template-driven forms.
|
|
||||||
|
Template-driven forms are suitable for small or simple forms, while reactive forms are more scalable and suitable for complex forms.
|
||||||
|
For a comparison of the two approaches, see [Introduction to Forms](guide/forms-overview "Overview of Angular forms.")
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
You can build almost any form with an Angular template—login forms, contact forms, and pretty much any business form.
|
You can build almost any kind of form with an Angular template—login forms, contact forms, and pretty much any business form.
|
||||||
You can lay out the controls creatively, bind them to data, specify validation rules and display validation errors,
|
You can lay out the controls creatively and bind them to the data in your object model.
|
||||||
|
You can specify validation rules and display validation errors,
|
||||||
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
|
conditionally enable or disable specific controls, trigger built-in visual feedback, and much more.
|
||||||
|
|
||||||
Angular makes the process easy by handling many of the repetitive, boilerplate tasks you'd
|
This tutorial shows you how to build a form from scratch, using a simplified sample form like the one from the [Tour of Heroes tutorial](tutorial "Tour of Heroes") to illustrate the techniques.
|
||||||
otherwise wrestle with yourself.
|
|
||||||
|
|
||||||
You'll learn to build a template-driven form that looks like this:
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
Run or download the example app: <live-example></live-example>.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Objectives
|
||||||
|
|
||||||
|
This tutorial teaches you how to do the following:
|
||||||
|
|
||||||
|
* Build an Angular form with a component and template.
|
||||||
|
* Use `ngModel` to create two-way data bindings for reading and writing input-control values.
|
||||||
|
* Provide visual feedback using special CSS classes that track the state of the controls.
|
||||||
|
* Display validation errors to users and enable or disable form controls based on the form status.
|
||||||
|
* Share information across HTML elements using [template reference variables](guide/template-syntax#template-reference-variables-var).
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Before going further into template-driven forms, you should have a basic understanding of the following.
|
||||||
|
|
||||||
|
* TypeScript and HTML5 programming.
|
||||||
|
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to Angular concepts.").
|
||||||
|
* The basics of [Angular template syntax](guide/template-syntax "Template syntax guide").
|
||||||
|
* The form-design concepts that are presented in [Introduction to Forms](guide/forms-overview "Overview of Angular forms.").
|
||||||
|
|
||||||
|
{@a intro}
|
||||||
|
|
||||||
|
## Build a template-driven form
|
||||||
|
|
||||||
|
Template-driven forms rely on directives defined in the `FormsModule`.
|
||||||
|
|
||||||
|
* The `NgModel` directive reconciles value changes in the attached form element with changes in the data model, allowing you to respond to user input with input validation and error handling.
|
||||||
|
|
||||||
|
* The `NgForm` directive creates a top-level `FormGroup` instance and binds it to a `<form>` element to track aggregated form value and validation status.
|
||||||
|
As soon as you import `FormsModule`, this directive becomes active by default on all `<form>` tags. You don't need to add a special selector.
|
||||||
|
|
||||||
|
* The `NgModelGroup` directive creates and binds a `FormGroup` instance to a DOM element.
|
||||||
|
|
||||||
|
### The sample application
|
||||||
|
|
||||||
|
The sample form in this guide is used by the *Hero Employment Agency* to maintain personal information about heroes.
|
||||||
|
Every hero needs a job. This form helps the agency match the right hero with the right crisis.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
<img src="generated/images/guide/forms/hero-form-1.png" alt="Clean Form">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The *Hero Employment Agency* uses this form to maintain personal information about heroes.
|
The form highlights some design features that make it easier to use. For instance, the two required fields have a green bar on the left to make them easy to spot. These fields have initial values, so the form is valid and the **Submit** button is enabled.
|
||||||
Every hero needs a job. It's the company mission to match the right hero with the right crisis.
|
|
||||||
|
|
||||||
Two of the three fields on this form are required. Required fields have a green bar on the left to make them easy to spot.
|
As you work with this form, you will learn how to include validation logic, how to customize the presentation with standard CSS, and how to handle error conditions to ensure valid input.
|
||||||
|
If the user deletes the hero name, for example, the form becomes invalid. The app detects the changed status, and displays a validation error in an attention-grabbing style.
|
||||||
If you delete the hero name, the form displays a validation error in an attention-grabbing style:
|
In addition, the **Submit** button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
|
<img src="generated/images/guide/forms/hero-form-2.png" alt="Invalid, Name Required">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
### Step overview
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
In the course of this tutorial, you bind a sample form to data and handle user input using the following steps.
|
||||||
|
|
||||||
You can customize the colors and location of the "required" bar with standard CSS.
|
1. Build the basic form.
|
||||||
|
* Define a sample data model.
|
||||||
|
* Include required infrastructure such as the `FormsModule`.
|
||||||
|
2. Bind form controls to data properties using the `ngModel` directive and two-way data-binding syntax.
|
||||||
|
* Examine how `ngModel` reports control states using CSS classes.
|
||||||
|
* Name controls to make them accessible to `ngModel`.
|
||||||
|
3. Track input validity and control status using `ngModel`.
|
||||||
|
* Add custom CSS to provide visual feedback on the status.
|
||||||
|
* Show and hide validation-error messages.
|
||||||
|
4. Respond to a native HTML button-click event by adding to the model data.
|
||||||
|
5. Handle form submission using the [`ngSubmit`(api/forms/NgForm#properties)] output property of the form.
|
||||||
|
* Disable the **Submit** button until the form is valid.
|
||||||
|
* After submit, swap out the finished form for different content on the page.
|
||||||
|
|
||||||
</div>
|
{@a step1}
|
||||||
|
|
||||||
You'll build this form in small steps:
|
## Build the form
|
||||||
|
|
||||||
1. Create the `Hero` model class.
|
You can recreate the sample application from the code provided here, or you can examine or download the <live-example></live-example>.
|
||||||
1. Create the component that controls the form.
|
|
||||||
1. Create a template with the initial form layout.
|
|
||||||
1. Bind data properties to each form control using the `ngModel` two-way data-binding syntax.
|
|
||||||
1. Add a `name` attribute to each form-input control.
|
|
||||||
1. Add custom CSS to provide visual feedback.
|
|
||||||
1. Show and hide validation-error messages.
|
|
||||||
1. Handle form submission with *ngSubmit*.
|
|
||||||
1. Disable the form’s *Submit* button until the form is valid.
|
|
||||||
|
|
||||||
## Setup
|
1. The provided sample application creates the `Hero` class which defines the data model reflected in the form.
|
||||||
|
|
||||||
Create a new project named <code>angular-forms</code>:
|
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
2. The form layout and details are defined in the `HeroFormComponent` class.
|
||||||
|
|
||||||
ng new angular-forms
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1"></code-example>
|
||||||
|
|
||||||
</code-example>
|
The component's `selector` value of "app-hero-form" means you can drop this form in a parent
|
||||||
|
template using the `<app-hero-form>` tag.
|
||||||
|
|
||||||
## Create the Hero model class
|
3. The following code creates a new hero instance, so that the initial form can show an example hero.
|
||||||
|
|
||||||
As users enter form data, you'll capture their changes and update an instance of a model.
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="SkyDog"></code-example>
|
||||||
You can't lay out the form until you know what the model looks like.
|
|
||||||
|
|
||||||
A model can be as simple as a "property bag" that holds facts about a thing of application importance.
|
This demo uses dummy data for `model` and `powers`. In a real app, you would inject a data service to get and save real data, or expose these properties as inputs and outputs.
|
||||||
That describes well the `Hero` class with its three required fields (`id`, `name`, `power`)
|
|
||||||
and one optional field (`alterEgo`).
|
|
||||||
|
|
||||||
Using the Angular CLI command [`ng generate class`](cli/generate), generate a new class named `Hero`:
|
4. The application enables the Forms feature and registers the created form component.
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts"></code-example>
|
||||||
|
|
||||||
ng generate class Hero
|
5. The form is displayed in the application layout defined by the root component's template.
|
||||||
|
|
||||||
</code-example>
|
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
With this content:
|
The initial template defines the layout for a form with two form groups and a submit button.
|
||||||
|
The form groups correspond to two properties of the Hero data model, name and alterEgo. Each group has a label and a box for user input.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero.ts" header="src/app/hero.ts"></code-example>
|
* The **Name** `<input>` control element has the HTML5 `required` attribute.
|
||||||
|
* The **Alter Ego** `<input>` control element does not because `alterEgo` is optional.
|
||||||
|
|
||||||
It's an anemic model with few requirements and no behavior. Perfect for the demo.
|
The **Submit** button has some classes on it for styling.
|
||||||
|
At this point, the form layout is all plain HTML5, with no bindings or directives.
|
||||||
|
|
||||||
The TypeScript compiler generates a public field for each `public` constructor parameter and
|
6. The sample form uses some style classes from [Twitter Bootstrap](http://getbootstrap.com/css/): `container`, `form-group`, `form-control`, and `btn`.
|
||||||
automatically assigns the parameter’s value to that field when you create heroes.
|
To use these styles, the app's style sheet imports the library.
|
||||||
|
|
||||||
The `alterEgo` is optional, so the constructor lets you omit it; note the question mark (?) in `alterEgo?`.
|
<code-example path="forms/src/styles.1.css" header="src/styles.css"></code-example>
|
||||||
|
|
||||||
You can create a new hero like this:
|
7. The form makes the hero applicant choose one superpower from a fixed list of agency-approved powers.
|
||||||
|
The predefined list of `powers` is part of the data model, maintained internally in `HeroFormComponent`.
|
||||||
|
The Angular [NgForOf directive](api/common/NgForOf "API reference") iterates over the data values to populate the `<select>` element.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="SkyDog"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (powers)" region="powers"></code-example>
|
||||||
|
|
||||||
## Create a form component
|
If you run the app right now, you see the list of powers in the selection control. The input elements are not yet bound to data values or events, so they are still blank and have no behavior.
|
||||||
|
|
||||||
An Angular form has two parts: an HTML-based _template_ and a component _class_
|
|
||||||
to handle data and user interactions programmatically.
|
|
||||||
Begin with the class because it states, in brief, what the hero editor can do.
|
|
||||||
|
|
||||||
Using the Angular CLI command [`ng generate component`](cli/generate), generate a new component named `HeroForm`:
|
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
|
||||||
|
|
||||||
ng generate component HeroForm
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
With this content:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (v1)" region="v1"></code-example>
|
|
||||||
|
|
||||||
There’s nothing special about this component, nothing form-specific,
|
|
||||||
nothing to distinguish it from any component you've written before.
|
|
||||||
|
|
||||||
Understanding this component requires only the Angular concepts covered in previous pages.
|
|
||||||
|
|
||||||
* The code imports the Angular core library and the `Hero` model you just created.
|
|
||||||
* The `@Component` selector value of "app-hero-form" means you can drop this form in a parent
|
|
||||||
template with a `<app-hero-form>` tag.
|
|
||||||
* The `templateUrl` property points to a separate file for the template HTML.
|
|
||||||
* 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](guide/template-syntax#inputs-outputs) 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.
|
|
||||||
|
|
||||||
## Revise *app.module.ts*
|
|
||||||
|
|
||||||
`app.module.ts` defines the application's root module. In it you identify the external modules you'll use in the application
|
|
||||||
and declare the components that belong to this module, such as the `HeroFormComponent`.
|
|
||||||
|
|
||||||
Because template-driven forms are in their own module, you need to add the `FormsModule` to the array of
|
|
||||||
`imports` for the application module before you can use forms.
|
|
||||||
|
|
||||||
Update it with the following:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/app.module.ts" header="src/app/app.module.ts"></code-example>
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
There are two changes:
|
|
||||||
|
|
||||||
1. You import `FormsModule`.
|
|
||||||
|
|
||||||
1. You add the `FormsModule` to the list of `imports` defined in the `@NgModule` decorator. This gives the application
|
|
||||||
access to all of the template-driven forms features, including `ngModel`.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="alert is-important">
|
|
||||||
|
|
||||||
If a component, directive, or pipe belongs to a module in the `imports` array, _don't_ re-declare it in the `declarations` array.
|
|
||||||
If you wrote it and it should belong to this module, _do_ declare it in the `declarations` array.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Revise *app.component.html*
|
|
||||||
|
|
||||||
`AppComponent` is the application's root component. It will host the new `HeroFormComponent`.
|
|
||||||
|
|
||||||
Replace the contents of its template with the following:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/app.component.html" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
There are only two changes.
|
|
||||||
The `template` is simply the new element tag identified by the component's `selector` property.
|
|
||||||
This displays the hero form when the application component is loaded.
|
|
||||||
Don't forget to remove the `name` field from the class body as well.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
## Create an initial HTML form template
|
|
||||||
|
|
||||||
Update the template file with the following contents:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="start" header="src/app/hero-form/hero-form.component.html"></code-example>
|
|
||||||
|
|
||||||
The language is simply HTML5. You're presenting two of the `Hero` fields, `name` and `alterEgo`, and
|
|
||||||
opening them up for user input in input boxes.
|
|
||||||
|
|
||||||
The *Name* `<input>` control has the HTML5 `required` attribute;
|
|
||||||
the *Alter Ego* `<input>` control does not because `alterEgo` is optional.
|
|
||||||
|
|
||||||
You added a *Submit* button at the bottom with some classes on it for styling.
|
|
||||||
|
|
||||||
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
|
|
||||||
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The `container`, `form-group`, `form-control`, and `btn` classes
|
|
||||||
come from [Twitter Bootstrap](http://getbootstrap.com/css/). These classes are purely cosmetic.
|
|
||||||
Bootstrap gives the form a little style.
|
|
||||||
|
|
||||||
<div class="callout is-important">
|
|
||||||
|
|
||||||
<header>
|
|
||||||
Angular forms don't require a style library
|
|
||||||
</header>
|
|
||||||
|
|
||||||
Angular makes no use of the `container`, `form-group`, `form-control`, and `btn` classes or
|
|
||||||
the styles of any external library. Angular apps can use any CSS library or none at all.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
To add the stylesheet, open `styles.css` and add the following import line at the top:
|
|
||||||
|
|
||||||
<code-example path="forms/src/styles.1.css" header="src/styles.css"></code-example>
|
|
||||||
|
|
||||||
## Add powers with _*ngFor_
|
|
||||||
|
|
||||||
The hero must choose one superpower from a fixed list of agency-approved powers.
|
|
||||||
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](guide/displaying-data) page.
|
|
||||||
|
|
||||||
Add the following HTML *immediately below* the *Alter Ego* group:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (powers)" region="powers"></code-example>
|
|
||||||
|
|
||||||
This code repeats the `<option>` tag for each power in the list of powers.
|
|
||||||
The `pow` template input variable is a different power in each iteration;
|
|
||||||
you display its name using the interpolation syntax.
|
|
||||||
|
|
||||||
{@a ngModel}
|
|
||||||
|
|
||||||
## Two-way data binding with _ngModel_
|
|
||||||
|
|
||||||
Running the app right now would be disappointing.
|
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
|
<img src="generated/images/guide/forms/hero-form-3.png" alt="Early form with no binding">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{@a ngModel}
|
||||||
|
|
||||||
You don't see hero data because you're not binding to the `Hero` yet.
|
## Bind input controls to data properties
|
||||||
You know how to do that from earlier pages.
|
|
||||||
[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.
|
The next step is to bind the input controls to the corresponding `Hero` properties with two-way data binding, so that they respond to user input by updating the data model, and also respond to programmatic changes in the data by updating the display.
|
||||||
|
|
||||||
You could use the techniques you already know, but
|
The `ngModel` directive declared in the `FormsModule` lets you bind controls in your template-driven form to properties in your data model.
|
||||||
instead you'll use the new `[(ngModel)]` syntax, which
|
When you include the directive using the syntax for two-way data binding, `[(ngModel)]`, Angular can track the value and user interaction of the control and keep the view synced with the model.
|
||||||
makes binding the form to the model easy.
|
|
||||||
|
|
||||||
Find the `<input>` tag for *Name* and update it like this:
|
1. Edit the template file `hero-form.component.html`.
|
||||||
|
|
||||||
|
2. Find the `<input>` tag next to the **Name** label.
|
||||||
|
|
||||||
|
3. Add the `ngModel` directive, using two-way data binding syntax `[(ngModel)]="..."`.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-1"></code-example>
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
You added a diagnostic interpolation after the input tag
|
This example has a temporary diagnostic interpolation after each input tag, `{{model.name}}`, to show the current data value of the corresponding property.
|
||||||
so you can see what you're doing.
|
The note reminds you to remove the diagnostic lines when you have finished observing the two-way data binding at work.
|
||||||
You left yourself a note to throw it away when you're done.
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Focus on the binding syntax: `[(ngModel)]="..."`.
|
{@a ngForm}
|
||||||
|
|
||||||
You need one more addition to display the data. Declare
|
### Access the overall form status
|
||||||
a template variable for the form. Update the `<form>` tag with
|
|
||||||
`#heroForm="ngForm"` as follows:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable"></code-example>
|
When you imported the `FormsModule` in your component, Angular automatically created and attached an [NgForm](api/forms/NgForm "API reference for NgForm") directive to the `<form>` tag in the template (because `NgForm` has the selector `form` that matches `<form>` elements).
|
||||||
|
|
||||||
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
|
To get access to the `NgForm` and the overall form status, declare a [template reference variable](guide/template-syntax#template-reference-variables-var).
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
1. Edit the template file `hero-form.component.html`.
|
||||||
|
|
||||||
{@a ngForm}
|
2. Update the `<form>` tag with a template reference variable, `#heroForm`, and set its value as follows.
|
||||||
|
|
||||||
### The _NgForm_ directive
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="template-variable"></code-example>
|
||||||
|
|
||||||
What `NgForm` directive?
|
The `heroForm` template variable is now a reference to the `NgForm` directive instance that governs the form as a whole.
|
||||||
You didn't add an [NgForm](api/forms/NgForm) directive.
|
|
||||||
|
|
||||||
Angular did. Angular automatically creates and attaches an `NgForm` directive to the `<form>` tag.
|
3. Run the app.
|
||||||
|
|
||||||
The `NgForm` directive supplements the `form` element with additional features.
|
4. Start typing in the **Name** input box.
|
||||||
It holds the controls you created for the elements with an `ngModel` directive
|
|
||||||
and `name` attribute, and monitors their properties, including their validity.
|
|
||||||
It also has its own `valid` property which is true only *if every contained
|
|
||||||
control* is valid.
|
|
||||||
|
|
||||||
</div>
|
As you add and delete characters, you can see them appear and disappear from the data model.
|
||||||
|
For example:
|
||||||
|
|
||||||
If you ran the app now and started typing in the *Name* input box,
|
<div class="lightbox">
|
||||||
adding and deleting characters, you'd see them appear and disappear
|
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
|
||||||
from the interpolated text.
|
</div>
|
||||||
At some point it might look like this:
|
|
||||||
|
|
||||||
<div class="lightbox">
|
The diagnostic line that shows interpolated values demonstrates that values are really flowing from the input box to the model and back again.
|
||||||
<img src="generated/images/guide/forms/ng-model-in-action.png" alt="ngModel in action">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The diagnostic is evidence that values really are flowing from the input box to the model and
|
### Naming control elements
|
||||||
back again.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
When you use `[(ngModel)]` on an element, you must define a `name` attribute for that element.
|
||||||
|
Angular uses the assigned name to register the element with the `NgForm` directive attached to the parent `<form>` element.
|
||||||
|
|
||||||
That's *two-way data binding*.
|
The example added a `name` attribute to the `<input>` element and set it to "name",
|
||||||
For more information, see
|
which makes sense for the hero's name.
|
||||||
[Two-way binding with NgModel](guide/template-syntax#ngModel) on the
|
Any unique value will do, but using a descriptive name is helpful.
|
||||||
the [Template Syntax](guide/template-syntax) page.
|
|
||||||
|
|
||||||
</div>
|
1. Add similar `[(ngModel)]` bindings and `name` attributes to **Alter Ego** and **Hero Power**.
|
||||||
|
|
||||||
Notice that you also added a `name` attribute to the `<input>` tag and set it to "name",
|
2. You can now remove the diagnostic messages that show interpolated values.
|
||||||
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.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
3. To confirm that two-way data binding works for the entire hero model, add a new binding at the top to the component's `diagnostic` property.
|
||||||
|
|
||||||
Internally, Angular creates `FormControl` instances and
|
After these revisions, the form template should look like the following:
|
||||||
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 previous section, [The NgForm directive](guide/forms#ngForm).
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
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.
|
|
||||||
Then you can confirm that two-way data binding works *for the entire hero model*.
|
|
||||||
|
|
||||||
After revision, the core of the form should look like this:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModel-2"></code-example>
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
* Notice that each `<input>` element has an `id` property. This is used by the `<label>` element's `for` attribute to match the label to its input control. This is a [standard HTML feature](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/label).
|
||||||
|
|
||||||
* Each input element has an `id` property that is used by the `label` element's `for` attribute
|
* Each `<input>` element also has the required `name` property that Angular uses to register the control with the form.
|
||||||
to match the label to its input control.
|
|
||||||
* Each input element has a `name` property that is required by Angular forms to register the control with the form.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
If you run the app now and change every hero model property, the form might display like this:
|
If you run the app now and change every hero model property, the form might display like this:
|
||||||
|
|
||||||
@ -391,18 +236,15 @@ If you run the app now and change every hero model property, the form might disp
|
|||||||
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
|
<img src="generated/images/guide/forms/ng-model-in-action-2.png" alt="ngModel in action">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The diagnostic near the top of the form
|
The diagnostic near the top of the form confirms that all of your changes are reflected in the model.
|
||||||
confirms that all of your changes are reflected in the model.
|
|
||||||
|
|
||||||
*Delete* the `{{diagnostic}}` binding at the top as it has served its purpose.
|
4. When you have observed the effects, you can delete the `{{diagnostic}}` binding.
|
||||||
|
|
||||||
## Track control state and validity with _ngModel_
|
## Track control states
|
||||||
|
|
||||||
Using `ngModel` in a form gives you more than just two-way data binding. It also tells
|
The `NgModel` directive on a control tracks the state of that control.
|
||||||
you if the user touched the control, if the value changed, or if the value became invalid.
|
It tells you if the user touched the control, if the value changed, or if the value became invalid.
|
||||||
|
Angular sets special CSS classes on the control element to reflect the state, as shown in the following table.
|
||||||
The *NgModel* directive doesn't just track state; it updates the control with special Angular CSS classes that reflect the state.
|
|
||||||
You can leverage those class names to change the appearance of the control.
|
|
||||||
|
|
||||||
<table>
|
<table>
|
||||||
|
|
||||||
@ -472,38 +314,32 @@ You can leverage those class names to change the appearance of the control.
|
|||||||
|
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
Temporarily add a [template reference variable](guide/template-syntax#ref-vars) named `spy`
|
You use these CSS classes to define the styles for your control based on its status.
|
||||||
to the _Name_ `<input>` tag and use it to display the input's CSS classes.
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="ngModelName-2"></code-example>
|
### Observe control states
|
||||||
|
|
||||||
Now run the app and look at the _Name_ input box.
|
To see how the classes are added and removed by the framework, open the browser's developer tools and inspect the `<input>` element that represents the hero name.
|
||||||
Follow these steps *precisely*:
|
|
||||||
|
|
||||||
1. Look but don't touch.
|
1. Using your browser's developer tools, find the `<input>` element that corresponds to the **Name** input box.
|
||||||
1. Click inside the name box, then click outside it.
|
You can see that the element has multiple CSS classes in addition to "form-control".
|
||||||
1. Add slashes to the end of the name.
|
|
||||||
1. Erase the name.
|
|
||||||
|
|
||||||
The actions and effects are as follows:
|
2. When you first bring it up, the classes indicate that it has a valid value, that the value has not been changed since initialization or reset, and that the control has not been visited since initialization or reset.
|
||||||
|
|
||||||
<div class="lightbox">
|
```
|
||||||
<img src="generated/images/guide/forms/control-state-transitions-anim.gif" alt="Control State Transition">
|
<input ... class="form-control ng-untouched ng-pristine ng-valid" ...>
|
||||||
</div>
|
```
|
||||||
|
|
||||||
You should see the following transitions and class names:
|
3. Take the following actions on the **Name** `<input>` box, and observe which classes appear.
|
||||||
|
* Look but don't touch. The classes indicate that it is untouched, pristine, and valid.
|
||||||
|
* Click inside the name box, then click outside it. The control has now been visited, and the element has the `ng-touched` class instead of the `ng-untouched` class.
|
||||||
|
* Add slashes to the end of the name. It is now touched and dirty.
|
||||||
|
* Erase the name. This makes the value invalid, so the `ng-invalid` class replaces the `ng-valid` class.
|
||||||
|
|
||||||
<div class="lightbox">
|
### Create visual feedback for states
|
||||||
<img src="generated/images/guide/forms/ng-control-class-changes.png" alt="Control state transitions">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The `ng-valid`/`ng-invalid` pair is the most interesting, because you want to send a
|
The `ng-valid`/`ng-invalid` pair is particularly interesting, because you want to send a
|
||||||
strong visual signal when the values are invalid. You also want to mark required fields.
|
strong visual signal when the values are invalid.
|
||||||
To create such visual feedback, add definitions for the `ng-*` CSS classes.
|
You also want to mark required fields.
|
||||||
|
|
||||||
*Delete* the `#spy` template reference variable and the `TODO` as they have served their purpose.
|
|
||||||
|
|
||||||
## Add custom CSS for visual feedback
|
|
||||||
|
|
||||||
You can mark required fields and invalid data at the same time with a colored bar
|
You can mark required fields and invalid data at the same time with a colored bar
|
||||||
on the left of the input box:
|
on the left of the input box:
|
||||||
@ -512,20 +348,25 @@ on the left of the input box:
|
|||||||
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
|
<img src="generated/images/guide/forms/validity-required-indicator.png" alt="Invalid Form">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
You achieve this effect by adding these class definitions to a new `forms.css` file
|
To change the appearance in this way, take the following steps.
|
||||||
that you add to the project as a sibling to `index.html`:
|
|
||||||
|
|
||||||
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css"></code-example>
|
1. Add definitions for the `ng-*` CSS classes.
|
||||||
|
|
||||||
Update the `<head>` of `index.html` to include this style sheet:
|
2. Add these class definitions to a new `forms.css` file.
|
||||||
|
|
||||||
<code-example path="forms/src/index.html" header="src/index.html (styles)" region="styles"></code-example>
|
3. Add the new file to the project as a sibling to `index.html`:
|
||||||
|
|
||||||
## Show and hide validation error messages
|
<code-example path="forms/src/assets/forms.css" header="src/assets/forms.css"></code-example>
|
||||||
|
|
||||||
You can improve the form. The _Name_ input box is required and clearing it turns the bar red.
|
4. In the `index.html` file, update the `<head>` tag to include the new style sheet.
|
||||||
That says something is wrong but the user doesn't know *what* is wrong or what to do about it.
|
|
||||||
Leverage the control's state to reveal a helpful message.
|
<code-example path="forms/src/index.html" header="src/index.html (styles)" region="styles"></code-example>
|
||||||
|
|
||||||
|
### Show and hide validation error messages
|
||||||
|
|
||||||
|
The **Name** input box is required and clearing it turns the bar red.
|
||||||
|
That indicates that something is wrong, but the user doesn't know what is wrong or what to do about it.
|
||||||
|
You can provide a helpful message by checking for and responding to the control's state.
|
||||||
|
|
||||||
When the user deletes the name, the form should look like this:
|
When the user deletes the name, the form should look like this:
|
||||||
|
|
||||||
@ -533,166 +374,135 @@ When the user deletes the name, the form should look like this:
|
|||||||
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
|
<img src="generated/images/guide/forms/name-required-error.png" alt="Name required">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
To achieve this effect, extend the `<input>` tag with the following:
|
The **Hero Power** select box is also required, but it doesn't need this kind of error handling because the selection box already constrains the selection to valid values.
|
||||||
|
|
||||||
* A [template reference variable](guide/template-syntax#ref-vars).
|
To define and show an error message when appropriate, take the following steps.
|
||||||
* 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:
|
1. Extend the `<input>` tag with a template reference variable that you can use to access the input box's Angular control from within the template. In the example, the variable is `#name="ngModel"`.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg"></code-example>
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
You need a template reference variable to access the input box's Angular control from within the template.
|
The template reference variable (`#name`) is set to `"ngModel"` because that is the value of the [`NgModel.exportAs`](api/core/Directive#exportAs) property. This property tells Angular how to link a reference variable to a directive.
|
||||||
Here you created a variable called `name` and gave it the value "ngModel".
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
</div>
|
||||||
|
|
||||||
Why "ngModel"?
|
2. Add a `<div>` that contains a suitable error message.
|
||||||
A directive's [exportAs](api/core/Directive) property
|
3. Show or hide the error message by binding properties of the `name`
|
||||||
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".
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
You control visibility of the name error message by binding properties of the `name`
|
|
||||||
control to the message `<div>` element's `hidden` property.
|
control to the message `<div>` element's `hidden` property.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (hidden-error-msg)" region="hidden-error-msg"></code-example>
|
||||||
|
|
||||||
In this example, you hide the message when the control is valid or pristine;
|
4. Add a conditional error message to the _name_ input box, as in the following example.
|
||||||
"pristine" means the user hasn't changed the value since it was displayed in this form.
|
|
||||||
|
|
||||||
This user experience is the developer's choice. Some developers want the message to display at all times.
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="name-with-error-msg"></code-example>
|
||||||
|
|
||||||
|
<div class="callout is-helpful">
|
||||||
|
|
||||||
|
<header>Illustrating the "pristine" state</header>
|
||||||
|
|
||||||
|
In this example, you hide the message when the control is either valid or *pristine*.
|
||||||
|
Pristine means the user hasn't changed the value since it was displayed in this form.
|
||||||
If you ignore the `pristine` state, you would hide the message only when the value is valid.
|
If you ignore the `pristine` state, you would hide the message only when the value is valid.
|
||||||
If you arrive in this component with a new (blank) hero or an invalid hero,
|
If you arrive in this component with a new (blank) hero or an invalid hero,
|
||||||
you'll see the error message immediately, before you've done anything.
|
you'll see the error message immediately, before you've done anything.
|
||||||
|
|
||||||
Some developers want the message to display only when the user makes an invalid change.
|
You might want the message to display only when the user makes an invalid change.
|
||||||
Hiding the message while the control is "pristine" achieves that goal.
|
Hiding the message while the control is in the `pristine` state achieves that goal.
|
||||||
You'll see the significance of this choice when you add a new hero to the form.
|
You'll see the significance of this choice when you add a new hero to the form in the next step.
|
||||||
|
|
||||||
The hero *Alter Ego* is optional so you can leave that be.
|
</div>
|
||||||
|
|
||||||
Hero *Power* selection is required.
|
## Add a new hero
|
||||||
You can add the same kind of error handling to the `<select>` if you want,
|
|
||||||
but it's not imperative because the selection box already constrains the
|
|
||||||
power to valid values.
|
|
||||||
|
|
||||||
Now you'll add a new hero in this form.
|
This exercise shows how you can respond to a native HTML button-click event by adding to the model data.
|
||||||
Place a *New Hero* button at the bottom of the form and bind its click event to a `newHero` component method.
|
To let form users add a new hero, you will add a **New Hero** button that responds to a click event.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)"></code-example>
|
1. In the template, place a "New Hero" `<button>` element at the bottom of the form.
|
||||||
|
2. In the component file, add the hero-creation method to the hero data model.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" region="new-hero" header="src/app/hero-form/hero-form.component.ts (New Hero method)"></code-example>
|
||||||
|
|
||||||
Run the application again, click the *New Hero* button, and the form clears.
|
3. Bind the button's click event to a hero-creation method, `newHero()`.
|
||||||
The *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
|
|
||||||
That's understandable as these are required fields.
|
|
||||||
The error messages are hidden because the form is pristine; you haven't changed anything yet.
|
|
||||||
|
|
||||||
Enter a name and click *New Hero* again.
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-no-reset" header="src/app/hero-form/hero-form.component.html (New Hero button)"></code-example>
|
||||||
The app displays a _Name is required_ error message.
|
|
||||||
You don't want error messages when you create a new (empty) hero.
|
|
||||||
Why are you getting one now?
|
|
||||||
|
|
||||||
Inspecting the element in the browser tools reveals that the *name* input box is _no longer pristine_.
|
4. Run the application again and click the **New Hero** button.
|
||||||
The form remembers that you entered a name before clicking *New Hero*.
|
|
||||||
Replacing the hero object *did not restore the pristine state* of the form controls.
|
|
||||||
|
|
||||||
You have to clear all of the flags imperatively, which you can do
|
The form clears, and the *required* bars to the left of the input box are red, indicating invalid `name` and `power` properties.
|
||||||
by calling the form's `reset()` method after calling the `newHero()` method.
|
Notice that the error messages are hidden. This is because the form is pristine; you haven't changed anything yet.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)"></code-example>
|
5. Enter a name and click **New Hero** again.
|
||||||
|
|
||||||
Now clicking "New Hero" resets both the form and its control flags.
|
Now the app displays a _Name is required_ error message, because the input box is no longer pristine.
|
||||||
|
The form remembers that you entered a name before clicking **New Hero**.
|
||||||
|
|
||||||
|
6. To restore the pristine state of the form controls, clear all of the flags imperatively by calling the form's `reset()` method after calling the `newHero()` method.
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" region="new-hero-button-form-reset" header="src/app/hero-form/hero-form.component.html (Reset the form)"></code-example>
|
||||||
|
|
||||||
|
Now clicking **New Hero** resets both the form and its control flags.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
See the [User Input](guide/user-input) guide for more information about listening for DOM events with an event binding and updating a corresponding component property.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
## Submit the form with _ngSubmit_
|
## Submit the form with _ngSubmit_
|
||||||
|
|
||||||
The user should be able to submit this form after filling it in.
|
The user should be able to submit this form after filling it in.
|
||||||
The *Submit* button at the bottom of the form
|
The **Submit** button at the bottom of the form does nothing on its own, but it does
|
||||||
does nothing on its own, but it will
|
trigger a form-submit event because of its type (`type="submit"`).
|
||||||
trigger a form submit because of its type (`type="submit"`).
|
To respond to this event, take the following steps.
|
||||||
|
|
||||||
A "form submit" is useless at the moment.
|
1. Bind the form's [`ngSubmit`](api/forms/NgForm#properties) event property to the hero-form component's `onSubmit()` method.
|
||||||
To make it useful, bind the form's `ngSubmit` event property
|
|
||||||
to the hero form component's `onSubmit()` method:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (ngSubmit)" region="ngSubmit"></code-example>
|
||||||
|
|
||||||
You'd already defined a template reference variable,
|
2. Use the template reference variable, `#heroForm` to access the form that contains the **Submit** button and create an event binding.
|
||||||
`#heroForm`, and initialized it with the value "ngForm".
|
You will bind the form property that indicates its overall validity to the **Submit** button's `disabled` property.
|
||||||
Now, use that variable to access the form with the Submit button.
|
|
||||||
|
|
||||||
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button"></code-example>
|
||||||
|
|
||||||
You'll bind the form's overall validity via
|
3. Run the application now. Notice that the button is enabled—although
|
||||||
the `heroForm` variable to the button's `disabled` property
|
|
||||||
using an event binding. Here's the code:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (submit-button)" region="submit-button"></code-example>
|
|
||||||
|
|
||||||
If you run the application now, you find that the button is enabled—although
|
|
||||||
it doesn't do anything useful yet.
|
it doesn't do anything useful yet.
|
||||||
|
|
||||||
Now if you delete the Name, you violate the "required" rule, which
|
4. Delete the **Name** value. This violates the "required" rule, so it displays the error message&emdash;and notice that it also disables the **Submit** button.
|
||||||
is duly noted in the error message.
|
|
||||||
The *Submit* button is also disabled.
|
|
||||||
|
|
||||||
Not impressed? Think about it for a moment. What would you have to do to
|
|
||||||
wire the button's enable/disabled state to the form's validity without Angular's help?
|
|
||||||
|
|
||||||
For you, it was as simple as this:
|
You didn't have to explicitly wire the button's enabled state to the form's validity.
|
||||||
|
The `FormsModule` did this automatically when you defined a template reference variable on the enhanced form element, then referred to that variable in the button control.
|
||||||
|
|
||||||
1. Define a template reference variable on the (enhanced) form element.
|
### Respond to form submission
|
||||||
2. Refer to that variable in a button many lines away.
|
|
||||||
|
|
||||||
## Toggle two form regions (extra credit)
|
To show a response to form submission, you can hide the data entry area and display something else in its place.
|
||||||
|
|
||||||
Submitting the form isn't terribly dramatic at the moment.
|
1. Wrap the entire form in a `<div>` and bind
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
An unsurprising observation for a demo. To be honest,
|
|
||||||
jazzing it up won't teach you anything new about forms.
|
|
||||||
But this is an opportunity to exercise some of your newly won
|
|
||||||
binding skills.
|
|
||||||
If you aren't interested, skip to this page's conclusion.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
For a more strikingly visual effect,
|
|
||||||
hide the data entry area and display something else.
|
|
||||||
|
|
||||||
Wrap the form in a `<div>` and bind
|
|
||||||
its `hidden` property to the `HeroFormComponent.submitted` property.
|
its `hidden` property to the `HeroFormComponent.submitted` property.
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="edit-div"></code-example>
|
||||||
|
|
||||||
The main form is visible from the start because the
|
* The main form is visible from the start because the `submitted` property is false until you submit the form, as this fragment from the `HeroFormComponent` shows:
|
||||||
`submitted` property is false until you submit the form,
|
|
||||||
as this fragment from the `HeroFormComponent` shows:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.ts" header="src/app/hero-form/hero-form.component.ts (submitted)" region="submitted"></code-example>
|
||||||
|
|
||||||
When you click the *Submit* button, the `submitted` flag becomes true and the form disappears
|
* When you click the **Submit** button, the `submitted` flag becomes true and the form disappears.
|
||||||
as planned.
|
|
||||||
|
|
||||||
Now the app needs to show something else while the form is in the submitted state.
|
2. To show something else while the form is in the submitted state, add the following HTML below the new `<div>` wrapper.
|
||||||
Add the following HTML below the `<div>` wrapper you just wrote:
|
|
||||||
|
|
||||||
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted"></code-example>
|
<code-example path="forms/src/app/hero-form/hero-form.component.html" header="src/app/hero-form/hero-form.component.html (excerpt)" region="submitted"></code-example>
|
||||||
|
|
||||||
There's the hero again, displayed read-only with interpolation bindings.
|
This `<div>`, which shows a read-only hero with interpolation bindings, appears only while the component is in the submitted state.
|
||||||
This `<div>` appears only while the component is in the submitted state.
|
|
||||||
|
|
||||||
The HTML includes an *Edit* button whose click event is bound to an expression
|
The alternative display includes an *Edit* button whose click event is bound to an expression
|
||||||
that clears the `submitted` flag.
|
that clears the `submitted` flag.
|
||||||
|
|
||||||
When you click the *Edit* button, this block disappears and the editable form reappears.
|
3. Click the *Edit* button to switch the display back to the editable form.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
|
||||||
The Angular form discussed in this page takes advantage of the following
|
The Angular form discussed in this page takes advantage of the following
|
||||||
framework features to provide support for data modification, validation, and more:
|
framework features to provide support for data modification, validation, and more.
|
||||||
|
|
||||||
* An Angular HTML form template.
|
* An Angular HTML form template.
|
||||||
* A form component class with a `@Component` decorator.
|
* A form component class with a `@Component` decorator.
|
||||||
@ -700,8 +510,8 @@ framework features to provide support for data modification, validation, and mor
|
|||||||
* Template-reference variables such as `#heroForm` and `#name`.
|
* Template-reference variables such as `#heroForm` and `#name`.
|
||||||
* `[(ngModel)]` syntax for two-way data binding.
|
* `[(ngModel)]` syntax for two-way data binding.
|
||||||
* The use of `name` attributes for validation and form-element change tracking.
|
* The use of `name` attributes for validation and form-element change tracking.
|
||||||
* The reference variable’s `valid` property on input controls to check if a control is valid and show/hide error messages.
|
* The reference variable’s `valid` property on input controls to check if a control is valid and show or hide error messages.
|
||||||
* Controlling the *Submit* button's enabled state by binding to `NgForm` validity.
|
* Controlling the **Submit** button's enabled state by binding to `NgForm` validity.
|
||||||
* Custom CSS classes that provide visual feedback to users about invalid controls.
|
* Custom CSS classes that provide visual feedback to users about invalid controls.
|
||||||
|
|
||||||
Here’s the code for the final version of the application:
|
Here’s the code for the final version of the application:
|
||||||
@ -741,4 +551,3 @@ Here’s the code for the final version of the application:
|
|||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
|
@ -214,6 +214,13 @@ Read more about component classes, templates, and views in [Introduction to Angu
|
|||||||
|
|
||||||
See [workspace configuration](#cli-config)
|
See [workspace configuration](#cli-config)
|
||||||
|
|
||||||
|
{@a content-projection}
|
||||||
|
|
||||||
|
## content projection
|
||||||
|
|
||||||
|
A way to insert DOM content from outside a component into the component's view in a designated spot.
|
||||||
|
|
||||||
|
For more information, see [Responding to changes in content](guide/lifecycle-hooks#content-projection).
|
||||||
|
|
||||||
{@a custom-element}
|
{@a custom-element}
|
||||||
|
|
||||||
@ -594,7 +601,7 @@ Compare to [NgModule](#ngmodule).
|
|||||||
## ngcc
|
## ngcc
|
||||||
|
|
||||||
Angular compatibility compiler.
|
Angular compatibility compiler.
|
||||||
If you build your app using [Ivy](#ivy), but it depends on libraries have not been compiled with Ivy, the CLI uses `ngcc` to automatically update the dependent libraries to use Ivy.
|
If you build your app using [Ivy](#ivy), but it depends on libraries that have not been compiled with Ivy, the CLI uses `ngcc` to automatically update the dependent libraries to use Ivy.
|
||||||
|
|
||||||
|
|
||||||
{@a ngmodule}
|
{@a ngmodule}
|
||||||
@ -943,9 +950,26 @@ Many code editors and IDEs support TypeScript either natively or with plug-ins.
|
|||||||
TypeScript is the preferred language for Angular development.
|
TypeScript is the preferred language for Angular development.
|
||||||
Read more about TypeScript at [typescriptlang.org](http://www.typescriptlang.org/).
|
Read more about TypeScript at [typescriptlang.org](http://www.typescriptlang.org/).
|
||||||
|
|
||||||
|
## TypeScript configuration file
|
||||||
|
|
||||||
|
A file specifies the root files and the compiler options required to compile a TypeScript project. For more information, see [TypeScript configuration](/guide/typescript-configuration).
|
||||||
|
|
||||||
|
|
||||||
{@a U}
|
{@a U}
|
||||||
|
|
||||||
|
{@a unidirectional-data-flow}
|
||||||
|
|
||||||
|
## unidirectional data flow
|
||||||
|
|
||||||
|
A data flow model where the component tree is always checked for changes in one direction (parent to child), which prevents cycles in the change detection graph.
|
||||||
|
|
||||||
|
In practice, this means that data in Angular flows downward during change detection.
|
||||||
|
A parent component can easily change values in its child components because the parent is checked first.
|
||||||
|
A failure could occur, however, if a child component tries to change a value in its parent during change detection (inverting the expected data flow), because the parent component has already been rendered.
|
||||||
|
In development mode, Angular throws the `ExpressionChangedAfterItHasBeenCheckedError` error if your app attempts to do this, rather than silently failing to render the new value.
|
||||||
|
|
||||||
|
To avoid this error, a [lifecycle hook](guide/lifecycle-hooks) method that seeks to make such a change should trigger a new change detection run. The new run follows the same direction as before, but succeeds in picking up the new value.
|
||||||
|
|
||||||
{@a universal}
|
{@a universal}
|
||||||
|
|
||||||
## Universal
|
## Universal
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# Communicating with backend services using HTTP
|
# Communicating with backend services using HTTP
|
||||||
|
|
||||||
Most front-end applications need to communicate with a server over the HTTP protocol, in order to download or upload data and accesss other back-end services.
|
Most front-end applications need to communicate with a server over the HTTP protocol, in order to download or upload data and access other back-end services.
|
||||||
Angular provides a simplified client HTTP API for Angular applications, the `HttpClient` service class in `@angular/common/http`.
|
Angular provides a simplified client HTTP API for Angular applications, the `HttpClient` service class in `@angular/common/http`.
|
||||||
|
|
||||||
The HTTP client service offers the following major features.
|
The HTTP client service offers the following major features.
|
||||||
@ -63,7 +63,7 @@ Look at the `AppModule` _imports_ to see how it is configured.
|
|||||||
## Requesting data from a server
|
## Requesting data from a server
|
||||||
|
|
||||||
Use the [`HTTPClient.get()`](api/common/http/HttpClient#get) method to fetch data from a server.
|
Use the [`HTTPClient.get()`](api/common/http/HttpClient#get) method to fetch data from a server.
|
||||||
The aynchronous method sends an HTTP request, and returns an Observable that emits the requested data when the response is received.
|
The asynchronous method sends an HTTP request, and returns an Observable that emits the requested data when the response is received.
|
||||||
The return type varies based on the `observe` and `responseType` values that you pass to the call.
|
The return type varies based on the `observe` and `responseType` values that you pass to the call.
|
||||||
|
|
||||||
The `get()` method takes two arguments; the endpoint URL from which to fetch, and an *options* object that you can use to configure the request.
|
The `get()` method takes two arguments; the endpoint URL from which to fetch, and an *options* object that you can use to configure the request.
|
||||||
@ -805,16 +805,16 @@ The `CachingInterceptor` in the following example demonstrates this approach.
|
|||||||
header="app/http-interceptors/caching-interceptor.ts)">
|
header="app/http-interceptors/caching-interceptor.ts)">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
* The `isCachable()` function determines if the request is cachable.
|
* The `isCacheable()` function determines if the request is cacheable.
|
||||||
In this sample, only GET requests to the npm package search api are cachable.
|
In this sample, only GET requests to the npm package search api are cacheable.
|
||||||
|
|
||||||
* If the request is not cachable, the interceptor simply forwards the request
|
* If the request is not cacheable, the interceptor simply forwards the request
|
||||||
to the next handler in the chain.
|
to the next handler in the chain.
|
||||||
|
|
||||||
* If a cachable request is found in the cache, the interceptor returns an `of()` _observable_ with
|
* If a cacheable request is found in the cache, the interceptor returns an `of()` _observable_ with
|
||||||
the cached response, by-passing the `next` handler (and all other interceptors downstream).
|
the cached response, by-passing the `next` handler (and all other interceptors downstream).
|
||||||
|
|
||||||
* If a cachable request is not in cache, the code calls `sendRequest()`.
|
* If a cacheable request is not in cache, the code calls `sendRequest()`.
|
||||||
This function creates a [request clone](#immutability) without headers, because the npm API forbids them.
|
This function creates a [request clone](#immutability) without headers, because the npm API forbids them.
|
||||||
The function then forwards the clone of the request to `next.handle()` which ultimately calls the server and returns the server's response.
|
The function then forwards the clone of the request to `next.handle()` which ultimately calls the server and returns the server's response.
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -292,3 +292,59 @@ To fix this problem, we recommend binding to the `selected` property on the `<op
|
|||||||
<option>
|
<option>
|
||||||
</select>
|
</select>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
{@a forward-refs-directive-inputs}
|
||||||
|
## Forward references to directive inputs accessed through local refs are no longer supported.
|
||||||
|
|
||||||
|
|
||||||
|
### Basic example of change
|
||||||
|
|
||||||
|
|
||||||
|
```ts
|
||||||
|
@Directive({
|
||||||
|
selector: '[myDir]',
|
||||||
|
exportAs: 'myDir'
|
||||||
|
})
|
||||||
|
export class MyDir {
|
||||||
|
@Input() message: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```html
|
||||||
|
{{ myDir.name }}
|
||||||
|
<div myDir #myDir="myDir" [name]="myName"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
In the View Engine runtime, the above code would print out the name without any errors.
|
||||||
|
In Ivy, the `myDir.name` binding will throw an `ExpressionChangedAfterItHasBeenCheckedError`.
|
||||||
|
|
||||||
|
|
||||||
|
### Background
|
||||||
|
|
||||||
|
In the ViewEngine runtime, directive input bindings and element bindings were executed in different stages. Angular would process the template one full time to check directive inputs only (e.g. `[name]`), then process the whole template again to check element and text bindings only (e.g.`{{ myDir.name }}`). This meant that the `name` directive input would be checked before the `myDir.name` text binding despite their relative order in the template, which some users felt to be counterintuitive.
|
||||||
|
|
||||||
|
In contrast, Ivy processes the template in just one pass, so that bindings are checked in the same order that they are written in the template. In this case, it means that the `myDir.name` binding will be checked before the `name` input sets the property on the directive (and thus it will be `undefined`). Since the `myDir.name` property will be set by the time the next change detection pass runs, a change detection error is thrown.
|
||||||
|
|
||||||
|
### Example of error
|
||||||
|
|
||||||
|
Assuming that the value for `myName` is `Angular`, you should see an error that looks like
|
||||||
|
|
||||||
|
```
|
||||||
|
Error: ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'undefined'. Current value: 'Angular'.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Recommended fix
|
||||||
|
|
||||||
|
To fix this problem, we recommend either getting the information for the binding directly from the host component (e.g. the `myName` property from our example) or to move the data binding after the directive has been declared so that the initial value is available on the first pass.
|
||||||
|
|
||||||
|
*Before*
|
||||||
|
```html
|
||||||
|
{{ myDir.name }}
|
||||||
|
<div myDir #myDir="myDir" [name]="myName"></div>
|
||||||
|
```
|
||||||
|
|
||||||
|
*After*
|
||||||
|
```html
|
||||||
|
{{ myName }}
|
||||||
|
<div myDir [name]="myName"></div>
|
||||||
|
```
|
||||||
|
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
The Angular team has worked hard to ensure Ivy is as backwards-compatible with the previous rendering engine ("View Engine") as possible.
|
The Angular team has worked hard to ensure Ivy is as backwards-compatible with the previous rendering engine ("View Engine") as possible.
|
||||||
However, in rare cases, minor changes were necessary to ensure that the Angular's behavior was predictable and consistent, correcting issues in the View Engine implementation.
|
However, in rare cases, minor changes were necessary to ensure that the Angular's behavior was predictable and consistent, correcting issues in the View Engine implementation.
|
||||||
In order to smooth the transition, we have provided [automated migrations](guide/updating-to-version-9#migrations) wherever possible so your application and library code is migrated automatically by the CLI.
|
In order to smooth the transition, we have provided [automated migrations](guide/updating-to-version-10#migrations) wherever possible so your application and library code is migrated automatically by the CLI.
|
||||||
That said, some applications will likely need to apply some manual updates.
|
That said, some applications will likely need to apply some manual updates.
|
||||||
|
|
||||||
{@a debugging}
|
{@a debugging}
|
||||||
## How to debug errors with Ivy
|
## How to debug errors with Ivy
|
||||||
|
|
||||||
In version 9, [a few deprecated APIs have been removed](guide/updating-to-version-9#removals) and there are a [few breaking changes](guide/updating-to-version-9#breaking-changes) unrelated to Ivy.
|
In version 10, [a few deprecated APIs have been removed](guide/updating-to-version-10#removals) and there are a [few breaking changes](guide/updating-to-version-10#breaking-changes) unrelated to Ivy.
|
||||||
If you're seeing errors after updating to version 9, you'll first want to rule those changes out.
|
If you're seeing errors after updating to version 9, you'll first want to rule those changes out.
|
||||||
|
|
||||||
To do so, temporarily [turn off Ivy](guide/ivy#opting-out-of-angular-ivy) in your `tsconfig.json` and re-start your app.
|
To do so, temporarily [turn off Ivy](guide/ivy#opting-out-of-angular-ivy) in your `tsconfig.base.json` and re-start your app.
|
||||||
|
|
||||||
If you're still seeing the errors, they are not specific to Ivy. In this case, you may want to consult the [general version 9 guide](guide/updating-to-version-9). If you've opted into any of the stricter type-checking settings that are new with v9, you may also want to check out the [template type-checking guide](guide/template-typecheck).
|
If you're still seeing the errors, they are not specific to Ivy. In this case, you may want to consult the [general version 10 guide](guide/updating-to-version-10). If you've opted into any of the new, stricter type-checking settings, you may also want to check out the [template type-checking guide](guide/template-typecheck).
|
||||||
|
|
||||||
If the errors are gone, switch back to Ivy by removing the changes to the `tsconfig.json` and review the list of expected changes below.
|
If the errors are gone, switch back to Ivy by removing the changes to the `tsconfig.base.json` and review the list of expected changes below.
|
||||||
|
|
||||||
{@a payload-size-debugging}
|
{@a payload-size-debugging}
|
||||||
### Payload size debugging
|
### Payload size debugging
|
||||||
@ -63,7 +63,7 @@ Please note that these constants are not meant to be used by 3rd party library o
|
|||||||
|
|
||||||
* Foreign functions or foreign constants in decorator metadata aren't statically resolvable (previously, you could import a constant or function from another compilation unit, like a library, and use that constant/function in your `@NgModule` definition).
|
* Foreign functions or foreign constants in decorator metadata aren't statically resolvable (previously, you could import a constant or function from another compilation unit, like a library, and use that constant/function in your `@NgModule` definition).
|
||||||
|
|
||||||
* Forward references to directive inputs accessed through local refs are no longer supported by default.
|
* Forward references to directive inputs accessed through local refs are no longer supported by default. [details](guide/ivy-compatibility-examples#forward-refs-directive-inputs)
|
||||||
|
|
||||||
* If there is both an unbound class attribute and a `[class]` binding, the classes in the unbound attribute will also be added (previously, the class binding would overwrite classes in the unbound attribute).
|
* If there is both an unbound class attribute and a `[class]` binding, the classes in the unbound attribute will also be added (previously, the class binding would overwrite classes in the unbound attribute).
|
||||||
|
|
||||||
|
@ -1,19 +1,61 @@
|
|||||||
# Lazy-loading feature modules
|
# Lazy-loading feature modules
|
||||||
|
|
||||||
## High level view
|
|
||||||
|
|
||||||
By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading—a design pattern that loads NgModules as needed. Lazy loading helps keep initial
|
By default, NgModules are eagerly loaded, which means that as soon as the app loads, so do all the NgModules, whether or not they are immediately necessary. For large apps with lots of routes, consider lazy loading—a design pattern that loads NgModules as needed. Lazy loading helps keep initial
|
||||||
bundle sizes smaller, which in turn helps decrease load times.
|
bundle sizes smaller, which in turn helps decrease load times.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
For the final sample app with two lazy-loaded modules that this page describes, see the
|
For the final sample app with two lazy-loaded modules that this page describes, see the
|
||||||
<live-example></live-example>.
|
<live-example></live-example>.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{@a lazy-loading}
|
||||||
|
|
||||||
|
## Lazy loading basics
|
||||||
|
|
||||||
|
This section introduces the basic procedure for configuring a lazy-loaded route.
|
||||||
|
For a step-by-step example, see the [step-by-step setup](#step-by-step) section on this page.
|
||||||
|
|
||||||
|
To lazy load Angular modules, use `loadchildren` (instead of `component`) in your `AppRoutingModule` `routes` configuration as follows.
|
||||||
|
|
||||||
|
<code-example header="AppRoutingModule (excerpt)">
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: 'items',
|
||||||
|
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
In the lazy-loaded module's routing module, add a route for the component.
|
||||||
|
|
||||||
|
<code-example header="Routing module for lazy loaded module (excerpt)">
|
||||||
|
|
||||||
|
const routes: Routes = [
|
||||||
|
{
|
||||||
|
path: '',
|
||||||
|
component: ItemsComponent
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Also be sure to remove the `ItemsModule` from the `AppModule`.
|
||||||
|
For step-by-step instructions on lazy loading modules, continue with the following sections of this page.
|
||||||
|
|
||||||
|
{@a step-by-step}
|
||||||
|
|
||||||
|
## Step-by-step setup
|
||||||
|
|
||||||
There are two main steps to setting up a lazy-loaded feature module:
|
There are two main steps to setting up a lazy-loaded feature module:
|
||||||
|
|
||||||
1. Create the feature module with the CLI, using the `--route` flag.
|
1. Create the feature module with the CLI, using the `--route` flag.
|
||||||
1. Configure the routes.
|
1. Configure the routes.
|
||||||
|
|
||||||
## Set up an app
|
### Set up an app
|
||||||
|
|
||||||
If you don’t already have an app, you can follow the steps below to
|
If you don’t already have an app, you can follow the steps below to
|
||||||
create one with the CLI. If you already have an app, skip to
|
create one with the CLI. If you already have an app, skip to
|
||||||
@ -36,7 +78,7 @@ See [Keeping Up to Date](guide/updating).
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
## Create a feature module with routing
|
### Create a feature module with routing
|
||||||
|
|
||||||
Next, you’ll need a feature module with a component to route to.
|
Next, you’ll need a feature module with a component to route to.
|
||||||
To make one, enter the following command in the terminal, where `customers` is the name of the feature module. The path for loading the `customers` feature modules is also `customers` because it is specified with the `--route` option:
|
To make one, enter the following command in the terminal, where `customers` is the name of the feature module. The path for loading the `customers` feature modules is also `customers` because it is specified with the `--route` option:
|
||||||
@ -59,7 +101,7 @@ Instead, it adds the declared route, `customers` to the `routes` array declared
|
|||||||
Notice that the lazy-loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports.
|
Notice that the lazy-loading syntax uses `loadChildren` followed by a function that uses the browser's built-in `import('...')` syntax for dynamic imports.
|
||||||
The import path is the relative path to the module.
|
The import path is the relative path to the module.
|
||||||
|
|
||||||
### Add another feature module
|
#### Add another feature module
|
||||||
|
|
||||||
Use the same command to create a second lazy-loaded feature module with routing, along with its stub component.
|
Use the same command to create a second lazy-loaded feature module with routing, along with its stub component.
|
||||||
|
|
||||||
@ -76,16 +118,14 @@ The `orders` route, specified with the `--route` option, is added to the `routes
|
|||||||
region="routes-customers-orders">
|
region="routes-customers-orders">
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
## Set up the UI
|
### Set up the UI
|
||||||
|
|
||||||
Though you can type the URL into the address bar, a navigation UI is easier for the user and more common.
|
Though you can type the URL into the address bar, a navigation UI is easier for the user and more common.
|
||||||
Replace the default placeholder markup in `app.component.html` with a custom nav
|
Replace the default placeholder markup in `app.component.html` with a custom nav
|
||||||
so you can easily navigate to your modules in the browser:
|
so you can easily navigate to your modules in the browser:
|
||||||
|
|
||||||
|
|
||||||
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" header="app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
|
<code-example path="lazy-loading-ngmodules/src/app/app.component.html" header="app.component.html" region="app-component-template" header="src/app/app.component.html"></code-example>
|
||||||
|
|
||||||
|
|
||||||
To see your app in the browser so far, enter the following command in the terminal window:
|
To see your app in the browser so far, enter the following command in the terminal window:
|
||||||
|
|
||||||
<code-example language="bash">
|
<code-example language="bash">
|
||||||
@ -102,7 +142,7 @@ These buttons work, because the CLI automatically added the routes to the featur
|
|||||||
|
|
||||||
{@a config-routes}
|
{@a config-routes}
|
||||||
|
|
||||||
## Imports and route configuration
|
### Imports and route configuration
|
||||||
|
|
||||||
The CLI automatically added each feature module to the routes map at the application level.
|
The CLI automatically added each feature module to the routes map at the application level.
|
||||||
Finish this off by adding the default route. In the `app-routing.module.ts` file, update the `routes` array with the following:
|
Finish this off by adding the default route. In the `app-routing.module.ts` file, update the `routes` array with the following:
|
||||||
@ -134,7 +174,7 @@ The other feature module's routing module is configured similarly.
|
|||||||
|
|
||||||
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" id="orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
|
<code-example path="lazy-loading-ngmodules/src/app/orders/orders-routing.module.ts" id="orders-routing.module.ts" region="orders-routing-module-detail" header="src/app/orders/orders-routing.module.ts (excerpt)"></code-example>
|
||||||
|
|
||||||
## Confirm it’s working
|
### Verify lazy loading
|
||||||
|
|
||||||
You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing `Cmd+Option+i` on a Mac or `Ctrl+Shift+j` on a PC and go to the Network Tab.
|
You can check to see that a module is indeed being lazy loaded with the Chrome developer tools. In Chrome, open the dev tools by pressing `Cmd+Option+i` on a Mac or `Ctrl+Shift+j` on a PC and go to the Network Tab.
|
||||||
|
|
||||||
@ -175,6 +215,105 @@ The `forRoot()` method takes care of the *global* injector configuration for the
|
|||||||
The `forChild()` method has no injector configuration. It uses directives such as `RouterOutlet` and `RouterLink`.
|
The `forChild()` method has no injector configuration. It uses directives such as `RouterOutlet` and `RouterLink`.
|
||||||
For more information, see the [`forRoot()` pattern](guide/singleton-services#forRoot) section of the [Singleton Services](guide/singleton-services) guide.
|
For more information, see the [`forRoot()` pattern](guide/singleton-services#forRoot) section of the [Singleton Services](guide/singleton-services) guide.
|
||||||
|
|
||||||
|
{@a preloading}
|
||||||
|
|
||||||
|
## Preloading
|
||||||
|
|
||||||
|
Preloading improves UX by loading parts of your app in the background.
|
||||||
|
You can preload modules or component data.
|
||||||
|
|
||||||
|
### Preloading modules
|
||||||
|
|
||||||
|
Preloading modules improves UX by loading parts of your app in the background so users don't have to wait for the elements to download when they activate a route.
|
||||||
|
|
||||||
|
To enable preloading of all lazy loaded modules, import the `PreloadAllModules` token from the Angular `router`.
|
||||||
|
|
||||||
|
<code-example header="AppRoutingModule (excerpt)">
|
||||||
|
|
||||||
|
import { PreloadAllModules } from '@angular/router';
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Still in the `AppRoutingModule`, specify your preloading strategy in `forRoot()`.
|
||||||
|
|
||||||
|
<code-example header="AppRoutingModule (excerpt)">
|
||||||
|
|
||||||
|
RouterModule.forRoot(
|
||||||
|
appRoutes,
|
||||||
|
{
|
||||||
|
preloadingStrategy: PreloadAllModules
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
### Preloading component data
|
||||||
|
|
||||||
|
To preload component data, you can use a `resolver`.
|
||||||
|
Resolvers improve UX by blocking the page load until all necessary data is available to fully display the page.
|
||||||
|
|
||||||
|
#### Resolvers
|
||||||
|
|
||||||
|
Create a resolver service.
|
||||||
|
With the CLI, the command to generate a service is as follows:
|
||||||
|
|
||||||
|
|
||||||
|
<code-example language="none" class="code-shell">
|
||||||
|
ng generate service <service-name>
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
In your service, import the following router members, implement `Resolve`, and inject the `Router` service:
|
||||||
|
|
||||||
|
<code-example header="Resolver service (excerpt)">
|
||||||
|
|
||||||
|
import { Resolve } from '@angular/router';
|
||||||
|
|
||||||
|
...
|
||||||
|
|
||||||
|
export class CrisisDetailResolverService implements Resolve<> {
|
||||||
|
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<> {
|
||||||
|
// your logic goes here
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Import this resolver into your module's routing module.
|
||||||
|
|
||||||
|
<code-example header="Feature module's routing module (excerpt)">
|
||||||
|
|
||||||
|
import { YourResolverService } from './your-resolver.service';
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Add a `resolve` object to the component's `route` configuration.
|
||||||
|
|
||||||
|
<code-example header="Feature module's routing module (excerpt)">
|
||||||
|
{
|
||||||
|
path: '/your-path',
|
||||||
|
component: YourComponent,
|
||||||
|
resolve: {
|
||||||
|
crisis: YourResolverService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
In the component, use an `Observable` to get the data from the `ActivatedRoute`.
|
||||||
|
|
||||||
|
|
||||||
|
<code-example header="Component (excerpt)">
|
||||||
|
ngOnInit() {
|
||||||
|
this.route.data
|
||||||
|
.subscribe((your-parameters) => {
|
||||||
|
// your data-specific code goes here
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
For more information with a working example, see the [routing tutorial section on preloading](guide/router#preloading-background-loading-of-feature-areas).
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
## More on NgModules and routing
|
## More on NgModules and routing
|
||||||
|
@ -1,45 +1,49 @@
|
|||||||
# Lifecycle hooks
|
# Hooking into the component lifecycle
|
||||||
|
|
||||||
A component has a lifecycle managed by Angular.
|
A component instance has a lifecycle that starts when Angular instantiates the component class and renders the component view along with its child views.
|
||||||
|
The lifecycle continues with change detection, as Angular checks to see when data-bound properties change, and updates both the view and the component instance as needed.
|
||||||
|
The lifecycle ends when Angular destroys the component instance and removes its rendered template from the DOM.
|
||||||
|
Directives have a similar lifecycle, as Angular creates, updates, and destroys instances in the course of execution.
|
||||||
|
|
||||||
Angular creates and renders components along with their children, checks when their data-bound properties change, and destroys them before removing them from the DOM.
|
Your application can use [lifecycle hook methods](guide/glossary#lifecycle-hook "Definition of lifecycle hook") to tap into key events in the lifecycle of a component or directive in order to initialize new instances, initiate change detection when needed, respond to updates during change detection, and clean up before deletion of instances.
|
||||||
|
|
||||||
Angular offers **lifecycle hooks**
|
## Prerequisites
|
||||||
that provide visibility into these key life moments and the ability to act when they occur.
|
|
||||||
|
|
||||||
A directive has the same set of lifecycle hooks.
|
Before working with lifecycle hooks, you should have a basic understanding of the following:
|
||||||
|
|
||||||
|
* [TypeScript programming](https://www.typescriptlang.org/).
|
||||||
|
* Angular app-design fundamentals, as described in [Angular Concepts](guide/architecture "Introduction to fundamental app-design concepts").
|
||||||
|
|
||||||
{@a hooks-overview}
|
{@a hooks-overview}
|
||||||
|
|
||||||
## Component lifecycle hooks overview
|
## Responding to lifecycle events
|
||||||
|
|
||||||
Directive and component instances have a lifecycle
|
You can respond to events in the lifecycle of a component or directive by implementing one or more of the *lifecycle hook* interfaces in the Angular `core` library.
|
||||||
as Angular creates, updates, and destroys them.
|
The hooks give you the opportunity to act on a component or directive instance at the appropriate moment, as Angular creates, updates, or destroys that instance.
|
||||||
Developers can tap into key moments in that lifecycle by implementing
|
|
||||||
one or more of the *lifecycle hook* interfaces in the Angular `core` library.
|
|
||||||
|
|
||||||
Each interface has a single hook method whose name is the interface name prefixed with `ng`.
|
Each interface defines the prototype for a single hook method, whose name is the interface name prefixed with `ng`.
|
||||||
For example, the `OnInit` interface has a hook method named `ngOnInit()`
|
For example, the `OnInit` interface has a hook method named `ngOnInit()`. If you implement this method in your component or directive class, Angular calls it shortly after checking the input properties for that component or directive for the first time.
|
||||||
that Angular calls shortly after creating the component:
|
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)"></code-example>
|
<code-example path="lifecycle-hooks/src/app/peek-a-boo.component.ts" region="ngOnInit" header="peek-a-boo.component.ts (excerpt)"></code-example>
|
||||||
|
|
||||||
No directive or component will implement all of the lifecycle hooks.
|
You don't have to implement all (or any) of the lifecycle hooks, just the ones you need.
|
||||||
Angular only calls a directive/component hook method *if it is defined*.
|
|
||||||
|
|
||||||
{@a hooks-purpose-timing}
|
{@a hooks-purpose-timing}
|
||||||
|
|
||||||
## Lifecycle sequence
|
### Lifecycle event sequence
|
||||||
|
|
||||||
*After* creating a component/directive by calling its constructor, Angular
|
After your application instantiates a component or directive by calling its constructor, Angular calls the hook methods you have implemented at the appropriate point in the lifecycle of that instance.
|
||||||
calls the lifecycle hook methods in the following sequence at specific moments:
|
|
||||||
|
Angular executes hook methods in the following sequence. You can use them to perform the following kinds of operations.
|
||||||
|
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<col width="20%"></col>
|
<col width="20%"></col>
|
||||||
<col width="80%"></col>
|
<col width="60%"></col>
|
||||||
|
<col width="20%"></col>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Hook</th>
|
<th>Hook method</th>
|
||||||
<th>Purpose and Timing</th>
|
<th>Purpose</th>
|
||||||
|
<th>Timing</th>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style='vertical-align:top'>
|
<tr style='vertical-align:top'>
|
||||||
<td>
|
<td>
|
||||||
@ -47,9 +51,15 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Respond when Angular (re)sets data-bound input properties.
|
Respond when Angular sets or resets data-bound input properties.
|
||||||
The method receives a `SimpleChanges` object of current and previous property values.
|
The method receives a `SimpleChanges` object of current and previous property values.
|
||||||
|
|
||||||
|
Note that this happens very frequently, so any operation you perform here impacts performance significantly.
|
||||||
|
See details in [Using change detection hooks](#onchanges) in this document.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
Called before `ngOnInit()` and whenever one or more data-bound input properties change.
|
Called before `ngOnInit()` and whenever one or more data-bound input properties change.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
@ -60,10 +70,14 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Initialize the directive/component after Angular first displays the data-bound properties
|
Initialize the directive or component after Angular first displays the data-bound properties
|
||||||
and sets the directive/component's input properties.
|
and sets the directive or component's input properties.
|
||||||
|
See details in [Initializing a component or directive](#oninit) in this document.
|
||||||
|
|
||||||
Called _once_, after the _first_ `ngOnChanges()`.
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Called once, after the first `ngOnChanges()`.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -74,8 +88,12 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
<td>
|
<td>
|
||||||
|
|
||||||
Detect and act upon changes that Angular can't or won't detect on its own.
|
Detect and act upon changes that Angular can't or won't detect on its own.
|
||||||
|
See details and example in [Defining custom change detection](#docheck) in this document.
|
||||||
|
|
||||||
Called during every change detection run, immediately after `ngOnChanges()` and `ngOnInit()`.
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Called immediately after `ngOnChanges()` on every change detection run, and immediately after `ngOnInit()` on the first run.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -85,7 +103,13 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Respond after Angular projects external content into the component's view / the view that a directive is in.
|
Respond after Angular projects external content into the component's view, or into the view that a directive is in.
|
||||||
|
|
||||||
|
See details and example in [Responding to changes in content](#aftercontent) in this document.
|
||||||
|
|
||||||
|
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
|
||||||
Called _once_ after the first `ngDoCheck()`.
|
Called _once_ after the first `ngDoCheck()`.
|
||||||
|
|
||||||
@ -97,9 +121,15 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Respond after Angular checks the content projected into the directive/component.
|
Respond after Angular checks the content projected into the directive or component.
|
||||||
|
|
||||||
Called after the `ngAfterContentInit()` and every subsequent `ngDoCheck()`.
|
See details and example in [Responding to projected content changes](#aftercontent) in this document.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Called after `ngAfterContentInit()` and every subsequent `ngDoCheck()`.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -109,10 +139,15 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Respond after Angular initializes the component's views and child views / the view that a directive is in.
|
Respond after Angular initializes the component's views and child views, or the view that contains the directive.
|
||||||
|
|
||||||
|
See details and example in [Responding to view changes](#afterview) in this document.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
Called _once_ after the first `ngAfterContentChecked()`.
|
Called _once_ after the first `ngAfterContentChecked()`.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style='vertical-align:top'>
|
<tr style='vertical-align:top'>
|
||||||
@ -121,7 +156,11 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Respond after Angular checks the component's views and child views / the view that a directive is in.
|
Respond after Angular checks the component's views and child views, or the view that contains the directive.
|
||||||
|
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
Called after the `ngAfterViewInit()` and every subsequent `ngAfterContentChecked()`.
|
Called after the `ngAfterViewInit()` and every subsequent `ngAfterContentChecked()`.
|
||||||
|
|
||||||
@ -133,53 +172,32 @@ calls the lifecycle hook methods in the following sequence at specific moments:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Cleanup just before Angular destroys the directive/component.
|
Cleanup just before Angular destroys the directive or component.
|
||||||
Unsubscribe Observables and detach event handlers to avoid memory leaks.
|
Unsubscribe Observables and detach event handlers to avoid memory leaks.
|
||||||
|
See details in [Cleaning up on instance destruction](#ondestroy) in this document.
|
||||||
|
|
||||||
Called _just before_ Angular destroys the directive/component.
|
</td>
|
||||||
|
|
||||||
|
<td>
|
||||||
|
|
||||||
|
Called immediately before Angular destroys the directive or component.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
{@a interface-optional}
|
|
||||||
|
|
||||||
## Interfaces are optional (technically)
|
|
||||||
|
|
||||||
The interfaces are optional for JavaScript and Typescript developers from a purely technical perspective.
|
|
||||||
The JavaScript language doesn't have interfaces.
|
|
||||||
Angular can't see TypeScript interfaces at runtime because they disappear from the transpiled JavaScript.
|
|
||||||
|
|
||||||
Fortunately, they aren't necessary.
|
|
||||||
You don't have to add the lifecycle hook interfaces to directives and components to benefit from the hooks themselves.
|
|
||||||
|
|
||||||
Angular instead inspects directive and component classes and calls the hook methods *if they are defined*.
|
|
||||||
Angular finds and calls methods like `ngOnInit()`, with or without the interfaces.
|
|
||||||
|
|
||||||
Nonetheless, it's good practice to add interfaces to TypeScript directive classes
|
|
||||||
in order to benefit from strong typing and editor tooling.
|
|
||||||
|
|
||||||
{@a other-lifecycle-hooks}
|
|
||||||
|
|
||||||
## Other Angular lifecycle hooks
|
|
||||||
|
|
||||||
Other Angular sub-systems may have their own lifecycle hooks apart from these component hooks.
|
|
||||||
|
|
||||||
3rd party libraries might implement their hooks as well in order to give developers more
|
|
||||||
control over how these libraries are used.
|
|
||||||
|
|
||||||
{@a the-sample}
|
{@a the-sample}
|
||||||
|
|
||||||
## Lifecycle examples
|
### Lifecycle example set
|
||||||
|
|
||||||
The <live-example></live-example>
|
The <live-example></live-example>
|
||||||
demonstrates the lifecycle hooks in action through a series of exercises
|
demonstrates the use of lifecycle hooks through a series of exercises
|
||||||
presented as components under the control of the root `AppComponent`.
|
presented as components under the control of the root `AppComponent`.
|
||||||
|
In each case a *parent* component serves as a test rig for
|
||||||
They follow a common pattern: a *parent* component serves as a test rig for
|
|
||||||
a *child* component that illustrates one or more of the lifecycle hook methods.
|
a *child* component that illustrates one or more of the lifecycle hook methods.
|
||||||
|
|
||||||
Here's a brief description of each exercise:
|
The following table lists the exercises with brief descriptions.
|
||||||
|
The sample code is also used to illustrate specific tasks in the following sections.
|
||||||
|
|
||||||
<table width="100%">
|
<table width="100%">
|
||||||
<col width="20%"></col>
|
<col width="20%"></col>
|
||||||
@ -205,12 +223,9 @@ Here's a brief description of each exercise:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Directives have lifecycle hooks too.
|
Shows how you can use lifecycle hooks with a custom directive.
|
||||||
A `SpyDirective` can log when the element it spies upon is
|
The `SpyDirective` implements the `ngOnInit()` and `ngOnDestroy()` hooks,
|
||||||
created or destroyed using the `ngOnInit` and `ngOnDestroy` hooks.
|
and uses them to watch and report when an element goes in or out of the current view.
|
||||||
|
|
||||||
This example applies the `SpyDirective` to a `<div>` in an `ngFor` *hero* repeater
|
|
||||||
managed by the parent `SpyComponent`.
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -220,9 +235,9 @@ Here's a brief description of each exercise:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
See how Angular calls the `ngOnChanges()` hook with a `changes` object
|
Demonstrates how Angular calls the `ngOnChanges()` hook
|
||||||
every time one of the component input properties changes.
|
every time one of the component input properties changes,
|
||||||
Shows how to interpret the `changes` object.
|
and shows how to interpret the `changes` object passed to the hook method.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -232,8 +247,8 @@ Here's a brief description of each exercise:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Implements an `ngDoCheck()` method with custom change detection.
|
Implements the `ngDoCheck()` method with custom change detection.
|
||||||
See how often Angular calls this hook and watch it post changes to a log.
|
Watch the hook post changes to a log to see how often Angular calls this hook.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -243,8 +258,8 @@ Here's a brief description of each exercise:
|
|||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Shows what Angular means by a *view*.
|
Shows what Angular means by a [view](guide/glossary#view "Definition of view.").
|
||||||
Demonstrates the `ngAfterViewInit` and `ngAfterViewChecked` hooks.
|
Demonstrates the `ngAfterViewInit()` and `ngAfterViewChecked()` hooks.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -256,40 +271,87 @@ Here's a brief description of each exercise:
|
|||||||
|
|
||||||
Shows how to project external content into a component and
|
Shows how to project external content into a component and
|
||||||
how to distinguish projected content from a component's view children.
|
how to distinguish projected content from a component's view children.
|
||||||
Demonstrates the `ngAfterContentInit` and `ngAfterContentChecked` hooks.
|
Demonstrates the `ngAfterContentInit()` and `ngAfterContentChecked()` hooks.
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr style='vertical-align:top'>
|
<tr style='vertical-align:top'>
|
||||||
<td>
|
<td>
|
||||||
Counter
|
<a href="#counter">Counter</a>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
||||||
Demonstrates a combination of a component and a directive
|
Demonstrates a combination of a component and a directive, each with its own hooks.
|
||||||
each with its own hooks.
|
|
||||||
|
|
||||||
In this example, a `CounterComponent` logs a change (via `ngOnChanges`)
|
|
||||||
every time the parent component increments its input counter property.
|
|
||||||
Meanwhile, the `SpyDirective` from the previous example is applied
|
|
||||||
to the `CounterComponent` log where it watches log entries being created and destroyed.
|
|
||||||
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
The remainder of this page discusses selected exercises in further detail.
|
|
||||||
|
{@a oninit}
|
||||||
|
|
||||||
|
## Initializing a component or directive
|
||||||
|
|
||||||
|
Use the `ngOnInit()` method to perform the following initialization tasks.
|
||||||
|
|
||||||
|
* Perform complex initializations outside of the constructor.
|
||||||
|
Components should be cheap and safe to construct.
|
||||||
|
You should not, for example, fetch data in a component constructor.
|
||||||
|
You shouldn't worry that a new component will try to contact a remote server when
|
||||||
|
created under test or before you decide to display it.
|
||||||
|
|
||||||
|
An `ngOnInit()` is a good place for a component to fetch its initial data.
|
||||||
|
For an example, see the [Tour of Heroes tutorial](tutorial/toh-pt4#oninit).
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
In [Flaw: Constructor does Real Work](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/), Misko Hevery, Angular team lead, explains why you should avoid complex constructor logic.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
* Set up the component after Angular sets the input properties.
|
||||||
|
Constructors should do no more than set the initial local variables to simple values.
|
||||||
|
|
||||||
|
Keep in mind that a directive's data-bound input properties are not set until _after construction_.
|
||||||
|
If you need to initialize the directive based on those properties, set them when `ngOnInit()` runs.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
The `ngOnChanges()` method is your first opportunity to access those properties.
|
||||||
|
Angular calls `ngOnChanges()` before `ngOnInit()`, but also many times after that.
|
||||||
|
It only calls `ngOnInit()` once.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{@a ondestroy}
|
||||||
|
|
||||||
|
## Cleaning up on instance destruction
|
||||||
|
|
||||||
|
Put cleanup logic in `ngOnDestroy()`, the logic that must run before Angular destroys the directive.
|
||||||
|
|
||||||
|
This is the place to free resources that won't be garbage-collected automatically.
|
||||||
|
You risk memory leaks if you neglect to do so.
|
||||||
|
|
||||||
|
* Unsubscribe from Observables and DOM events.
|
||||||
|
* Stop interval timers.
|
||||||
|
* Unregister all callbacks that the directive registered with global or application services.
|
||||||
|
|
||||||
|
The `ngOnDestroy()` method is also the time to notify another part of the application that the component is going away.
|
||||||
|
|
||||||
|
|
||||||
|
## General examples
|
||||||
|
|
||||||
|
The following examples demonstrate the call sequence and relative frequency of the various lifecycle events, and how the hooks can be used separately or together for components and directives.
|
||||||
|
|
||||||
{@a peek-a-boo}
|
{@a peek-a-boo}
|
||||||
|
|
||||||
## Peek-a-boo: all hooks
|
### Sequence and frequency of all lifecycle events
|
||||||
|
|
||||||
The `PeekABooComponent` demonstrates all of the hooks in one component.
|
To show how Angular calls the hooks in the expected order, the `PeekABooComponent` demonstrates all of the hooks in one component.
|
||||||
|
|
||||||
You would rarely, if ever, implement all of the interfaces like this.
|
In practice you would rarely, if ever, implement all of the interfaces the way this demo does.
|
||||||
The peek-a-boo exists to show how Angular calls the hooks in the expected order.
|
|
||||||
|
|
||||||
This snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
The following snapshot reflects the state of the log after the user clicked the *Create...* button and then the *Destroy...* button.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src="generated/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo">
|
<img src="generated/images/guide/lifecycle-hooks/peek-a-boo.png" alt="Peek-a-boo">
|
||||||
@ -301,52 +363,42 @@ The sequence of log messages follows the prescribed hook calling order:
|
|||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
The constructor isn't an Angular hook *per se*.
|
Notice that the log confirms that input properties (the `name` property in this case) have no assigned values at construction.
|
||||||
The log confirms that input properties (the `name` property in this case) have no assigned values at construction.
|
The input properties are available to the `onInit()` method for further initialization.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Had the user clicked the *Update Hero* button, the log would show another `OnChanges` and two more triplets of
|
Had the user clicked the *Update Hero* button, the log would show another `OnChanges` and two more triplets of `DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
||||||
`DoCheck`, `AfterContentChecked` and `AfterViewChecked`.
|
Notice that these three hooks fire *often*, so it is important to keep their logic as lean as possible.
|
||||||
Clearly these three hooks fire *often*. Keep the logic in these hooks as lean as possible!
|
|
||||||
|
|
||||||
The next examples focus on hook details.
|
|
||||||
|
|
||||||
|
|
||||||
{@a spy}
|
{@a spy}
|
||||||
|
|
||||||
## Spying *OnInit* and *OnDestroy*
|
### Use directives to watch the DOM
|
||||||
|
|
||||||
Go undercover with these two spy hooks to discover when an element is initialized or destroyed.
|
The `Spy` example demonstrates how you can use hook method for directives as well as components.
|
||||||
|
The `SpyDirective` implements two hooks, `ngOnInit()` and `ngOnDestroy()`, in order to discover when a watched element is in the current view.
|
||||||
|
|
||||||
This is the perfect infiltration job for a directive.
|
This template applies the `SpyDirective` to a `<div>` in the `ngFor` *hero* repeater managed by the parent `SpyComponent`.
|
||||||
The heroes will never know they're being watched.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
The example does not perform any initialization or clean-up.
|
||||||
|
It just tracks the appearance and disappearance of an element in the view by recording when the directive itself is instantiated and destroyed.
|
||||||
|
|
||||||
Kidding aside, pay attention to two key points:
|
A spy directive like this can provide insight into a DOM object that you cannot change directly.
|
||||||
|
You can't touch the implementation of a native `<div>`, or modify a third party component.
|
||||||
|
You can, however watch these elements with a directive.
|
||||||
|
|
||||||
1. Angular calls hook methods for *directives* as well as components.<br><br>
|
The directive defines `ngOnInit()` and `ngOnDestroy()` hooks
|
||||||
|
|
||||||
2. A spy directive can provide insight into a DOM object that you cannot change directly.
|
|
||||||
Obviously you can't touch the implementation of a native `<div>`.
|
|
||||||
You can't modify a third party component either.
|
|
||||||
But you can watch both with a directive.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The sneaky spy directive is simple, consisting almost entirely of `ngOnInit()` and `ngOnDestroy()` hooks
|
|
||||||
that log messages to the parent via an injected `LoggerService`.
|
that log messages to the parent via an injected `LoggerService`.
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts"></code-example>
|
<code-example path="lifecycle-hooks/src/app/spy.directive.ts" region="spy-directive" header="src/app/spy.directive.ts"></code-example>
|
||||||
|
|
||||||
You can apply the spy to any native or component element and it'll be initialized and destroyed
|
You can apply the spy to any native or component element, and see that it is initialized and destroyed
|
||||||
at the same time as that element.
|
at the same time as that element.
|
||||||
Here it is attached to the repeated hero `<div>`:
|
Here it is attached to the repeated hero `<div>`:
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html"></code-example>
|
<code-example path="lifecycle-hooks/src/app/spy.component.html" region="template" header="src/app/spy.component.html"></code-example>
|
||||||
|
|
||||||
Each spy's birth and death marks the birth and death of the attached hero `<div>`
|
Each spy's creation and destruction marks the appearance and disappearance of the attached hero `<div>`
|
||||||
with an entry in the *Hook Log* as seen here:
|
with an entry in the *Hook Log* as seen here:
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
@ -359,70 +411,20 @@ The *Reset* button clears the `heroes` list.
|
|||||||
Angular removes all hero `<div>` elements from the DOM and destroys their spy directives at the same time.
|
Angular removes all hero `<div>` elements from the DOM and destroys their spy directives at the same time.
|
||||||
The spy's `ngOnDestroy()` method reports its last moments.
|
The spy's `ngOnDestroy()` method reports its last moments.
|
||||||
|
|
||||||
The `ngOnInit()` and `ngOnDestroy()` methods have more vital roles to play in real applications.
|
{@a counter}
|
||||||
|
|
||||||
{@a oninit}
|
### Use component and directive hooks together
|
||||||
|
|
||||||
### _OnInit()_
|
In this example, a `CounterComponent` uses the `ngOnChanges()` method to log a change every time the parent component increments its input `counter` property.
|
||||||
|
|
||||||
Use `ngOnInit()` for two main reasons:
|
This example applies the `SpyDirective` from the previous example to the `CounterComponent` log, in order to watch the creation and destruction of log entries.
|
||||||
|
|
||||||
1. To perform complex initializations shortly after construction.
|
|
||||||
1. To set up the component after Angular sets the input properties.
|
|
||||||
|
|
||||||
Experienced developers agree that components should be cheap and safe to construct.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
Misko Hevery, Angular team lead,
|
|
||||||
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
|
|
||||||
you should avoid complex constructor logic.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
Don't fetch data in a component constructor.
|
|
||||||
You shouldn't worry that a new component will try to contact a remote server when
|
|
||||||
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
|
|
||||||
[Tour of Heroes Tutorial](tutorial/toh-pt4#oninit) guide shows how.
|
|
||||||
|
|
||||||
|
|
||||||
Remember also that a directive's data-bound input properties are not set until _after construction_.
|
|
||||||
That's a problem if you need to initialize the directive based on those properties.
|
|
||||||
They'll have been set when `ngOnInit()` runs.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
The `ngOnChanges()` method is your first opportunity to access those properties.
|
|
||||||
Angular calls `ngOnChanges()` before `ngOnInit()` and many times after that.
|
|
||||||
It only calls `ngOnInit()` once.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
You can count on Angular to call the `ngOnInit()` method _soon_ after creating the component.
|
|
||||||
That's where the heavy initialization logic belongs.
|
|
||||||
|
|
||||||
{@a ondestroy}
|
|
||||||
|
|
||||||
### _OnDestroy()_
|
|
||||||
|
|
||||||
Put cleanup logic in `ngOnDestroy()`, the logic that *must* run before Angular destroys the directive.
|
|
||||||
|
|
||||||
This is the time to notify another part of the application that the component is going away.
|
|
||||||
|
|
||||||
This is the place to free resources that won't be garbage collected automatically.
|
|
||||||
Unsubscribe from Observables and DOM events. Stop interval timers.
|
|
||||||
Unregister all callbacks that this directive registered with global or application services.
|
|
||||||
You risk memory leaks if you neglect to do so.
|
|
||||||
|
|
||||||
{@a onchanges}
|
{@a onchanges}
|
||||||
|
|
||||||
## _OnChanges()_
|
## Using change detection hooks
|
||||||
|
|
||||||
Angular calls its `ngOnChanges()` method whenever it detects changes to ***input properties*** of the component (or directive).
|
Angular calls the `ngOnChanges()` method of a component or directive whenever it detects changes to the ***input properties***.
|
||||||
This example monitors the `OnChanges` hook.
|
The *onChanges* example demonstrates this by monitoring the `OnChanges()` hook.
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)"></code-example>
|
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="ng-on-changes" header="on-changes.component.ts (excerpt)"></code-example>
|
||||||
|
|
||||||
@ -434,7 +436,7 @@ The example component, `OnChangesComponent`, has two input properties: `hero` an
|
|||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts"></code-example>
|
<code-example path="lifecycle-hooks/src/app/on-changes.component.ts" region="inputs" header="src/app/on-changes.component.ts"></code-example>
|
||||||
|
|
||||||
The host `OnChangesParentComponent` binds to them like this:
|
The host `OnChangesParentComponent` binds to them as follows.
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes" header="src/app/on-changes-parent.component.html"></code-example>
|
<code-example path="lifecycle-hooks/src/app/on-changes-parent.component.html" region="on-changes" header="src/app/on-changes-parent.component.html"></code-example>
|
||||||
|
|
||||||
@ -445,51 +447,20 @@ Here's the sample in action as the user makes changes.
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
The log entries appear as the string value of the *power* property changes.
|
The log entries appear as the string value of the *power* property changes.
|
||||||
But the `ngOnChanges` does not catch changes to `hero.name`
|
Notice, however, that the `ngOnChanges()` method does not catch changes to `hero.name`.
|
||||||
That's surprising at first.
|
This is because Angular calls the hook only when the value of the input property changes.
|
||||||
|
In this case, `hero` is the input property, and the value of the `hero` property is the *reference to the hero object*.
|
||||||
|
The object reference did not change when the value of its own `name` property changed.
|
||||||
|
|
||||||
Angular only calls the hook when the value of the input property changes.
|
|
||||||
The value of the `hero` property is the *reference to the hero object*.
|
|
||||||
Angular doesn't care that the hero's own `name` property changed.
|
|
||||||
The hero object *reference* didn't change so, from Angular's perspective, there is no change to report!
|
|
||||||
|
|
||||||
{@a docheck}
|
|
||||||
|
|
||||||
## _DoCheck()_
|
|
||||||
|
|
||||||
Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
Use this method to detect a change that Angular overlooked.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
|
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)"></code-example>
|
|
||||||
|
|
||||||
This code inspects certain _values of interest_, capturing and comparing their current state against previous values.
|
|
||||||
It writes a special message to the log when there are no substantive changes to the `hero` or the `power`
|
|
||||||
so you can see how often `DoCheck` is called. The results are illuminating:
|
|
||||||
|
|
||||||
<div class="lightbox">
|
|
||||||
<img src='generated/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it has a frightful cost.
|
|
||||||
This hook is called with enormous frequency—after _every_
|
|
||||||
change detection cycle no matter where the change occurred.
|
|
||||||
It's called over twenty times in this example before the user can do anything.
|
|
||||||
|
|
||||||
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
|
||||||
Mere mousing into another `<input>` triggers a call.
|
|
||||||
Relatively few calls reveal actual changes to pertinent data.
|
|
||||||
Clearly our implementation must be very lightweight or the user experience suffers.
|
|
||||||
|
|
||||||
{@a afterview}
|
{@a afterview}
|
||||||
|
|
||||||
## AfterView
|
### Responding to view changes
|
||||||
|
|
||||||
|
As Angular traverses the [view hierarchy](guide/glossary#view-hierarchy "Definition of view hierarchy definition") during change detection, it needs to be sure that a change in a child does not attempt to cause a change in its own parent. Such a change would not be rendered properly, because of how [unidirectional data flow](guide/glossary#unidirectional-data-flow "Definition") works.
|
||||||
|
|
||||||
|
If you need to make a change that inverts the expected data flow, you must trigger a new change detection cycle to allow that change to be rendered.
|
||||||
|
The examples illustrate how to make such changes safely.
|
||||||
|
|
||||||
The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` hooks that Angular calls
|
The *AfterView* sample explores the `AfterViewInit()` and `AfterViewChecked()` hooks that Angular calls
|
||||||
*after* it creates a component's child views.
|
*after* it creates a component's child views.
|
||||||
@ -506,47 +477,46 @@ 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
|
which can only be reached by querying for the child view via the property decorated with
|
||||||
[@ViewChild](api/core/ViewChild).
|
[@ViewChild](api/core/ViewChild).
|
||||||
|
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)"></code-example>
|
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="hooks" header="AfterViewComponent (class excerpts)"></code-example>
|
||||||
|
|
||||||
{@a wait-a-tick}
|
{@a wait-a-tick}
|
||||||
|
|
||||||
### Abide by the unidirectional data flow rule
|
#### Wait before updating the view
|
||||||
The `doSomething()` method updates the screen when the hero name exceeds 10 characters.
|
|
||||||
|
In this example, the `doSomething()` method updates the screen when the hero name exceeds 10 characters, but waits a tick before updating `comment`.
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)"></code-example>
|
<code-example path="lifecycle-hooks/src/app/after-view.component.ts" region="do-something" header="AfterViewComponent (doSomething)"></code-example>
|
||||||
|
|
||||||
Why does the `doSomething()` method wait a tick before updating `comment`?
|
Both the `AfterViewInit()` and `AfterViewChecked()` hooks fire after the component's view has been composed.
|
||||||
|
If you modify the code so that the hook updates the component's data-bound `comment` property immediately, you can see that Angular throws an error.
|
||||||
|
|
||||||
Angular's unidirectional data flow rule forbids updates to the view *after* it has been composed.
|
The `LoggerService.tick_then()` statement postpones the log update
|
||||||
Both of these hooks fire _after_ the component's view has been composed.
|
for one turn of the browser's JavaScript cycle, which triggers a new change-detection cycle.
|
||||||
|
|
||||||
Angular throws an error if the hook updates the component's data-bound `comment` property immediately (try it!).
|
#### Write lean hook methods to avoid performance problems
|
||||||
The `LoggerService.tick_then()` postpones the log update
|
|
||||||
for one turn of the browser's JavaScript cycle and that's just long enough.
|
|
||||||
|
|
||||||
Here's *AfterView* in action:
|
When you run the *AfterView* sample, notice how frequently Angular calls `AfterViewChecked()`$emdash;often when there are no changes of interest.
|
||||||
|
Be very careful about how much logic or computation you put into one of these methods.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
|
|
||||||
<img src='generated/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView">
|
<img src='generated/images/guide/lifecycle-hooks/after-view-anim.gif' alt="AfterView">
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Notice that Angular frequently calls `AfterViewChecked()`, often when there are no changes of interest.
|
|
||||||
Write lean hook methods to avoid performance problems.
|
|
||||||
|
|
||||||
{@a aftercontent}
|
{@a aftercontent}
|
||||||
|
{@a aftercontent-hooks}
|
||||||
## AfterContent
|
|
||||||
|
|
||||||
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls
|
|
||||||
*after* Angular projects external content into the component.
|
|
||||||
|
|
||||||
{@a content-projection}
|
{@a content-projection}
|
||||||
|
|
||||||
### Content projection
|
### Responding to projected content changes
|
||||||
|
|
||||||
*Content projection* is a way to import HTML content from outside the component and insert that content
|
*Content projection* is a way to import HTML content from outside the component and insert that content
|
||||||
into the component's template in a designated spot.
|
into the component's template in a designated spot.
|
||||||
|
You can identify content projection in a template by looking for the following constructs.
|
||||||
|
|
||||||
|
* HTML between component element tags.
|
||||||
|
* The presence of `<ng-content>` tags in the component's template.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
@ -554,9 +524,12 @@ into the component's template in a designated spot.
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Consider this variation on the [previous _AfterView_](guide/lifecycle-hooks#afterview) example.
|
The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChecked()` hooks that Angular calls *after* Angular projects external content into the component.
|
||||||
|
|
||||||
|
Consider this variation on the [previous _AfterView_](#afterview) example.
|
||||||
This time, instead of including the child view within the template, it imports the content from
|
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:
|
the `AfterContentComponent`'s parent.
|
||||||
|
The following is the parent's template.
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)"></code-example>
|
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="parent-template" header="AfterContentParentComponent (template excerpt)"></code-example>
|
||||||
|
|
||||||
@ -564,7 +537,7 @@ Notice that the `<app-child>` tag is tucked between the `<after-content>` tags.
|
|||||||
Never put content between a component's element tags *unless you intend to project that content
|
Never put content between a component's element tags *unless you intend to project that content
|
||||||
into the component*.
|
into the component*.
|
||||||
|
|
||||||
Now look at the component's template:
|
Now look at the component's template.
|
||||||
|
|
||||||
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)"></code-example>
|
<code-example path="lifecycle-hooks/src/app/after-content.component.ts" region="template" header="AfterContentComponent (template)"></code-example>
|
||||||
|
|
||||||
@ -576,18 +549,8 @@ In this case, the projected content is the `<app-child>` from the parent.
|
|||||||
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
|
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
The telltale signs of *content projection* are twofold:
|
#### Using AfterContent hooks
|
||||||
|
|
||||||
* HTML between component element tags.
|
|
||||||
* The presence of `<ng-content>` tags in the component's template.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{@a aftercontent-hooks}
|
|
||||||
|
|
||||||
### AfterContent hooks
|
|
||||||
|
|
||||||
*AfterContent* hooks are similar to the *AfterView* hooks.
|
*AfterContent* hooks are similar to the *AfterView* hooks.
|
||||||
The key difference is in the child component.
|
The key difference is in the child component.
|
||||||
@ -606,11 +569,44 @@ which can only be reached by querying for them via the property decorated with
|
|||||||
|
|
||||||
{@a no-unidirectional-flow-worries}
|
{@a no-unidirectional-flow-worries}
|
||||||
|
|
||||||
### No unidirectional flow worries with _AfterContent_
|
<div class="alert is-helpful>
|
||||||
|
|
||||||
This component's `doSomething()` method update's the component's data-bound `comment` property immediately.
|
<header>No need to wait for content updates</header>
|
||||||
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.
|
This component's `doSomething()` method updates the component's data-bound `comment` property immediately.
|
||||||
|
There's no need to [delay the update to ensure proper rendering](#wait-a-tick "Delaying updates").
|
||||||
|
|
||||||
|
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.
|
Angular completes composition of the projected content *before* finishing the composition of this component's view.
|
||||||
There is a small window between the `AfterContent...` and `AfterView...` hooks to modify the host view.
|
There is a small window between the `AfterContent...` and `AfterView...` hooks that allows you to modify the host view.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{@a docheck}
|
||||||
|
|
||||||
|
## Defining custom change detection
|
||||||
|
|
||||||
|
To monitor changes that occur where `ngOnChanges()` won't catch them, you can implement your own change check, as shown in the *DoCheck* example.
|
||||||
|
This example shows how you can use the `ngDoCheck()` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||||
|
|
||||||
|
The *DoCheck* sample extends the *OnChanges* sample with the following `ngDoCheck()` hook:
|
||||||
|
|
||||||
|
<code-example path="lifecycle-hooks/src/app/do-check.component.ts" region="ng-do-check" header="DoCheckComponent (ngDoCheck)"></code-example>
|
||||||
|
|
||||||
|
This code inspects certain _values of interest_, capturing and comparing their current state against previous values.
|
||||||
|
It writes a special message to the log when there are no substantive changes to the `hero` or the `power` so you can see how often `DoCheck()` is called.
|
||||||
|
The results are illuminating.
|
||||||
|
|
||||||
|
<div class="lightbox">
|
||||||
|
<img src='generated/images/guide/lifecycle-hooks/do-check-anim.gif' alt="DoCheck">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
While the `ngDoCheck()` hook can detect when the hero's `name` has changed, it is very expensive.
|
||||||
|
This hook is called with enormous frequency—after _every_
|
||||||
|
change detection cycle no matter where the change occurred.
|
||||||
|
It's called over twenty times in this example before the user can do anything.
|
||||||
|
|
||||||
|
Most of these initial checks are triggered by Angular's first rendering of *unrelated data elsewhere on the page*.
|
||||||
|
Just moving the cursor into another `<input>` triggers a call.
|
||||||
|
Relatively few calls reveal actual changes to pertinent data.
|
||||||
|
If you use this hook, your implementation must be extremely lightweight or the user experience suffers.
|
||||||
|
@ -53,11 +53,11 @@ With dynamic queries (`static: false`), the query resolves after either `ngAfter
|
|||||||
The result will be updated for changes to your view, such as changes to `ngIf` and `ngFor` blocks.
|
The result will be updated for changes to your view, such as changes to `ngIf` and `ngFor` blocks.
|
||||||
|
|
||||||
For more information, see the following entries in the
|
For more information, see the following entries in the
|
||||||
[Static Query Migration Guide](https://angular.io/guide/static-query-migration):
|
[Static Query Migration Guide](guide/static-query-migration):
|
||||||
|
|
||||||
* [How do I choose which `static` flag value to use: `true` or `false`?](https://angular.io/guide/static-query-migration#how-do-i-choose-which-static-flag-value-to-use-true-or-false)
|
* [How do I choose which `static` flag value to use: `true` or `false`?](guide/static-query-migration#how-do-i-choose-which-static-flag-value-to-use-true-or-false)
|
||||||
|
|
||||||
* [Is there a case where I should use `{static: true}`?](https://angular.io/guide/static-query-migration#is-there-a-case-where-i-should-use-static-true)
|
* [Is there a case where I should use `{static: true}`?](guide/static-query-migration#is-there-a-case-where-i-should-use-static-true)
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
54
aio/content/guide/migration-solution-style-tsconfig.md
Normal file
54
aio/content/guide/migration-solution-style-tsconfig.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Solution-style `tsconfig.json` migration
|
||||||
|
|
||||||
|
## What does this migration do?
|
||||||
|
|
||||||
|
This migration adds support to existing projects for TypeScript's new ["solution-style" tsconfig feature](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig).
|
||||||
|
|
||||||
|
Support is added by making two changes:
|
||||||
|
1. Renaming the workspace-level `tsconfig.json` to `tsconfig.base.json`.
|
||||||
|
All project [TypeScript configuration files](guide/typescript-configuration) will extend from this base which contains the common options used throughout the workspace.
|
||||||
|
|
||||||
|
2. Adding the solution `tsconfig.json` file at the root of the workspace.
|
||||||
|
This `tsconfig.json` file will only contain references to project-level TypeScript configuration files and is only used by editors/IDEs.
|
||||||
|
|
||||||
|
As an example, the solution `tsconfig.json` for a new project is as follows:
|
||||||
|
```json
|
||||||
|
// This is a "Solution Style" tsconfig.json file, and is used by editors and TypeScript’s language server to improve development experience.
|
||||||
|
// It is not intended to be used to perform a compilation.
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./e2e/tsconfig.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why is this migration necessary?
|
||||||
|
|
||||||
|
Solution-style `tsconfig.json` files provide an improved editing experience and fix several long-standing defects when editing files in an IDE.
|
||||||
|
IDEs that leverage the TypeScript language service (for example, [Visual Studio Code](https://code.visualstudio.com)), will only use TypeScript configuration files that are named `tsconfig.json`.
|
||||||
|
In complex projects, there may be more than one compilation unit and each of these units may have different settings and options.
|
||||||
|
|
||||||
|
With the Angular CLI, a project will have application code that will target a browser.
|
||||||
|
It will also have unit tests that should not be included within the built application and that also need additional type information present (`jasmine` in this case).
|
||||||
|
Both parts of the project also share some but not all of the code within the project.
|
||||||
|
As a result, two separate TypeScript configuration files (`tsconfig.app.json` and `tsconfig.spec.json`) are needed to ensure that each part of the application is configured properly and that the right types are used for each part.
|
||||||
|
Also if web workers are used within a project, an additional tsconfig (`tsconfig.worker.json`) is needed.
|
||||||
|
Web workers use similar but incompatible types to the main browser application.
|
||||||
|
This requires the additional configuration file to ensure that the web worker files use the appropriate types and will build successfully.
|
||||||
|
|
||||||
|
While the Angular build system knows about all of these TypeScript configuration files, an IDE using TypeScript's language service does not.
|
||||||
|
Because of this, an IDE will not be able to properly analyze the code from each part of the project and may generate false errors or make suggestions that are incorrect for certain files.
|
||||||
|
By leveraging the new solution-style tsconfig, the IDE can now be aware of the configuration of each part of a project.
|
||||||
|
This allows each file to be treated appropriately based on its tsconfig.
|
||||||
|
IDE features such as error/warning reporting and auto-suggestion will operate more effectively as well.
|
||||||
|
|
||||||
|
The TypeScript 3.9 release [blog post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-9/#solution-style-tsconfig) also contains some additional information regarding this new feature.
|
52
aio/content/guide/migration-update-libraries-tslib.md
Normal file
52
aio/content/guide/migration-update-libraries-tslib.md
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# `tslib` direct dependency migration
|
||||||
|
|
||||||
|
## What does this migration do?
|
||||||
|
|
||||||
|
If you have any libraries within your workspace, this migration will convert `tslib` peer dependencies to direct dependencies for the libraries.
|
||||||
|
TypeScript uses the `tslib` package to provide common helper functions used in compiled TypeScript code.
|
||||||
|
The `tslib` version is also updated to `2.0.0` to support TypeScript 3.9.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "my-lib",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^9.0.0",
|
||||||
|
"@angular/core": "^9.0.0",
|
||||||
|
"tslib": "^1.12.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
After:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"name": "my-lib",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"peerDependencies": {
|
||||||
|
"@angular/common": "^9.0.0",
|
||||||
|
"@angular/core": "^9.0.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"tslib": "^2.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Why is this migration necessary?
|
||||||
|
|
||||||
|
The [`tslib`](https://github.com/Microsoft/tslib) is a runtime library for Typescript.
|
||||||
|
The version of this library is bound to the version of the TypeScript compiler used to compile a library.
|
||||||
|
Peer dependencies do not accurately represent this relationship between the runtime and the compiler.
|
||||||
|
If `tslib` remained declared as a library peer dependency, it would be possible for some Angular workspaces to get into a state where the workspace could not satisfy `tslib` peer dependency requirements for multiple libraries, resulting in build-time or run-time errors.
|
||||||
|
|
||||||
|
As of TypeScript 3.9 (used by Angular v10), `tslib` version of 2.x is required to build new applications.
|
||||||
|
However, older libraries built with previous version of TypeScript and already published to npm might need `tslib` 1.x.
|
||||||
|
This migration makes it possible for code depending on incompatible versions of the `tslib` runtime library to remain interoperable.
|
||||||
|
|
||||||
|
|
||||||
|
## Do I still need `tslib` as a dependency in my workspace `package.json`?
|
||||||
|
|
||||||
|
Yes.
|
||||||
|
The `tslib` dependency declared in the `package.json` file of the workspace is used to build applications within this workspace, as well as run unit tests for workspace libraries, and is required.
|
@ -0,0 +1,33 @@
|
|||||||
|
# Update `module` and `target` compiler options migration
|
||||||
|
|
||||||
|
## What does this migration do?
|
||||||
|
|
||||||
|
This migration adjusts the [`target`](https://www.typescriptlang.org/v2/en/tsconfig#target) and [`module`](https://www.typescriptlang.org/v2/en/tsconfig#module) settings within the [TypeScript configuration files](guide/typescript-configuration) for the workspace.
|
||||||
|
The changes to each option vary based on the builder or command that uses the TypeScript configuration file.
|
||||||
|
Unless otherwise noted, changes are only made if the existing value was not changed since the project was created.
|
||||||
|
This process helps ensure that intentional changes to the options are kept in place.
|
||||||
|
|
||||||
|
TypeScript Configuration File(s) | Changed Property | Existing Value | New Value
|
||||||
|
------------- | ------------- | ------------- | ------------- | -------------
|
||||||
|
`<workspace base>/tsconfig.base.json` | `"module"` | `"esnext"` | `"es2020"`
|
||||||
|
Used in `browser` builder options (`ng build` for applications) | `"module"` | `"esnext"` | `"es2020"`
|
||||||
|
Used in `ng-packgr` builder options (`ng build` for libraries) | `"module"` | `"esnext"` | `"es2020"`
|
||||||
|
Used in `karma` builder options (`ng test` for applications) | `"module"` | `"esnext"` | `"es2020"`
|
||||||
|
Used in `server` builder options (universal) | `"module"` | `"commonjs"` | _removed_
|
||||||
|
Used in `server` builder options (universal) | `"target"` | _any_ | `"es2016"`
|
||||||
|
Used in `protractor` builder options (`ng e2e` for applications) | `"target"` | `"es5"` | `"es2018"`
|
||||||
|
|
||||||
|
## Why is this migration necessary?
|
||||||
|
|
||||||
|
This migration provides improvements to the long-term supportability of projects by updating the projects to use recommended best practice compilation options.
|
||||||
|
|
||||||
|
For the functionality that executes on Node.js, such as Universal and Protractor, the new settings provide performance and troubleshooting benefits as well.
|
||||||
|
The minimum Node.js version for the Angular CLI (v10.13) supports features in ES2018 and earlier.
|
||||||
|
By targeting later ES versions, the compiler transforms less code and can use newer features directly.
|
||||||
|
Since zone.js does not support native `async` and `await`, the universal builds still target ES2016.
|
||||||
|
|
||||||
|
## Why `"es2020"` instead of `"esnext"`?
|
||||||
|
|
||||||
|
In TypeScript 3.9, the behavior of the TypeScript compiler controlled by `module` is the same with both `"esnext"` and `"es2020"` values.
|
||||||
|
This behavior can change in the future, because the `"esnext"` option could evolve in a backwards incompatible ways, resulting in build-time or run-time errors during a TypeScript update.
|
||||||
|
As a result, code can become unstable. Using the `"es2020"` option mitigates this risk.
|
@ -207,7 +207,7 @@ The following table summarizes the `@NgModule` metadata properties.
|
|||||||
|
|
||||||
Angular automatically adds components in the module's `bootstrap` and route definitions into the `entryComponents` list.
|
Angular automatically adds components in the module's `bootstrap` and route definitions into the `entryComponents` list.
|
||||||
|
|
||||||
That leaves only components bootstrapped using one of the imperative techniques, such as [`ViewComponentRef.createComponent()`](https://angular.io/api/core/ViewContainerRef#createComponent) as undiscoverable.
|
That leaves only components bootstrapped using one of the imperative techniques, such as [`ViewComponentRef.createComponent()`](api/core/ViewContainerRef#createComponent) as undiscoverable.
|
||||||
|
|
||||||
Dynamic component loading is not common in most apps beyond the router. If you need to dynamically load components, you must add these components to the `entryComponents` list yourself.
|
Dynamic component loading is not common in most apps beyond the router. If you need to dynamically load components, you must add these components to the `entryComponents` list yourself.
|
||||||
|
|
||||||
|
@ -118,7 +118,6 @@ Package name | Description
|
|||||||
[**@angular‑devkit/<br />build‑angular**](https://github.com/angular/angular-cli/) | The Angular build tools.
|
[**@angular‑devkit/<br />build‑angular**](https://github.com/angular/angular-cli/) | The Angular build tools.
|
||||||
[**@angular/cli**](https://github.com/angular/angular-cli/) | The Angular CLI tools.
|
[**@angular/cli**](https://github.com/angular/angular-cli/) | The Angular CLI tools.
|
||||||
**@angular/<br />compiler‑cli** | The Angular compiler, which is invoked by the Angular CLI's `ng build` and `ng serve` commands.
|
**@angular/<br />compiler‑cli** | The Angular compiler, which is invoked by the Angular CLI's `ng build` and `ng serve` commands.
|
||||||
**@angular/<br />language‑service** | The [Angular language service](guide/language-service) analyzes component templates and provides type and error information that TypeScript-aware editors can use to improve the developer's experience. For example, see the [Angular language service extension for VS Code](https://marketplace.visualstudio.com/items?itemName=Angular.ng-template).
|
|
||||||
**@types/... ** | TypeScript definition files for 3rd party libraries such as Jasmine and Node.js.
|
**@types/... ** | TypeScript definition files for 3rd party libraries such as Jasmine and Node.js.
|
||||||
[**codelyzer**](https://www.npmjs.com/package/codelyzer) | A linter for Angular apps whose rules conform to the Angular [style guide](guide/styleguide).
|
[**codelyzer**](https://www.npmjs.com/package/codelyzer) | A linter for Angular apps whose rules conform to the Angular [style guide](guide/styleguide).
|
||||||
**jasmine/... ** | Packages to support the [Jasmine](https://jasmine.github.io/) test library.
|
**jasmine/... ** | Packages to support the [Jasmine](https://jasmine.github.io/) test library.
|
||||||
@ -135,3 +134,4 @@ Package name | Description
|
|||||||
|
|
||||||
* [Building and serving](guide/build) describes how packages come together to create a development build.
|
* [Building and serving](guide/build) describes how packages come together to create a development build.
|
||||||
* [Deployment](guide/deployment) describes how packages come together to create a production build.
|
* [Deployment](guide/deployment) describes how packages come together to create a production build.
|
||||||
|
|
@ -12,7 +12,7 @@ Angular provides an `EventEmitter` class that is used when publishing values fro
|
|||||||
`EventEmitter` extends [RxJS `Subject`](https://rxjs.dev/api/index/class/Subject), adding an `emit()` method so it can send arbitrary values.
|
`EventEmitter` extends [RxJS `Subject`](https://rxjs.dev/api/index/class/Subject), adding an `emit()` method so it can send arbitrary values.
|
||||||
When you call `emit()`, it passes the emitted value to the `next()` method of any subscribed observer.
|
When you call `emit()`, it passes the emitted value to the `next()` method of any subscribed observer.
|
||||||
|
|
||||||
A good example of usage can be found in the [EventEmitter](https://angular.io/api/core/EventEmitter) documentation. Here is the example component that listens for open and close events:
|
A good example of usage can be found in the [EventEmitter](api/core/EventEmitter) documentation. Here is the example component that listens for open and close events:
|
||||||
|
|
||||||
`<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy>`
|
`<zippy (open)="onOpen($event)" (close)="onClose($event)"></zippy>`
|
||||||
|
|
||||||
@ -30,7 +30,7 @@ Angular’s `HttpClient` returns observables from HTTP method calls. For instanc
|
|||||||
|
|
||||||
## Async pipe
|
## Async pipe
|
||||||
|
|
||||||
The [AsyncPipe](https://angular.io/api/common/AsyncPipe) subscribes to an observable or promise and returns the latest value it has emitted. When a new value is emitted, the pipe marks the component to be checked for changes.
|
The [AsyncPipe](api/common/AsyncPipe) subscribes to an observable or promise and returns the latest value it has emitted. When a new value is emitted, the pipe marks the component to be checked for changes.
|
||||||
|
|
||||||
The following example binds the `time` observable to the component's view. The observable continuously updates the view with the current time.
|
The following example binds the `time` observable to the component's view. The observable continuously updates the view with the current time.
|
||||||
|
|
||||||
@ -38,16 +38,16 @@ The following example binds the `time` observable to the component's view. The o
|
|||||||
|
|
||||||
## Router
|
## Router
|
||||||
|
|
||||||
[`Router.events`](https://angular.io/api/router/Router#events) provides events as observables. You can use the `filter()` operator from RxJS to look for events of interest, and subscribe to them in order to make decisions based on the sequence of events in the navigation process. Here's an example:
|
[`Router.events`](api/router/Router#events) provides events as observables. You can use the `filter()` operator from RxJS to look for events of interest, and subscribe to them in order to make decisions based on the sequence of events in the navigation process. Here's an example:
|
||||||
|
|
||||||
<code-example path="observables-in-angular/src/main.ts" header="Router events" region="router"></code-example>
|
<code-example path="observables-in-angular/src/main.ts" header="Router events" region="router"></code-example>
|
||||||
|
|
||||||
The [ActivatedRoute](https://angular.io/api/router/ActivatedRoute) is an injected router service that makes use of observables to get information about a route path and parameters. For example, `ActivatedRoute.url` contains an observable that reports the route path or paths. Here's an example:
|
The [ActivatedRoute](api/router/ActivatedRoute) is an injected router service that makes use of observables to get information about a route path and parameters. For example, `ActivatedRoute.url` contains an observable that reports the route path or paths. Here's an example:
|
||||||
|
|
||||||
<code-example path="observables-in-angular/src/main.ts" header="ActivatedRoute" region="activated_route"></code-example>
|
<code-example path="observables-in-angular/src/main.ts" header="ActivatedRoute" region="activated_route"></code-example>
|
||||||
|
|
||||||
## Reactive forms
|
## Reactive forms
|
||||||
|
|
||||||
Reactive forms have properties that use observables to monitor form control values. The [`FormControl`](https://angular.io/api/forms/FormControl) properties `valueChanges` and `statusChanges` contain observables that raise change events. Subscribing to an observable form-control property is a way of triggering application logic within the component class. For example:
|
Reactive forms have properties that use observables to monitor form control values. The [`FormControl`](api/forms/FormControl) properties `valueChanges` and `statusChanges` contain observables that raise change events. Subscribing to an observable form-control property is a way of triggering application logic within the component class. For example:
|
||||||
|
|
||||||
<code-example path="observables-in-angular/src/main.ts" header="Reactive forms" region="forms"></code-example>
|
<code-example path="observables-in-angular/src/main.ts" header="Reactive forms" region="forms"></code-example>
|
||||||
|
@ -1,607 +1,440 @@
|
|||||||
# Pipes
|
# Transforming Data Using Pipes
|
||||||
|
|
||||||
Every application starts out with what seems like a simple task: get data, transform them, and show them to users.
|
Use [pipes](guide/glossary#pipe "Definition of a pipe") to transform and format strings, currency amounts, dates, and other display data.
|
||||||
Getting data could be as simple as creating a local variable or as complex as streaming data over a WebSocket.
|
Pipes are simple functions you can use in [template expressions](/guide/glossary#template-expression "Definition of template expression") to accept an input value and return a transformed value.
|
||||||
|
For example, you would use a pipe to show a date as **April 15, 1988** rather than the raw string format.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
For the sample app that this page describes, see the <live-example></live-example>.
|
For the sample app used in this topic, see the <live-example></live-example>.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Once data arrives, you could push their raw `toString` values directly to the view,
|
Angular provides built-in pipes for typical data transformations, including transformations for internationalization (i18n), which use locale information to format data.
|
||||||
but that rarely makes for a good user experience.
|
The following are commonly used built-in pipes for data formatting:
|
||||||
For example, in most use cases, users prefer to see a date in a simple format like
|
|
||||||
<samp>April 15, 1988</samp> rather than the raw string format
|
|
||||||
<samp>Fri Apr 15 1988 00:00:00 GMT-0700 (Pacific Daylight Time)</samp>.
|
|
||||||
|
|
||||||
Clearly, some values benefit from a bit of editing. You may notice that you
|
|
||||||
desire many of the same transformations repeatedly, both within and across many applications.
|
|
||||||
You can almost think of them as styles.
|
|
||||||
In fact, you might like to apply them in your HTML templates as you do styles.
|
|
||||||
|
|
||||||
Introducing Angular pipes, a way to write display-value transformations that you can declare in your HTML.
|
|
||||||
|
|
||||||
|
|
||||||
## Using pipes
|
|
||||||
|
|
||||||
A pipe takes in data as input and transforms it to a desired output.
|
|
||||||
In this page, you'll use pipes to transform a component's birthday property into
|
|
||||||
a human-friendly date.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/hero-birthday1.component.ts" header="src/app/hero-birthday1.component.ts"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Focus on the component's template.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/app.component.html" region="hero-birthday-template" header="src/app/app.component.html"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Inside the interpolation expression, you flow the component's `birthday` value through the
|
|
||||||
[pipe operator](guide/template-syntax#pipe) ( | ) to the [Date pipe](api/common/DatePipe)
|
|
||||||
function on the right. All pipes work this way.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Built-in pipes
|
|
||||||
Angular comes with a stock of pipes such as
|
|
||||||
`DatePipe`, `UpperCasePipe`, `LowerCasePipe`, `CurrencyPipe`, and `PercentPipe`.
|
|
||||||
They are all available for use in any template.
|
|
||||||
|
|
||||||
|
* [`DatePipe`](api/common/DatePipe): Formats a date value according to locale rules.
|
||||||
|
* [`UpperCasePipe`](api/common/UpperCasePipe): Transforms text to all upper case.
|
||||||
|
* [`LowerCasePipe`](api/common/LowerCasePipe): Transforms text to all lower case.
|
||||||
|
* [`CurrencyPipe`](api/common/CurrencyPipe): Transforms a number to a currency string, formatted according to locale rules.
|
||||||
|
* [`DecimalPipe`](/api/common/DecimalPipe): Transforms a number into a string with a decimal point, formatted according to locale rules.
|
||||||
|
* [`PercentPipe`](api/common/PercentPipe): Transforms a number to a percentage string, formatted according to locale rules.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
* For a complete list of built-in pipes, see the [pipes API documentation](/api/common#pipes "Pipes API reference summary").
|
||||||
|
* To learn more about using pipes for internationalization (i18n) efforts, see [formatting data based on locale](/guide/i18n#i18n-pipes "Formatting data based on locale").
|
||||||
Read more about these and many other built-in pipes in the [pipes topics](api?type=pipe) of the
|
|
||||||
[API Reference](api); filter for entries that include the word "pipe".
|
|
||||||
|
|
||||||
Angular doesn't have a `FilterPipe` or an `OrderByPipe` for reasons explained in the [Appendix](guide/pipes#no-filter-pipe) of this page.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
You can also create pipes to encapsulate custom transformations and use your custom pipes in template expressions.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
To use pipes you should have a basic understanding of the following:
|
||||||
|
|
||||||
## Parameterizing a pipe
|
* [Typescript](guide/glossary#typescript "Definition of Typescript") and HTML5 programming
|
||||||
|
* [Templates](guide/glossary#template "Definition of a template") in HTML with CSS styles
|
||||||
|
* [Components](guide/glossary#component "Definition of a component")
|
||||||
|
|
||||||
A pipe can accept any number of optional parameters to fine-tune its output.
|
## Using a pipe in a template
|
||||||
To add parameters to a pipe, follow the pipe name with a colon ( : ) and then the parameter value
|
|
||||||
(such as `currency:'EUR'`). If the pipe accepts multiple parameters, separate the values with colons (such as `slice:1:5`)
|
|
||||||
|
|
||||||
Modify the birthday template to give the date pipe a format parameter.
|
To apply a pipe, use the pipe operator (`|`) within a template expression as shown in the following code example, along with the *name* of the pipe, which is `date` for the built-in [`DatePipe`](api/common/DatePipe).
|
||||||
After formatting the hero's April 15th birthday, it renders as **<samp>04/15/88</samp>**:
|
The tabs in the example show the following:
|
||||||
|
|
||||||
|
* `app.component.html` uses `date` in a separate template to display a birthday.
|
||||||
|
* `hero-birthday1.component.ts` uses the same pipe as part of an in-line template in a component that also sets the birthday value.
|
||||||
|
|
||||||
<code-example path="pipes/src/app/app.component.html" region="format-birthday" header="src/app/app.component.html"></code-example>
|
<code-tabs>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/app.component.html"
|
||||||
|
region="hero-birthday-template"
|
||||||
|
path="pipes/src/app/app.component.html">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/hero-birthday1.component.ts"
|
||||||
|
path="pipes/src/app/hero-birthday1.component.ts">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
|
The component's `birthday` value flows through the
|
||||||
|
[pipe operator](guide/template-syntax#pipe) ( | ) to the [`date`](api/common/DatePipe)
|
||||||
|
function.
|
||||||
|
|
||||||
|
{@a parameterizing-a-pipe}
|
||||||
|
|
||||||
The parameter value can be any valid template expression,
|
## Formatting data with parameters and chained pipes
|
||||||
(see the [Template expressions](guide/template-syntax#template-expressions) section of the
|
|
||||||
[Template Syntax](guide/template-syntax) page)
|
|
||||||
such as a string literal or a component property.
|
|
||||||
In other words, you can control the format through a binding the same way you control the birthday value through a binding.
|
|
||||||
|
|
||||||
Write a second component that *binds* the pipe's format parameter
|
Use optional parameters to fine-tune a pipe's output.
|
||||||
to the component's `format` property. Here's the template for that component:
|
For example, you can use the [`CurrencyPipe`](api/common/CurrencyPipe "API reference") with a country code such as EUR as a parameter.
|
||||||
|
The template expression `{{ amount | currency:'EUR' }}` transforms the `amount` to currency in euros.
|
||||||
|
Follow the pipe name (`currency`) with a colon (`:`) and the parameter value (`'EUR'`).
|
||||||
|
|
||||||
|
If the pipe accepts multiple parameters, separate the values with colons.
|
||||||
|
For example, `{{ amount | currency:'EUR':'Euros '}}` adds the second parameter, the string literal `'Euros '`, to the output string. You can use any valid template expression as a parameter, such as a string literal or a component property.
|
||||||
|
|
||||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="template" header="src/app/hero-birthday2.component.ts (template)"></code-example>
|
Some pipes require at least one parameter and allow more optional parameters, such as [`SlicePipe`](/api/common/SlicePipe "API reference for SlicePipe"). For example, `{{ slice:1:5 }}` creates a new array or string containing a subset of the elements starting with element `1` and ending with element `5`.
|
||||||
|
|
||||||
|
### Example: Formatting a date
|
||||||
|
|
||||||
|
The tabs in the following example demonstrates toggling between two different formats (`'shortDate'` and `'fullDate'`):
|
||||||
|
|
||||||
You also added a button to the template and bound its click event to the component's `toggleFormat()` method.
|
* The `app.component.html` template uses a format parameter for the [`DatePipe`](api/common/DatePipe) (named `date`) to show the date as **04/15/88**.
|
||||||
That method toggles the component's `format` property between a short form
|
* The `hero-birthday2.component.ts` component binds the pipe's format parameter to the component's `format` property in the `template` section, and adds a button for a click event bound to the component's `toggleFormat()` method.
|
||||||
|
* The `hero-birthday2.component.ts` component's `toggleFormat()` method toggles the component's `format` property between a short form
|
||||||
(`'shortDate'`) and a longer form (`'fullDate'`).
|
(`'shortDate'`) and a longer form (`'fullDate'`).
|
||||||
|
|
||||||
|
<code-tabs>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/app.component.html"
|
||||||
|
region="format-birthday"
|
||||||
|
path="pipes/src/app/app.component.html">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/hero-birthday2.component.ts (template)"
|
||||||
|
region="template"
|
||||||
|
path="pipes/src/app/hero-birthday2.component.ts">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/hero-birthday2.component.ts (class)"
|
||||||
|
region="class"
|
||||||
|
path="pipes/src/app/hero-birthday2.component.ts">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
<code-example path="pipes/src/app/hero-birthday2.component.ts" region="class" header="src/app/hero-birthday2.component.ts (class)"></code-example>
|
Clicking the **Toggle Format** button alternates the date format between **04/15/1988** and **Friday, April 15, 1988** as shown in Figure 1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
As you click the button, the displayed date alternates between
|
|
||||||
"**<samp>04/15/1988</samp>**" and
|
|
||||||
"**<samp>Friday, April 15, 1988</samp>**".
|
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
|
<img src='generated/images/guide/pipes/date-format-toggle-anim.gif' alt="Date Format Toggle">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
**Figure 1.** Clicking the button toggles the date format
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
For `date` pipe format options, see [DatePipe](api/common/DatePipe "DatePipe API Reference page").
|
||||||
|
|
||||||
Read more about the `DatePipe` format options in the [Date Pipe](api/common/DatePipe)
|
|
||||||
API Reference page.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
### Example: Applying two formats by chaining pipes
|
||||||
|
|
||||||
|
You can chain pipes so that the output of one pipe becomes the input to the next.
|
||||||
|
|
||||||
## Chaining pipes
|
In the following example, chained pipes first apply a format to a date value, then convert the formatted date to uppercase characters.
|
||||||
|
The first tab for the `src/app/app.component.html` template chains `DatePipe` and `UpperCasePipe` to display the birthday as **APR 15, 1988**.
|
||||||
|
The second tab for the `src/app/app.component.html` template passes the `fullDate` parameter to `date` before chaining to `uppercase`, which produces **FRIDAY, APRIL 15, 1988**.
|
||||||
|
|
||||||
You can chain pipes together in potentially useful combinations.
|
<code-tabs>
|
||||||
In the following example, to display the birthday in uppercase,
|
<code-pane
|
||||||
the birthday is chained to the `DatePipe` and on to the `UpperCasePipe`.
|
header="src/app/app.component.html (1)"
|
||||||
The birthday displays as **<samp>APR 15, 1988</samp>**.
|
region="chained-birthday"
|
||||||
|
path="pipes/src/app/app.component.html">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/app.component.html (2)"
|
||||||
|
region="chained-parameter-birthday"
|
||||||
|
path="pipes/src/app/app.component.html">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
|
{@a Custom-pipes}
|
||||||
|
|
||||||
<code-example path="pipes/src/app/app.component.html" region="chained-birthday" header="src/app/app.component.html"></code-example>
|
## Creating pipes for custom data transformations
|
||||||
|
|
||||||
|
Create custom pipes to encapsulate transformations that are not provided with the built-in pipes.
|
||||||
|
You can then use your custom pipe in template expressions, the same way you use built-in pipes—to transform input values to output values for display.
|
||||||
|
|
||||||
|
### Marking a class as a pipe
|
||||||
|
|
||||||
This example—which displays **<samp>FRIDAY, APRIL 15, 1988</samp>**—chains
|
To mark a class as a pipe and supply configuration metadata, apply the [`@Pipe`](/api/core/Pipe "API reference for Pipe") [decorator](/guide/glossary#decorator--decoration "Definition for decorator") to the class.
|
||||||
the same pipes as above, but passes in a parameter to `date` as well.
|
Use [UpperCamelCase](guide/glossary#case-types "Definition of case types") (the general convention for class names) for the pipe class name, and [camelCase](guide/glossary#case-types "Definition of case types") for the corresponding `name` string.
|
||||||
|
Do not use hyphens in the `name`.
|
||||||
|
For details and more examples, see [Pipe names](guide/styleguide#pipe-names "Pipe names in the Angular coding style guide").
|
||||||
|
|
||||||
|
Use `name` in template expressions as you would for a built-in pipe.
|
||||||
|
|
||||||
<code-example path="pipes/src/app/app.component.html" region="chained-parameter-birthday" header="src/app/app.component.html"></code-example>
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
* Include your pipe in the `declarations` field of the `NgModule` metadata in order for it to be available to a template. See the `app.module.ts` file in the example app (<live-example></live-example>). For details, see [NgModules](guide/ngmodules "NgModules introduction").
|
||||||
|
* Register your custom pipes. The [Angular CLI](cli "CLI Overview and Command Reference") [`ng generate pipe`](cli/generate#pipe "ng generate pipe in the CLI Command Reference") command registers the pipe automatically.
|
||||||
|
|
||||||
## Custom pipes
|
|
||||||
|
|
||||||
You can write your own custom pipes.
|
|
||||||
Here's a custom pipe named `ExponentialStrengthPipe` that can boost a hero's powers:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/exponential-strength.pipe.ts" header="src/app/exponential-strength.pipe.ts"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
This pipe definition reveals the following key points:
|
|
||||||
|
|
||||||
* A pipe is a class decorated with pipe metadata.
|
|
||||||
* The pipe class implements the `PipeTransform` interface's `transform` method that
|
|
||||||
accepts an input value followed by optional parameters and returns the transformed value.
|
|
||||||
* There will be one additional argument to the `transform` method for each parameter passed to the pipe.
|
|
||||||
Your pipe has one such parameter: the `exponent`.
|
|
||||||
* To tell Angular that this is a pipe, you apply the
|
|
||||||
`@Pipe` decorator, which you import from the core Angular library.
|
|
||||||
* The `@Pipe` decorator allows you to define the
|
|
||||||
pipe name that you'll use within template expressions. It must be a valid JavaScript identifier.
|
|
||||||
Your pipe's name is `exponentialStrength`.
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## The *PipeTransform* interface
|
|
||||||
|
|
||||||
The `transform` method is essential to a pipe.
|
|
||||||
The `PipeTransform` *interface* defines that method and guides both tooling and the compiler.
|
|
||||||
Technically, it's optional; Angular looks for and executes the `transform` method regardless.
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
Now you need a component to demonstrate the pipe.
|
### Using the PipeTransform interface
|
||||||
|
|
||||||
<code-example path="pipes/src/app/power-booster.component.ts" header="src/app/power-booster.component.ts"></code-example>
|
Implement the [`PipeTransform`](/api/core/PipeTransform "API reference for PipeTransform") interface in your custom pipe class to perform the transformation.
|
||||||
|
|
||||||
|
Angular invokes the `transform` method with the value of a binding as the first argument, and any parameters as the second argument in list form, and returns the transformed value.
|
||||||
|
|
||||||
|
### Example: Transforming a value exponentially
|
||||||
|
|
||||||
|
In a game, you may want to implement a transformation that raises a value exponentially to increase a hero's power.
|
||||||
|
For example, if the hero's score is 2, boosting the hero's power exponentially by 10 produces a score of 1024.
|
||||||
|
You can use a custom pipe for this transformation.
|
||||||
|
|
||||||
|
The following code example shows two component definitions:
|
||||||
|
|
||||||
|
* The `exponential-strength.pipe.ts` component defines a custom pipe named `exponentialStrength` with the `transform` method that performs the transformation.
|
||||||
|
It defines an argument to the `transform` method (`exponent`) for a parameter passed to the pipe.
|
||||||
|
|
||||||
|
* The `power-booster.component.ts` component demonstrates how to use the pipe, specifying a value (`2`) and the exponent parameter (`10`).
|
||||||
|
Figure 2 shows the output.
|
||||||
|
|
||||||
|
<code-tabs>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/exponential-strength.pipe.ts"
|
||||||
|
path="pipes/src/app/exponential-strength.pipe.ts">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/power-booster.component.ts"
|
||||||
|
path="pipes/src/app/power-booster.component.ts">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
|
<img src='generated/images/guide/pipes/power-booster.png' alt="Power Booster">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
**Figure 2.** Output from the `exponentialStrength` pipe
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
Note the following:
|
To examine the behavior the `exponentialStrength` pipe in the <live-example></live-example>, change the value and optional exponent in the template.
|
||||||
|
|
||||||
* You use your custom pipe the same way you use built-in pipes.
|
|
||||||
* You must include your pipe in the `declarations` array of the `AppModule`
|
|
||||||
* If you choose to inject your pipe into a class, you must provide it in the `providers` array of your `NgModule`.
|
|
||||||
|
|
||||||
<div class="callout is-helpful">
|
|
||||||
|
|
||||||
<header>
|
|
||||||
Remember the declarations array
|
|
||||||
</header>
|
|
||||||
|
|
||||||
|
|
||||||
You must register custom pipes.
|
|
||||||
If you don't, Angular reports an error.
|
|
||||||
The [Angular CLI's](cli) generator registers the pipe automatically.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{@a change-detection}
|
||||||
|
|
||||||
|
## Detecting changes with data binding in pipes
|
||||||
|
|
||||||
To probe the behavior in the <live-example></live-example>,
|
You use [data binding](/guide/glossary#data-binding "Definition of data binding") with a pipe to display values and respond to user actions.
|
||||||
change the value and optional exponent in the template.
|
If the data is a primitive input value, such as `String` or `Number`, or an object reference as input, such as `Date` or `Array`, Angular executes the pipe whenever it detects a change for the input value or reference.
|
||||||
|
|
||||||
## Power Boost Calculator
|
|
||||||
|
|
||||||
It's not much fun updating the template to test the custom pipe.
|
|
||||||
Upgrade the example to a "Power Boost Calculator" that combines
|
|
||||||
your pipe and two-way data binding with `ngModel`.
|
|
||||||
|
|
||||||
|
For example, you could change the previous custom pipe example to use two-way data binding with `ngModel` to input the amount and boost factor, as shown in the following code example.
|
||||||
|
|
||||||
<code-example path="pipes/src/app/power-boost-calculator.component.ts" header="src/app/power-boost-calculator.component.ts">
|
<code-example path="pipes/src/app/power-boost-calculator.component.ts" header="src/app/power-boost-calculator.component.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
The `exponentialStrength` pipe executes every time the user changes the "normal power" value or the "boost factor", as shown in Figure 3.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src='generated/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator">
|
<img src='generated/images/guide/pipes/power-boost-calculator-anim.gif' alt="Power Boost Calculator">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
**Figure 3.** Changing the amount and boost factor for the `exponentialStrength` pipe
|
||||||
|
|
||||||
|
Angular detects each change and immediately runs the pipe.
|
||||||
|
This is fine for primitive input values.
|
||||||
|
However, if you change something *inside* a composite object (such as the month of a date, an element of an array, or an object property), you need to understand how change detection works, and how to use an `impure` pipe.
|
||||||
|
|
||||||
|
### How change detection works
|
||||||
|
|
||||||
{@a change-detection}
|
Angular looks for changes to data-bound values in a [change detection](guide/glossary#change-detection "Definition of change detection") process that runs after every DOM event: every keystroke, mouse move, timer tick, and server response.
|
||||||
|
The following example, which doesn't use a pipe, demonstrates how Angular uses its default change detection strategy to monitor and update its display of every hero in the `heroes` array.
|
||||||
|
The example tabs show the following:
|
||||||
|
|
||||||
|
* In the `flying-heroes.component.html (v1)` template, the `*ngFor` repeater displays the hero names.
|
||||||
|
* Its companion component class `flying-heroes.component.ts (v1)` provides heroes, adds heroes into the array, and resets the array.
|
||||||
|
|
||||||
## Pipes and change detection
|
<code-tabs>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/flying-heroes.component.html (v1)"
|
||||||
|
region="template-1"
|
||||||
|
path="pipes/src/app/flying-heroes.component.html">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/flying-heroes.component.ts (v1)"
|
||||||
|
region="v1"
|
||||||
|
path="pipes/src/app/flying-heroes.component.ts">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
Angular looks for changes to data-bound values through a *change detection* process that runs after every DOM event:
|
Angular updates the display every time the user adds a hero.
|
||||||
every keystroke, mouse move, timer tick, and server response. This could be expensive.
|
If the user clicks the **Reset** button, Angular replaces `heroes` with a new array of the original heroes and updates the display.
|
||||||
Angular strives to lower the cost whenever possible and appropriate.
|
If you add the ability to remove or change a hero, Angular would detect those changes and update the display as well.
|
||||||
|
|
||||||
Angular picks a simpler, faster change detection algorithm when you use a pipe.
|
However, executing a pipe to update the display with every change would slow down your app's performance.
|
||||||
|
So Angular uses a faster change-detection algorithm for executing a pipe, as described in the next section.
|
||||||
|
|
||||||
<h3 class="no-toc">No pipe</h3>
|
{@a pure-and-impure-pipes}
|
||||||
|
|
||||||
In the next example, the component uses the default, aggressive change detection strategy to monitor and update
|
### Detecting pure changes to primitives and object references
|
||||||
its display of every hero in the `heroes` array. Here's the template:
|
|
||||||
|
|
||||||
|
By default, pipes are defined as *pure* so that Angular executes the pipe only when it detects a *pure change* to the input value.
|
||||||
|
A pure change is either a change to a primitive input value (such as `String`, `Number`, `Boolean`, or `Symbol`), or a changed object reference (such as `Date`, `Array`, `Function`, or `Object`).
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-1" header="src/app/flying-heroes.component.html (v1)"></code-example>
|
{@a pure-pipe-pure-fn}
|
||||||
|
|
||||||
|
A pure pipe must use a pure function, which is one that processes inputs and returns values without side effects.
|
||||||
|
In other words, given the same input, a pure function should always return the same output.
|
||||||
|
|
||||||
|
With a pure pipe, Angular ignores changes within composite objects, such as a newly added element of an existing array, because checking a primitive value or object reference is much faster than performing a deep check for differences within objects.
|
||||||
|
Angular can quickly determine if it can skip executing the pipe and updating the view.
|
||||||
|
|
||||||
The companion component class provides heroes, adds heroes into the array, and can reset the array.
|
However, a pure pipe with an array as input may not work the way you want.
|
||||||
|
To demonstrate this issue, change the previous example to filter the list of heroes to just those heroes who can fly.
|
||||||
|
Use the `FlyingHeroesPipe` in the `*ngFor` repeater as shown in the following code.
|
||||||
|
The tabs for the example show the following:
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="v1" header="src/app/flying-heroes.component.ts (v1)"></code-example>
|
* The template (`flying-heroes.component.html (flyers)`) with the new pipe.
|
||||||
|
* The `FlyingHeroesPipe` custom pipe implementation (`flying-heroes.pipe.ts`).
|
||||||
|
|
||||||
|
<code-tabs>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/flying-heroes.component.html (flyers)"
|
||||||
|
region="template-flying-heroes"
|
||||||
|
path="pipes/src/app/flying-heroes.component.html">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/flying-heroes.pipe.ts"
|
||||||
|
region="pure"
|
||||||
|
path="pipes/src/app/flying-heroes.pipe.ts">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
|
The app now shows unexpected behavior: When the user adds flying heroes, none of them appear under "Heroes who fly."
|
||||||
You can add heroes and Angular updates the display when you do.
|
This happens because the code that adds a hero does so by pushing it onto the `heroes` array:
|
||||||
If you click the `reset` button, Angular replaces `heroes` with a new array of the original heroes and updates the display.
|
|
||||||
If you added the ability to remove or change a hero, Angular would detect those changes and update the display as well.
|
|
||||||
|
|
||||||
<h3 class="no-toc"><i>FlyingHeroesPipe</i></h3>
|
|
||||||
|
|
||||||
Add a `FlyingHeroesPipe` to the `*ngFor` repeater that filters the list of heroes to just those heroes who can fly.
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.component.html" region="template-flying-heroes" header="src/app/flying-heroes.component.html (flyers)"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Here's the `FlyingHeroesPipe` implementation, which follows the pattern for custom pipes described earlier.
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pure" header="src/app/flying-heroes.pipe.ts"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Notice the odd behavior in the <live-example></live-example>:
|
|
||||||
when you add flying heroes, none of them are displayed under "Heroes who fly."
|
|
||||||
|
|
||||||
Although you're not getting the behavior you want, Angular isn't broken.
|
|
||||||
It's just using a different change-detection algorithm that ignores changes to the list or any of its items.
|
|
||||||
|
|
||||||
Notice how a hero is added:
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts"></code-example>
|
<code-example path="pipes/src/app/flying-heroes.component.ts" region="push" header="src/app/flying-heroes.component.ts"></code-example>
|
||||||
|
|
||||||
|
The change detector ignores changes to elements of an array, so the pipe doesn't run.
|
||||||
|
|
||||||
|
The reason Angular ignores the changed array element is that the *reference* to the array hasn't changed.
|
||||||
|
Since the array is the same, Angular does not update the display.
|
||||||
|
|
||||||
You add the hero into the `heroes` array. The reference to the array hasn't changed.
|
One way to get the behavior you want is to change the object reference itself.
|
||||||
It's the same array. That's all Angular cares about. From its perspective, *same array, no change, no display update*.
|
You can replace the array with a new array containing the newly changed elements, and then input the new array to the pipe.
|
||||||
|
In the above example, you can create an array with the new hero appended, and assign that to `heroes`. Angular detects the change in the array reference and executes the pipe.
|
||||||
To fix that, create an array with the new hero appended and assign that to `heroes`.
|
|
||||||
This time Angular detects that the array reference has changed.
|
|
||||||
It executes the pipe and updates the display with the new array, which includes the new flying hero.
|
|
||||||
|
|
||||||
If you *mutate* the array, no pipe is invoked and the display isn't updated;
|
|
||||||
if you *replace* the array, the pipe executes and the display is updated.
|
|
||||||
The Flying Heroes application extends the
|
|
||||||
code with checkbox switches and additional displays to help you experience these effects.
|
|
||||||
|
|
||||||
|
To summarize, if you mutate the input array, the pure pipe doesn't execute.
|
||||||
|
If you *replace* the input array, the pipe executes and the display is updated, as shown in Figure 4.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
|
<img src='generated/images/guide/pipes/flying-heroes-anim.gif' alt="Flying Heroes">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
**Figure 4.** The `flyingHeroes` pipe filtering the display to flying heroes
|
||||||
|
|
||||||
|
The above example demonstrates changing a component's code to accommodate a pipe.
|
||||||
|
|
||||||
Replacing the array is an efficient way to signal Angular to update the display.
|
To keep your component simpler and independent of HTML templates that use pipes, you can, as an alternative, use an *impure* pipe to detect changes within composite objects such as arrays, as described in the next section.
|
||||||
When do you replace the array? When the data changes.
|
|
||||||
That's an easy rule to follow in *this* example
|
|
||||||
where the only way to change the data is by adding a hero.
|
|
||||||
|
|
||||||
More often, you don't know when the data has changed,
|
|
||||||
especially in applications that mutate data in many ways,
|
|
||||||
perhaps in application locations far away.
|
|
||||||
A component in such an application usually can't know about those changes.
|
|
||||||
Moreover, it's unwise to distort the component design to accommodate a pipe.
|
|
||||||
Strive to keep the component class independent of the HTML.
|
|
||||||
The component should be unaware of pipes.
|
|
||||||
|
|
||||||
For filtering flying heroes, consider an *impure pipe*.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Pure and impure pipes
|
|
||||||
|
|
||||||
There are two categories of pipes: *pure* and *impure*.
|
|
||||||
Pipes are pure by default. Every pipe you've seen so far has been pure.
|
|
||||||
You make a pipe impure by setting its pure flag to false. You could make the `FlyingHeroesPipe`
|
|
||||||
impure like this:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts"></code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Before doing that, understand the difference between pure and impure, starting with a pure pipe.
|
|
||||||
|
|
||||||
<h3 class="no-toc">Pure pipes</h3>
|
|
||||||
|
|
||||||
Angular executes a *pure pipe* only when it detects a *pure change* to the input value.
|
|
||||||
A pure change is either a change to a primitive input value (`String`, `Number`, `Boolean`, `Symbol`)
|
|
||||||
or a changed object reference (`Date`, `Array`, `Function`, `Object`).
|
|
||||||
|
|
||||||
Angular ignores changes within (composite) objects.
|
|
||||||
It won't call a pure pipe if you change an input month, add to an input array, or update an input object property.
|
|
||||||
|
|
||||||
This may seem restrictive but it's also fast.
|
|
||||||
An object reference check is fast—much faster than a deep check for
|
|
||||||
differences—so Angular can quickly determine if it can skip both the
|
|
||||||
pipe execution and a view update.
|
|
||||||
|
|
||||||
For this reason, a pure pipe is preferable when you can live with the change detection strategy.
|
|
||||||
When you can't, you *can* use the impure pipe.
|
|
||||||
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Or you might not use a pipe at all.
|
|
||||||
It may be better to pursue the pipe's purpose with a property of the component,
|
|
||||||
a point that's discussed later in this page.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<h3 class="no-toc">Impure pipes</h3>
|
|
||||||
|
|
||||||
Angular executes an *impure pipe* during every component change detection cycle.
|
|
||||||
An impure pipe is called often, as often as every keystroke or mouse-move.
|
|
||||||
|
|
||||||
With that concern in mind, implement an impure pipe with great care.
|
|
||||||
An expensive, long-running pipe could destroy the user experience.
|
|
||||||
|
|
||||||
|
|
||||||
{@a impure-flying-heroes}
|
{@a impure-flying-heroes}
|
||||||
|
|
||||||
|
### Detecting impure changes within composite objects
|
||||||
|
|
||||||
<h3 class="no-toc">An impure <i>FlyingHeroesPipe</i></h3>
|
To execute a custom pipe after a change *within* a composite object, such as a change to an element of an array, you need to define your pipe as `impure` to detect impure changes.
|
||||||
|
Angular executes an impure pipe every time it detects a change with every keystroke or mouse movement.
|
||||||
|
|
||||||
A flip of the switch turns the `FlyingHeroesPipe` into a `FlyingHeroesImpurePipe`.
|
<div class="alert is-important">
|
||||||
The complete implementation is as follows:
|
|
||||||
|
While an impure pipe can be useful, be careful using one. A long-running impure pipe could dramatically slow down your app.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Make a pipe impure by setting its `pure` flag to `false`:
|
||||||
|
|
||||||
|
<code-example path="pipes/src/app/flying-heroes.pipe.ts" region="pipe-decorator" header="src/app/flying-heroes.pipe.ts"></code-example>
|
||||||
|
|
||||||
|
The following code shows the complete implementation of `FlyingHeroesImpurePipe`, which extends `FlyingHeroesPipe` to inherit its characteristics.
|
||||||
|
The example shows that you don't have to change anything else—the only difference is setting the `pure` flag as `false` in the pipe metadata.
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
|
<code-pane
|
||||||
<code-pane header="FlyingHeroesImpurePipe" path="pipes/src/app/flying-heroes.pipe.ts" region="impure">
|
header="src/app/flying-heroes.pipe.ts (FlyingHeroesImpurePipe)"
|
||||||
|
region="impure"
|
||||||
|
path="pipes/src/app/flying-heroes.pipe.ts">
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
<code-pane header="FlyingHeroesPipe" path="pipes/src/app/flying-heroes.pipe.ts" region="pure">
|
header="src/app/flying-heroes.pipe.ts (FlyingHeroesPipe)"
|
||||||
|
region="pure"
|
||||||
|
path="pipes/src/app/flying-heroes.pipe.ts">
|
||||||
</code-pane>
|
</code-pane>
|
||||||
|
|
||||||
</code-tabs>
|
</code-tabs>
|
||||||
|
|
||||||
|
`FlyingHeroesImpurePipe` is a good candidate for an impure pipe because the `transform` function is trivial and fast:
|
||||||
|
|
||||||
You inherit from `FlyingHeroesPipe` to prove the point that nothing changed internally.
|
|
||||||
The only difference is the `pure` flag in the pipe metadata.
|
|
||||||
|
|
||||||
This is a good candidate for an impure pipe because the `transform` function is trivial and fast.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes.pipe.ts" header="src/app/flying-heroes.pipe.ts (filter)" region="filter"></code-example>
|
<code-example path="pipes/src/app/flying-heroes.pipe.ts" header="src/app/flying-heroes.pipe.ts (filter)" region="filter"></code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
|
You can derive a `FlyingHeroesImpureComponent` from `FlyingHeroesComponent`.
|
||||||
|
As shown in the code below, only the pipe in the template changes.
|
||||||
|
|
||||||
<code-example path="pipes/src/app/flying-heroes-impure.component.html" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes"></code-example>
|
<code-example path="pipes/src/app/flying-heroes-impure.component.html" header="src/app/flying-heroes-impure.component.html (excerpt)" region="template-flying-heroes"></code-example>
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
To confirm that the display updates as the user adds heroes, see the <live-example></live-example>.
|
||||||
|
|
||||||
The only substantive change is the pipe in the template.
|
</div>
|
||||||
You can confirm in the <live-example></live-example> that the _flying heroes_
|
|
||||||
display updates as you add heroes, even when you mutate the `heroes` array.
|
|
||||||
|
|
||||||
|
|
||||||
{@a async-pipe}
|
{@a async-pipe}
|
||||||
<h3 class="no-toc">The impure <i>AsyncPipe</i></h3>
|
|
||||||
|
|
||||||
|
## Unwrapping data from an observable
|
||||||
|
|
||||||
The Angular `AsyncPipe` is an interesting example of an impure pipe.
|
[Observables](/guide/glossary#observable "Definition of observable") let you pass messages between parts of your application.
|
||||||
The `AsyncPipe` accepts a `Promise` or `Observable` as input
|
Observables are recommended for event handling, asynchronous programming, and handling multiple values.
|
||||||
and subscribes to the input automatically, eventually returning the emitted values.
|
Observables can deliver single or multiple values of any type, either synchronously (as a function delivers a value to its caller) or asynchronously on a schedule.
|
||||||
|
|
||||||
The `AsyncPipe` is also stateful.
|
<div class="alert is-helpful">
|
||||||
The pipe maintains a subscription to the input `Observable` and
|
|
||||||
keeps delivering values from that `Observable` as they arrive.
|
|
||||||
|
|
||||||
This next example binds an `Observable` of message strings
|
For details and examples of observables, see the [Observables Overview](/guide/observables#using-observables-to-pass-values "Using observables to pass values"").
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Use the built-in [`AsyncPipe`](/api/common/AsyncPipe "API description of AsyncPipe") to accept an observable as input and subscribe to the input automatically.
|
||||||
|
Without this pipe, your component code would have to subscribe to the observable to consume its values, extract the resolved values, expose them for binding, and unsubscribe when the observable is destroyed in order to prevent memory leaks. `AsyncPipe` is an impure pipe that saves boilerplate code in your component to maintain the subscription and keep delivering values from that observable as they arrive.
|
||||||
|
|
||||||
|
The following code example binds an observable of message strings
|
||||||
(`message$`) to a view with the `async` pipe.
|
(`message$`) to a view with the `async` pipe.
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/hero-async-message.component.ts" header="src/app/hero-async-message.component.ts">
|
<code-example path="pipes/src/app/hero-async-message.component.ts" header="src/app/hero-async-message.component.ts">
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
{@a no-filter-pipe}
|
||||||
|
|
||||||
|
## Caching HTTP requests
|
||||||
|
|
||||||
The Async pipe saves boilerplate in the component code.
|
To [communicate with backend services using HTTP](/guide/http "Communicating with backend services using HTTP"), the `HttpClient` service uses observables and offers the `HTTPClient.get()` method to fetch data from a server.
|
||||||
The component doesn't have to subscribe to the async data source,
|
The aynchronous method sends an HTTP request, and returns an observable that emits the requested data for the response.
|
||||||
extract the resolved values and expose them for binding,
|
|
||||||
and have to unsubscribe when it's destroyed
|
|
||||||
(a potent source of memory leaks).
|
|
||||||
|
|
||||||
<h3 class="no-toc">An impure caching pipe</h3>
|
As shown in the previous section, you can use the impure `AsyncPipe` to accept an observable as input and subscribe to the input automatically.
|
||||||
|
You can also create an impure pipe to make and cache an HTTP request.
|
||||||
|
|
||||||
Write one more impure pipe, a pipe that makes an HTTP request.
|
Impure pipes are called whenever change detection runs for a component, which could be every few milliseconds for `CheckAlways`.
|
||||||
|
To avoid performance problems, call the server only when the requested URL changes, as shown in the following example, and use the pipe to cache the server response.
|
||||||
|
The tabs show the following:
|
||||||
|
|
||||||
Remember that impure pipes are called every few milliseconds.
|
* The `fetch` pipe (`fetch-json.pipe.ts`).
|
||||||
If you're not careful, this pipe will punish the server with requests.
|
* A harness component (`hero-list.component.ts`) for demonstrating the request, using a template that defines two bindings to the pipe requesting the heroes from the `heroes.json` file. The second binding chains the `fetch` pipe with the built-in `JsonPipe` to display the same hero data in JSON format.
|
||||||
|
|
||||||
In the following code, the pipe only calls the server when the requested URL changes and it caches the server response.
|
<code-tabs>
|
||||||
The code uses the [Angular http](guide/http) client to retrieve data:
|
<code-pane
|
||||||
|
header="src/app/fetch-json.pipe.ts"
|
||||||
|
path="pipes/src/app/fetch-json.pipe.ts">
|
||||||
|
</code-pane>
|
||||||
|
<code-pane
|
||||||
|
header="src/app/hero-list.component.ts"
|
||||||
|
path="pipes/src/app/hero-list.component.ts">
|
||||||
|
</code-pane>
|
||||||
|
</code-tabs>
|
||||||
|
|
||||||
|
In the above example, a breakpoint on the pipe's request for data shows the following:
|
||||||
|
|
||||||
<code-example path="pipes/src/app/fetch-json.pipe.ts" header="src/app/fetch-json.pipe.ts">
|
* Each binding gets its own pipe instance.
|
||||||
|
* Each pipe instance caches its own URL and data and calls the server only once.
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Now demonstrate it in a harness component whose template defines two bindings to this pipe,
|
|
||||||
both requesting the heroes from the `heroes.json` file.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example path="pipes/src/app/hero-list.component.ts" header="src/app/hero-list.component.ts">
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The component renders as the following:
|
|
||||||
|
|
||||||
|
The `fetch` and `fetch-json` pipes display the heroes as shown in Figure 5.
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
<img src='generated/images/guide/pipes/hero-list.png' alt="Hero List">
|
<img src='generated/images/guide/pipes/hero-list.png' alt="Hero List">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
**Figure 5.** The `fetch` and `fetch-json` pipes displaying the heroes
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
A breakpoint on the pipe's request for data shows the following:
|
The built-in [JsonPipe](api/common/JsonPipe "API description for JsonPipe") provides a way to diagnose a mysteriously failing data binding or to inspect an object for future binding.
|
||||||
|
|
||||||
* Each binding gets its own pipe instance.
|
|
||||||
* Each pipe instance caches its own URL and data.
|
|
||||||
* Each pipe instance only calls the server once.
|
|
||||||
|
|
||||||
<h3 class="no-toc"><i>JsonPipe</i></h3>
|
|
||||||
|
|
||||||
In the previous code sample, the second `fetch` pipe binding demonstrates more pipe chaining.
|
|
||||||
It displays the same hero data in JSON format by chaining through to the built-in `JsonPipe`.
|
|
||||||
|
|
||||||
|
|
||||||
<div class="callout is-helpful">
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<header>
|
|
||||||
Debugging with the json pipe
|
|
||||||
</header>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
The [JsonPipe](api/common/JsonPipe)
|
|
||||||
provides an easy way to diagnose a mysteriously failing data binding or
|
|
||||||
inspect an object for future binding.
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a pure-pipe-pure-fn}
|
|
||||||
|
|
||||||
|
|
||||||
<h3 class="no-toc">Pure pipes and pure functions</h3>
|
|
||||||
|
|
||||||
A pure pipe uses pure functions.
|
|
||||||
Pure functions process inputs and return values without detectable side effects.
|
|
||||||
Given the same input, they should always return the same output.
|
|
||||||
|
|
||||||
The pipes discussed earlier in this page are implemented with pure functions.
|
|
||||||
The built-in `DatePipe` is a pure pipe with a pure function implementation.
|
|
||||||
So are the `ExponentialStrengthPipe` and `FlyingHeroesPipe`.
|
|
||||||
A few steps back, you reviewed the `FlyingHeroesImpurePipe`—an impure pipe with a pure function.
|
|
||||||
|
|
||||||
But always implement a *pure pipe* with a *pure function*.
|
|
||||||
Otherwise, you'll see many console errors regarding expressions that changed after they were checked.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Next steps
|
|
||||||
|
|
||||||
Pipes are a great way to encapsulate and share common display-value
|
|
||||||
transformations. Use them like styles, dropping them
|
|
||||||
into your template's expressions to enrich the appeal and usability
|
|
||||||
of your views.
|
|
||||||
|
|
||||||
Explore Angular's inventory of built-in pipes in the [API Reference](api?type=pipe).
|
|
||||||
Try writing a custom pipe and perhaps contributing it to the community.
|
|
||||||
|
|
||||||
|
|
||||||
{@a no-filter-pipe}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Appendix: No *FilterPipe* or *OrderByPipe*
|
|
||||||
|
|
||||||
Angular doesn't provide pipes for filtering or sorting lists.
|
|
||||||
Developers familiar with AngularJS know these as `filter` and `orderBy`.
|
|
||||||
There are no equivalents in Angular.
|
|
||||||
|
|
||||||
This isn't an oversight. Angular doesn't offer such pipes because
|
|
||||||
they perform poorly and prevent aggressive minification.
|
|
||||||
Both `filter` and `orderBy` require parameters that reference object properties.
|
|
||||||
Earlier in this page, you learned that such pipes must be [impure](guide/pipes#pure-and-impure-pipes) and that
|
|
||||||
Angular calls impure pipes in almost every change-detection cycle.
|
|
||||||
|
|
||||||
Filtering and especially sorting are expensive operations.
|
|
||||||
The user experience can degrade severely for even moderate-sized lists when Angular calls these pipe methods many times per second.
|
|
||||||
`filter` and `orderBy` have often been abused in AngularJS apps, leading to complaints that Angular itself is slow.
|
|
||||||
That charge is fair in the indirect sense that AngularJS prepared this performance trap
|
|
||||||
by offering `filter` and `orderBy` in the first place.
|
|
||||||
|
|
||||||
The minification hazard is also compelling, if less obvious. Imagine a sorting pipe applied to a list of heroes.
|
|
||||||
The list might be sorted by hero `name` and `planet` of origin properties in the following way:
|
|
||||||
|
|
||||||
<code-example language="html">
|
|
||||||
<!-- NOT REAL CODE! -->
|
|
||||||
<div *ngFor="let hero of heroes | orderBy:'name,planet'"></div>
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
You identify the sort fields by text strings, expecting the pipe to reference a property value by indexing
|
|
||||||
(such as `hero['name']`).
|
|
||||||
Unfortunately, aggressive minification manipulates the `Hero` property names so that `Hero.name` and `Hero.planet`
|
|
||||||
become something like `Hero.a` and `Hero.b`. Clearly `hero['name']` doesn't work.
|
|
||||||
|
|
||||||
While some may not care to minify this aggressively,
|
|
||||||
the Angular product shouldn't prevent anyone from minifying aggressively.
|
|
||||||
Therefore, the Angular team decided that everything Angular provides will minify safely.
|
|
||||||
|
|
||||||
The Angular team and many experienced Angular developers strongly recommend moving
|
|
||||||
filtering and sorting logic into the component itself.
|
|
||||||
The component can expose a `filteredHeroes` or `sortedHeroes` property and take control
|
|
||||||
over when and how often to execute the supporting logic.
|
|
||||||
Any capabilities that you would have put in a pipe and shared across the app can be
|
|
||||||
written in a filtering/sorting service and injected into the component.
|
|
||||||
|
|
||||||
If these performance and minification considerations don't apply to you, you can always create your own such pipes
|
|
||||||
(similar to the [FlyingHeroesPipe](guide/pipes#impure-flying-heroes)) or find them in the community.
|
|
||||||
|
@ -58,8 +58,8 @@ Though you can provide services by lazy loading modules, not all services can be
|
|||||||
Another way to limit provider scope is by adding the service you want to limit to the component’s
|
Another way to limit provider scope is by adding the service you want to limit to the component’s
|
||||||
`providers` array. Component providers and NgModule providers are independent of each other. This
|
`providers` array. Component providers and NgModule providers are independent of each other. This
|
||||||
method is helpful when you want to eagerly load a module that needs a service all to itself.
|
method is helpful when you want to eagerly load a module that needs a service all to itself.
|
||||||
Providing a service in the component limits the service only to that component (other components in
|
Providing a service in the component limits the service only to that component and its descendants.
|
||||||
the same module can’t access it).
|
Other components in the same module can’t access it.
|
||||||
|
|
||||||
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts"></code-example>
|
<code-example path="providers/src/app/app.component.ts" region="component-providers" header="src/app/app.component.ts"></code-example>
|
||||||
|
|
||||||
|
@ -9,11 +9,11 @@ A basic understanding of the following concepts:
|
|||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
|
||||||
The [AnimationOptions](https://angular.io/api/animations/AnimationOptions) interface in Angular animations enables you to create animations that you can reuse across different components.
|
The [AnimationOptions](api/animations/AnimationOptions) interface in Angular animations enables you to create animations that you can reuse across different components.
|
||||||
|
|
||||||
## Creating reusable animations
|
## Creating reusable animations
|
||||||
|
|
||||||
To create a reusable animation, use the [`animation()`](https://angular.io/api/animations/animation) method to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your app components using the [`useAnimation()`](https://angular.io/api/animations/useAnimation) API.
|
To create a reusable animation, use the [`animation()`](api/animations/animation) method to define an animation in a separate `.ts` file and declare this animation definition as a `const` export variable. You can then import and reuse this animation in any of your app components using the [`useAnimation()`](api/animations/useAnimation) API.
|
||||||
|
|
||||||
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript"></code-example>
|
<code-example path="animations/src/app/animations.ts" header="src/app/animations.ts" region="reusable" language="typescript"></code-example>
|
||||||
|
|
||||||
|
262
aio/content/guide/router-tutorial.md
Normal file
262
aio/content/guide/router-tutorial.md
Normal file
@ -0,0 +1,262 @@
|
|||||||
|
# Using Angular routes in a single-page application
|
||||||
|
|
||||||
|
This tutorial describes how you can build a single-page application, SPA that uses multiple Angular routes.
|
||||||
|
|
||||||
|
|
||||||
|
In an SPA, all of your application's functions exist in a single HTML page.
|
||||||
|
As users access your application's features, the browser needs to render only the parts that matter to the user, instead of loading a new page. This pattern can significantly improve your application's user exprience.
|
||||||
|
|
||||||
|
To define how users navigate through your application, you use routes. You can add routes to define how users navigate from one part of your application to another.
|
||||||
|
You can also configure routes to guard against unexpected or unauthorized behavior.
|
||||||
|
|
||||||
|
To explore a sample app featuring the contents of this tutorial, see the <live-example></live-example>.
|
||||||
|
|
||||||
|
## Objectives
|
||||||
|
|
||||||
|
* Organize a sample application's features into modules.
|
||||||
|
* Define how to navigate to a component.
|
||||||
|
* Pass information to a component using a parameter.
|
||||||
|
* Structure routes by nesting several routes.
|
||||||
|
* Check whether users can access a route.
|
||||||
|
* Control whether the application can discard unsaved changes.
|
||||||
|
* Improve performance by pre-fetching route data and lazy loading feature modules.
|
||||||
|
* Require specific criteria to load components.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
To complete this tutorial, you should have a basic understanding of the following concepts:
|
||||||
|
|
||||||
|
* JavaScript
|
||||||
|
* HTML
|
||||||
|
* CSS
|
||||||
|
* [Angular CLI](/cli)
|
||||||
|
|
||||||
|
You might find the [Tour of Heroes tutorial](/tutorial) helpful, but it is not required.
|
||||||
|
|
||||||
|
## Create a sample application
|
||||||
|
|
||||||
|
Using the Angular CLI, create a new application, _angular-router-sample_. This application will have two components: _crisis-list_ and _heroes-list_.
|
||||||
|
|
||||||
|
1. Create a new Angular project, _angular-router-sample_.
|
||||||
|
|
||||||
|
<code-example language="sh">
|
||||||
|
ng new angular-router-sample
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
When prompted with `Would you like to add Angular routing?`, select `N`.
|
||||||
|
|
||||||
|
When prompted with `Which stylesheet format would you like to use?`, select `CSS`.
|
||||||
|
|
||||||
|
After a few moments, a new project, `angular-router-sample`, is ready.
|
||||||
|
|
||||||
|
1. From your terminal, navigate to the `angular-router-sample` directory.
|
||||||
|
|
||||||
|
1. Create a component, _crisis-list_.
|
||||||
|
|
||||||
|
<code-example language="sh">
|
||||||
|
ng generate component crisis-list
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
1. In your code editor, locate the file, `crisis-list.component.html` and replace
|
||||||
|
the placeholder content with the following HTML.
|
||||||
|
|
||||||
|
<code-example header="src/app/crisis-list/crisis-list.component.html" path="router-tutorial/src/app/crisis-list/crisis-list.component.html"></code-example>
|
||||||
|
|
||||||
|
1. Create a second component, _heroes-list_.
|
||||||
|
|
||||||
|
<code-example language="sh">
|
||||||
|
ng generate component heroes-list
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
1. In your code editor, locate the file, `heroes-list.component.html` and replace the placeholder content with the following HTML.
|
||||||
|
|
||||||
|
<code-example header="src/app/heroes-list/heroes-list.component.html" path="router-tutorial/src/app/heroes-list/heroes-list.component.html"></code-example>
|
||||||
|
|
||||||
|
1. In your code editor, open the file, `app.component.html` and replace its contents with the following HTML.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.component.html" path="router-tutorial/src/app/app.component.html" region="setup"></code-example>
|
||||||
|
|
||||||
|
1. Verify that your new application runs as expected by running the `ng serve` command.
|
||||||
|
|
||||||
|
<code-example language="sh">
|
||||||
|
ng serve
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
1. Open a browser to `http://localhost:4200`.
|
||||||
|
|
||||||
|
You should see a single web page, consisting of a title and the HTML of your two components.
|
||||||
|
|
||||||
|
## Import `RouterModule` from `@angular/router`
|
||||||
|
|
||||||
|
Routing allows you to display specific views of your application depending on the URL path.
|
||||||
|
To add this functionality to your sample application, you need to update the `app.module.ts` file to use the module, `RouterModule`.
|
||||||
|
You import this module from `@angular/router`.
|
||||||
|
|
||||||
|
1. From your code editor, open the `app.module.ts` file.
|
||||||
|
|
||||||
|
1. Add the following `import` statement.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.module.ts" path="router-tutorial/src/app/app.module.ts" region="router-import"></code-example>
|
||||||
|
|
||||||
|
## Define your routes
|
||||||
|
|
||||||
|
In this section, you'll define two routes:
|
||||||
|
|
||||||
|
* The route `/crisis-center` opens the `crisis-center` component.
|
||||||
|
* The route `/heroes-list` opens the `heroes-list` component.
|
||||||
|
|
||||||
|
A route definition is a JavaScript object. Each route typically has two propteries. The first property, `path`, is a string
|
||||||
|
that specifies the URL path for the route. The second property, `component`, is a string that specifies
|
||||||
|
what component your application should display for that path.
|
||||||
|
|
||||||
|
1. From your code editor, open the `app.module.ts` file.
|
||||||
|
|
||||||
|
1. Locate the `@NgModule()` section.
|
||||||
|
|
||||||
|
1. Replace the `imports` array in that section with the following.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.module.ts" path="router-tutorial/src/app/app.module.ts" region="import-basic"></code-example>
|
||||||
|
|
||||||
|
This code adds the `RouterModule` to the `imports` array. Next, the code uses the `forRoot()` method of the `RouterModule` to
|
||||||
|
define your two routes. This method takes an array of JavaScript objects, with each object defining the proprties of a route.
|
||||||
|
The `forRoot()` method ensures that your application only instantiates one `RouterModule`. For more information, see
|
||||||
|
[Singleton Services](/guide/singleton-services#forroot-and-the-router).
|
||||||
|
|
||||||
|
## Update your component with `router-outlet`
|
||||||
|
|
||||||
|
At this point, you have defined two routes for your application. However, your application
|
||||||
|
still has both the `crisis-list` and `heroes-list` components hard-coded in your `app.component.html` template. For your routes to
|
||||||
|
work, you need to update your template to dynamically load a component based on the URL path.
|
||||||
|
|
||||||
|
To implement this functionality, you add the `router-outlet` directive to your template file.
|
||||||
|
|
||||||
|
1. From your code editor, open the `app.component.html` file.
|
||||||
|
|
||||||
|
1. Delete the following lines.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.component.html" path="router-tutorial/src/app/app.component.html" region="components"></code-example>
|
||||||
|
|
||||||
|
1. Add the `router-outlet` directive.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.component.html" path="router-tutorial/src/app/app.component.html" region="router-outlet"></code-example>
|
||||||
|
|
||||||
|
View your updated application in your browser. You should see only the application title. To
|
||||||
|
view the `crisis-list` component, add `crisis-list` to the end of the path in your browser's
|
||||||
|
address bar. For example:
|
||||||
|
|
||||||
|
<code-example language="none">
|
||||||
|
http://localhost:4200/crisis-list
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
Notice that the `crisis-list` component displays. Angular is using the route you defined to dynamically load the
|
||||||
|
component. You can load the `heroes-list` component the same way:
|
||||||
|
|
||||||
|
<code-example language="none">
|
||||||
|
http://localhost:4200/heroes-list
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
## Control navigation with UI elements
|
||||||
|
|
||||||
|
Currently, your application supports two routes. However, the only way to use those routes
|
||||||
|
is for the user to manually type the path in the browser's address bar. In this section, you'll
|
||||||
|
add two links that users can click to navigate between the `heroes-list` and `crisis-list`
|
||||||
|
components. You'll also add some CSS styles. While these styles are not required, they make
|
||||||
|
it easier to identify the link for the currently-displayed component. You'll add that functionality
|
||||||
|
in the next section.
|
||||||
|
|
||||||
|
1. Open the `app.component.html` file and add the following HTML below the title.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.component.html" path="router-tutorial/src/app/app.component.html" region="nav"></code-example>
|
||||||
|
|
||||||
|
This HTML uses an Angular directive, `routerLink`. This directive connects the routes
|
||||||
|
you defined to your template files.
|
||||||
|
|
||||||
|
1. Open the `app.component.css` file and add the following styles.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.component.css" path="router-tutorial/src/app/app.component.css"></code-example>
|
||||||
|
|
||||||
|
|
||||||
|
If you view your application in the browser, you should see these two links. When you click
|
||||||
|
on a link, the corresponding component appears.
|
||||||
|
|
||||||
|
## Identify the active route
|
||||||
|
|
||||||
|
While users can navigate your application using the links you added in the previous section,
|
||||||
|
they don't have an easy way to identify what the active route is. You can add this functionality
|
||||||
|
using Angular's `routerLinkActive` directive.
|
||||||
|
|
||||||
|
1. From your code editor, open the `app.component.html` file.
|
||||||
|
|
||||||
|
1. Update the anchor tags to include the `routerLinkActive` directive.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.component.html" path="router-tutorial/src/app/app.component.html" region="routeractivelink"></code-example>
|
||||||
|
|
||||||
|
View your application again. As you click one of the buttons, the style for that button updates
|
||||||
|
automatically, identifying the active component to the user. By adding the `routerLinkActive`
|
||||||
|
directive, you inform your application to apply a specific CSS class to the active route. In this
|
||||||
|
tutorial, that CSS class is `activebutton`, but you could use any class that you want.
|
||||||
|
|
||||||
|
## Adding a redirect
|
||||||
|
|
||||||
|
In this step of the tutorial, you add a route that redirects the user to display the `/heroes-list` component.
|
||||||
|
|
||||||
|
1. From your code editor, open the `app.module.ts` file.
|
||||||
|
|
||||||
|
1. In the `imports` array, update the `RouterModule` section as follows.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.module.ts" path="router-tutorial/src/app/app.module.ts" region="import-redirect"></code-example>
|
||||||
|
|
||||||
|
Notice that this new route uses an empty string as its path. In addition, it replaces the `component` property with two new ones:
|
||||||
|
|
||||||
|
* `redirectTo`. This property instructs Angular to redirect from an empty path to the
|
||||||
|
`heroes-list` path.
|
||||||
|
* `pathMatch`. This property instructs Angular on how much of the URL to match. For this
|
||||||
|
tutorial, you should set this property to `full`. This strategy is recommended when
|
||||||
|
you have an empty string for a path. For more information about this property,
|
||||||
|
see the [Route API documentation](/api/router/Route).
|
||||||
|
|
||||||
|
Now when you open your application, it displays the `heroes-list` component by default.
|
||||||
|
|
||||||
|
## Adding a 404 page
|
||||||
|
|
||||||
|
It is possible for a user to try to access a route that you have not defined. To account for
|
||||||
|
this behavior, a best practice is to display a 404 page. In this section, you'll create a 404 page and
|
||||||
|
update your route configuration to show that page for any unspecified routes.
|
||||||
|
|
||||||
|
1. From the terminal, create a new component, `PageNotFound`.
|
||||||
|
|
||||||
|
<code-example language="sh">
|
||||||
|
ng generate component page-not-found
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
1. From your code editor, open the `page-not-found.component.html` file and replace its contents
|
||||||
|
with the following HTML.
|
||||||
|
|
||||||
|
<code-example header="src/app/page-not-found/page-not-found.component.html" path="router-tutorial/src/app/page-not-found/page-not-found.component.html"></code-example>
|
||||||
|
|
||||||
|
1. Open the `app.module.ts` file. In the `imports` array, update the `RouterModule` section as follows.
|
||||||
|
|
||||||
|
<code-example header="src/app/app.module.ts" path="router-tutorial/src/app/app.module.ts" region="import-wildcard"></code-example>
|
||||||
|
|
||||||
|
The new route uses a path, `**`. This path is how Angular identifies a wildcard route. Any route
|
||||||
|
that does not match an existing route in your configuration will use this route.
|
||||||
|
|
||||||
|
<div class="alert is-important">
|
||||||
|
Notice that the wildcard route is placed at the end of the array. The order of your
|
||||||
|
routes is important, as Angular applies routes in order and uses the first match it finds.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
Try navigating to a non-existing route on your application, such as `http://localhost:4200/powers`.
|
||||||
|
This route doesn't match anything defined in your `app.module.ts` file. However, because you
|
||||||
|
defined a wildcard route, the application automatically displays your `PageNotFound` component.
|
||||||
|
|
||||||
|
## Next steps
|
||||||
|
|
||||||
|
At this point, you have a basic application that uses Angular's routing feature to change
|
||||||
|
what components the user can see based on the URL address. You have extended these features
|
||||||
|
to include a redirect, as well as a wildcard route to display a custom 404 page.
|
||||||
|
|
||||||
|
For more information about routing, see the following topics:
|
||||||
|
|
||||||
|
* [In-app Routing and Navigation](/guide/router)
|
||||||
|
* [Router API](/api/router)
|
@ -169,7 +169,7 @@ To get information from a route:
|
|||||||
|
|
||||||
<code-example header="In the component (excerpt)">
|
<code-example header="In the component (excerpt)">
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.activatedRoute.queryParams.subscribe(params => {
|
this.route.queryParams.subscribe(params => {
|
||||||
this.name = params['name'];
|
this.name = params['name'];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -357,130 +357,12 @@ Inject `ActivatedRoute` and `Router` in the constructor of the component class s
|
|||||||
|
|
||||||
{@a lazy-loading}
|
{@a lazy-loading}
|
||||||
|
|
||||||
## Lazy loading modules
|
## Lazy loading
|
||||||
|
|
||||||
To lazy load Angular modules, use `loadchildren` (instead of `component`) in your `AppRoutingModule` `routes` configuration as follows:
|
You can configure your routes to lazy load modules, which means that Angular only loads modules as needed, rather than loading all modules when the app launches.
|
||||||
|
Additionally, you can preload parts of your app in the background to improve the user experience.
|
||||||
<code-example header="AppRoutingModule (excerpt)">
|
|
||||||
|
|
||||||
const routes: Routes = [
|
|
||||||
{
|
|
||||||
path: 'items',
|
|
||||||
loadChildren: () => import('./items/items.module').then(m => m.ItemsModule)
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
In the lazy loaded module's routing module, add a route for the component.
|
|
||||||
|
|
||||||
<code-example header="Routing module for lazy loaded module (excerpt)">
|
|
||||||
|
|
||||||
const routes: Routes = [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
component: ItemsComponent
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Also be sure to remove the `ItemsModule` from the `AppModule`. For more information on lazy loading modules see [Lazy-loading feature modules](guide/lazy-loading-ngmodules).
|
|
||||||
|
|
||||||
|
|
||||||
{@a preloading}
|
|
||||||
|
|
||||||
## Preloading
|
|
||||||
|
|
||||||
Preloading improves UX by loading parts of your app in the background. You can preload modules or component data.
|
|
||||||
|
|
||||||
### Preloading modules
|
|
||||||
|
|
||||||
To enable preloading of all lazy loaded modules, import the `PreloadAllModules` token from the Angular `router`.
|
|
||||||
|
|
||||||
<code-example header="AppRoutingModule (excerpt)">
|
|
||||||
|
|
||||||
import { PreloadAllModules } from '@angular/router';
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Still in the `AppRoutingModule`, specify your preloading strategy in `forRoot()`.
|
|
||||||
|
|
||||||
<code-example header="AppRoutingModule (excerpt)">
|
|
||||||
|
|
||||||
RouterModule.forRoot(
|
|
||||||
appRoutes,
|
|
||||||
{
|
|
||||||
preloadingStrategy: PreloadAllModules
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
### Preloading component data
|
|
||||||
|
|
||||||
You can preload component data so that all elements and data on a page render at the same time when the user activates a route. To preload component data, you can use a `resolver`.
|
|
||||||
|
|
||||||
#### Resolvers
|
|
||||||
|
|
||||||
Create a resolver service. With the CLI, the command to generate a service is as follows:
|
|
||||||
|
|
||||||
|
|
||||||
<code-example language="none" class="code-shell">
|
|
||||||
ng generate service <service-name>
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
In your service, import the following router members, implement `Resolve`, and inject the `Router` service:
|
|
||||||
|
|
||||||
<code-example header="Resolver service (excerpt)">
|
|
||||||
|
|
||||||
import { Resolve } from '@angular/router';
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
export class CrisisDetailResolverService implements Resolve<> {
|
|
||||||
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<> {
|
|
||||||
// your logic goes here
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Import this resolver into your module's routing module.
|
|
||||||
|
|
||||||
<code-example header="Feature module's routing module (excerpt)">
|
|
||||||
|
|
||||||
import { YourResolverService } from './your-resolver.service';
|
|
||||||
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
Add a `resolve` object to the component's `route` configuration.
|
|
||||||
|
|
||||||
<code-example header="Feature module's routing module (excerpt)">
|
|
||||||
{
|
|
||||||
path: '/your-path',
|
|
||||||
component: YourComponent,
|
|
||||||
resolve: {
|
|
||||||
crisis: YourResolverService
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
|
|
||||||
In the component, use an observable to get the data from the `ActivatedRoute`.
|
|
||||||
|
|
||||||
|
|
||||||
<code-example header="Component (excerpt)">
|
|
||||||
ngOnInit() {
|
|
||||||
this.route.data
|
|
||||||
.subscribe((your-parameters) => {
|
|
||||||
// your data-specific code goes here
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</code-example>
|
|
||||||
|
|
||||||
For more information with a working example, see the [routing tutorial section on preloading](guide/router#preloading-background-loading-of-feature-areas).
|
|
||||||
|
|
||||||
|
For more information on lazy loading and preloading see the dedicated guide [Lazy loading NgModules](guide/lazy-loading-ngmodules).
|
||||||
|
|
||||||
## Preventing unauthorized access
|
## Preventing unauthorized access
|
||||||
|
|
||||||
@ -2737,7 +2619,7 @@ If the user is logged in, it returns true and the navigation continues.
|
|||||||
The `ActivatedRouteSnapshot` contains the _future_ route that will be activated and the `RouterStateSnapshot` contains the _future_ `RouterState` of the application, should you pass through the guard check.
|
The `ActivatedRouteSnapshot` contains the _future_ route that will be activated and the `RouterStateSnapshot` contains the _future_ `RouterState` of the application, should you pass through the guard check.
|
||||||
|
|
||||||
If the user is not logged in, you store the attempted URL the user came from using the `RouterStateSnapshot.url` and tell the router to redirect to a login page—a page you haven't created yet.
|
If the user is not logged in, you store the attempted URL the user came from using the `RouterStateSnapshot.url` and tell the router to redirect to a login page—a page you haven't created yet.
|
||||||
This secondary navigation automatically cancels the current navigation; `checkLogin()` returns `false`.
|
Returning a `UrlTree` tells the `Router` to cancel the current navigation and schedule a new one to redirect the user.
|
||||||
|
|
||||||
{@a add-login-component}
|
{@a add-login-component}
|
||||||
|
|
||||||
@ -2790,8 +2672,8 @@ Extend the `AuthGuard` to protect when navigating between the `admin` routes.
|
|||||||
Open `auth.guard.ts` and add the `CanActivateChild` interface to the imported tokens from the router package.
|
Open `auth.guard.ts` and add the `CanActivateChild` interface to the imported tokens from the router package.
|
||||||
|
|
||||||
Next, implement the `canActivateChild()` method which takes the same arguments as the `canActivate()` method: an `ActivatedRouteSnapshot` and `RouterStateSnapshot`.
|
Next, implement the `canActivateChild()` method which takes the same arguments as the `canActivate()` method: an `ActivatedRouteSnapshot` and `RouterStateSnapshot`.
|
||||||
The `canActivateChild()` method can return an `Observable<boolean>` or `Promise<boolean>` for async checks and a `boolean` for sync checks.
|
The `canActivateChild()` method can return an `Observable<boolean|UrlTree>` or `Promise<boolean|UrlTree>` for async checks and a `boolean` or `UrlTree` for sync checks.
|
||||||
This one returns a `boolean`:
|
This one returns either `true` to allow the user to access the admin feature module or `UrlTree` to redirect the user to the login page instead:
|
||||||
|
|
||||||
<code-example path="router/src/app/auth/auth.guard.3.ts" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child"></code-example>
|
<code-example path="router/src/app/auth/auth.guard.3.ts" header="src/app/auth/auth.guard.ts (excerpt)" region="can-activate-child"></code-example>
|
||||||
|
|
||||||
@ -2956,7 +2838,7 @@ Update the `CrisisDetailComponent` to get the crisis from the `ActivatedRoute.d
|
|||||||
|
|
||||||
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit"></code-example>
|
<code-example path="router/src/app/crisis-center/crisis-detail/crisis-detail.component.ts" header="src/app/crisis-center/crisis-detail/crisis-detail.component.ts (ngOnInit v2)" region="ngOnInit"></code-example>
|
||||||
|
|
||||||
Note the following two important points:
|
Note the following three important points:
|
||||||
|
|
||||||
1. The router's `Resolve` interface is optional.
|
1. The router's `Resolve` interface is optional.
|
||||||
The `CrisisDetailResolverService` doesn't inherit from a base class.
|
The `CrisisDetailResolverService` doesn't inherit from a base class.
|
||||||
@ -2964,6 +2846,8 @@ The router looks for that method and calls it if found.
|
|||||||
|
|
||||||
1. The router calls the resolver in any case where the the user could navigate away so you don't have to code for each use case.
|
1. The router calls the resolver in any case where the the user could navigate away so you don't have to code for each use case.
|
||||||
|
|
||||||
|
1. Returning an empty `Observable` in at least one resolver will cancel navigation.
|
||||||
|
|
||||||
The relevant Crisis Center code for this milestone follows.
|
The relevant Crisis Center code for this milestone follows.
|
||||||
|
|
||||||
<code-tabs>
|
<code-tabs>
|
||||||
|
@ -31,28 +31,27 @@ You can use these events to notify the user of a pending update or to refresh th
|
|||||||
|
|
||||||
### Checking for updates
|
### Checking for updates
|
||||||
|
|
||||||
It's possible to ask the service worker to check if any updates have been deployed to the server. You might choose to do this if you have a site that changes frequently or want updates to happen on a schedule.
|
It's possible to ask the service worker to check if any updates have been deployed to the server.
|
||||||
|
The service worker checks for updates during initialization and on each navigation request—that is, when the user navigates from a different address to your app.
|
||||||
|
However, you might choose to manually check for updates if you have a site that changes frequently or want updates to happen on a schedule.
|
||||||
|
|
||||||
Do this with the `checkForUpdate()` method:
|
Do this with the `checkForUpdate()` method:
|
||||||
|
|
||||||
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" header="check-for-update.service.ts"></code-example>
|
<code-example path="service-worker-getting-started/src/app/check-for-update.service.ts" header="check-for-update.service.ts"></code-example>
|
||||||
|
|
||||||
|
|
||||||
This method returns a `Promise` which indicates that the update check has completed successfully, though it does not indicate whether an update was discovered as a result of the check. Even if one is found, the service worker must still successfully download the changed files, which can fail. If successful, the `available` event will indicate availability of a new version of the app.
|
This method returns a `Promise` which indicates that the update check has completed successfully, though it does not indicate whether an update was discovered as a result of the check. Even if one is found, the service worker must still successfully download the changed files, which can fail. If successful, the `available` event will indicate availability of a new version of the app.
|
||||||
|
|
||||||
<div class="alert is-important">
|
<div class="alert is-important">
|
||||||
|
|
||||||
In order to avoid negatively affecting the initial rendering, `ServiceWorkerModule` will by default
|
In order to avoid negatively affecting the initial rendering of the page, `ServiceWorkerModule` waits for up to 30 seconds by default for the app to stabilize, before registering the ServiceWorker script.
|
||||||
wait for the app to stabilize, before registering the ServiceWorker script. Constantly polling for
|
Constantly polling for updates, for example, with [setInterval()](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval) or RxJS' [interval()](https://rxjs.dev/api/index/function/interval), will prevent the app from stabilizing and the ServiceWorker script will not be registered with the browser until the 30 seconds upper limit is reached.
|
||||||
updates, e.g. with `interval()`, will prevent the app from stabilizing and the ServiceWorker
|
|
||||||
script will never be registered with the browser.
|
|
||||||
|
|
||||||
You can avoid that by waiting for the app to stabilize first, before starting to poll for updates
|
|
||||||
(as shown in the example above).
|
|
||||||
|
|
||||||
Note that this is true for any kind of polling done by your application.
|
Note that this is true for any kind of polling done by your application.
|
||||||
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
|
Check the {@link ApplicationRef#isStable isStable} documentation for more information.
|
||||||
|
|
||||||
|
You can avoid that delay by waiting for the app to stabilize first, before starting to poll for updates, as shown in the example above.
|
||||||
|
Alternatively, you might want to define a different {@link SwRegistrationOptions#registrationStrategy registration strategy} for the ServiceWorker.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
### Forcing update activation
|
### Forcing update activation
|
||||||
@ -61,7 +60,12 @@ If the current tab needs to be updated to the latest app version immediately, it
|
|||||||
|
|
||||||
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" header="prompt-update.service.ts" region="sw-activate"></code-example>
|
<code-example path="service-worker-getting-started/src/app/prompt-update.service.ts" header="prompt-update.service.ts" region="sw-activate"></code-example>
|
||||||
|
|
||||||
Doing this could break lazy-loading into currently running apps, especially if the lazy-loaded chunks use filenames with hashes, which change every version.
|
<div class="alert is-important">
|
||||||
|
|
||||||
|
Calling `activateUpdate()` without reloading the page could break lazy-loading in a currently running app, especially if the lazy-loaded chunks use filenames with hashes, which change every version.
|
||||||
|
Therefore, it is recommended to reload the page once the promise returned by `activateUpdate()` is resolved.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
## More on Angular service workers
|
## More on Angular service workers
|
||||||
|
|
||||||
|
@ -74,6 +74,9 @@ interface AssetGroup {
|
|||||||
files?: string[];
|
files?: string[];
|
||||||
urls?: string[];
|
urls?: string[];
|
||||||
};
|
};
|
||||||
|
cacheQueryOptions?: {
|
||||||
|
ignoreSearch?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -110,6 +113,12 @@ This section describes the resources to cache, broken up into the following grou
|
|||||||
* `urls` includes both URLs and URL patterns that will be matched at runtime. These resources are not fetched directly and do not have content hashes, but they will be cached according to their HTTP headers. This is most useful for CDNs such as the Google Fonts service.<br>
|
* `urls` includes both URLs and URL patterns that will be matched at runtime. These resources are not fetched directly and do not have content hashes, but they will be cached according to their HTTP headers. This is most useful for CDNs such as the Google Fonts service.<br>
|
||||||
_(Negative glob patterns are not supported and `?` will be matched literally; i.e. it will not match any character other than `?`.)_
|
_(Negative glob patterns are not supported and `?` will be matched literally; i.e. it will not match any character other than `?`.)_
|
||||||
|
|
||||||
|
### `cacheQueryOptions`
|
||||||
|
|
||||||
|
These options are used to modify the matching behavior of requests. They are passed to the browsers `Cache#match` function. See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match) for details. Currently, only the following options are supported:
|
||||||
|
|
||||||
|
* `ignoreSearch`: Ignore query parameters. Defaults to `false`.
|
||||||
|
|
||||||
## `dataGroups`
|
## `dataGroups`
|
||||||
|
|
||||||
Unlike asset resources, data requests are not versioned along with the app. They're cached according to manually-configured policies that are more useful for situations such as API requests and other data dependencies.
|
Unlike asset resources, data requests are not versioned along with the app. They're cached according to manually-configured policies that are more useful for situations such as API requests and other data dependencies.
|
||||||
@ -127,6 +136,9 @@ export interface DataGroup {
|
|||||||
timeout?: string;
|
timeout?: string;
|
||||||
strategy?: 'freshness' | 'performance';
|
strategy?: 'freshness' | 'performance';
|
||||||
};
|
};
|
||||||
|
cacheQueryOptions?: {
|
||||||
|
ignoreSearch?: boolean;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -181,6 +193,25 @@ The Angular service worker can use either of two caching strategies for data res
|
|||||||
|
|
||||||
* `freshness` optimizes for currency of data, preferentially fetching requested data from the network. Only if the network times out, according to `timeout`, does the request fall back to the cache. This is useful for resources that change frequently; for example, account balances.
|
* `freshness` optimizes for currency of data, preferentially fetching requested data from the network. Only if the network times out, according to `timeout`, does the request fall back to the cache. This is useful for resources that change frequently; for example, account balances.
|
||||||
|
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
You can also emulate a third strategy, [staleWhileRevalidate](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate), which returns cached data (if available), but also fetches fresh data from the network in the background for next time.
|
||||||
|
To use this strategy set `strategy` to `freshness` and `timeout` to `0u` in `cacheConfig`.
|
||||||
|
|
||||||
|
This will essentially do the following:
|
||||||
|
|
||||||
|
1. Try to fetch from the network first.
|
||||||
|
2. If the network request does not complete after 0ms (i.e. immediately), fall back to the cache (ignoring cache age).
|
||||||
|
3. Once the network request completes, update the cache for future requests.
|
||||||
|
4. If the resource does not exist in the cache, wait for the network request anyway.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
### `cacheQueryOptions`
|
||||||
|
|
||||||
|
See [assetGroups](#assetgroups) for details.
|
||||||
|
|
||||||
## `navigationUrls`
|
## `navigationUrls`
|
||||||
|
|
||||||
This optional section enables you to specify a custom list of URLs that will be redirected to the index file.
|
This optional section enables you to specify a custom list of URLs that will be redirected to the index file.
|
||||||
|
@ -23,7 +23,7 @@ The above command completes the following actions:
|
|||||||
2. Enables service worker build support in the CLI.
|
2. Enables service worker build support in the CLI.
|
||||||
3. Imports and registers the service worker in the app module.
|
3. Imports and registers the service worker in the app module.
|
||||||
4. Updates the `index.html` file:
|
4. Updates the `index.html` file:
|
||||||
* Includes a link to add the `manifest.json` file.
|
* Includes a link to add the `manifest.webmanifest` file.
|
||||||
* Adds meta tags for `theme-color`.
|
* Adds meta tags for `theme-color`.
|
||||||
5. Installs icon files to support the installed Progressive Web App (PWA).
|
5. Installs icon files to support the installed Progressive Web App (PWA).
|
||||||
6. Creates the service worker configuration file called [`ngsw-config.json`](/guide/service-worker-config), which specifies the caching behaviors and other settings.
|
6. Creates the service worker configuration file called [`ngsw-config.json`](/guide/service-worker-config), which specifies the caching behaviors and other settings.
|
||||||
@ -107,7 +107,7 @@ Notice that all of the files the browser needs to render this application are ca
|
|||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
Pay attention to two key points:
|
Pay attention to two key points:
|
||||||
|
|
||||||
1. The generated `ngsw-config.json` includes a limited list of cachable fonts and images extentions. In some cases, you might want to modify the glob pattern to suit your needs.
|
1. The generated `ngsw-config.json` includes a limited list of cacheable fonts and images extentions. In some cases, you might want to modify the glob pattern to suit your needs.
|
||||||
|
|
||||||
1. If `resourcesOutputPath` or `assets` paths are modified after the generation of configuration file, you need to change the paths manually in `ngsw-config.json`.
|
1. If `resourcesOutputPath` or `assets` paths are modified after the generation of configuration file, you need to change the paths manually in `ngsw-config.json`.
|
||||||
</div>
|
</div>
|
||||||
|
@ -25,49 +25,48 @@ To use the Angular framework, you should be familiar with the following:
|
|||||||
|
|
||||||
Knowledge of [TypeScript](https://www.typescriptlang.org/) is helpful, but not required.
|
Knowledge of [TypeScript](https://www.typescriptlang.org/) is helpful, but not required.
|
||||||
|
|
||||||
|
To install Angular on your local system, you need the following:
|
||||||
|
|
||||||
{@a nodejs}
|
{@a nodejs}
|
||||||
### Node.js
|
|
||||||
|
|
||||||
Make sure your development environment includes `Node.js®` and an npm package manager.
|
* **Node.js**
|
||||||
|
|
||||||
Angular requires a [current, active LTS, or maintenance LTS](https://nodejs.org/about/releases/) version of `Node.js`. See the `engines` key for the specific version requirements in our [package.json](https://unpkg.com/@angular/cli/package.json).
|
Angular requires a [current, active LTS, or maintenance LTS](https://nodejs.org/about/releases) version of Node.js.
|
||||||
|
|
||||||
* To check your version, run `node -v` in a terminal/console window.
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
* To get `Node.js`, go to [nodejs.org](https://nodejs.org "Nodejs.org").
|
For information about specific version requirements, see the `engines` key in the [package.json](https://unpkg.com/@angular/cli/package.json) file.
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
For more information on installing Node.js, see [nodejs.org](http://nodejs.org "Nodejs.org").
|
||||||
|
If you are unsure what version of Node.js runs on your system, run `node -v` in a terminal window.
|
||||||
|
|
||||||
{@a npm}
|
{@a npm}
|
||||||
### npm package manager
|
|
||||||
|
|
||||||
Angular, the Angular CLI, and Angular apps depend on features and functionality provided by libraries that are available as [npm packages](https://docs.npmjs.com/getting-started/what-is-npm). To download and install npm packages, you must have an npm package manager.
|
* **npm package manager**
|
||||||
|
|
||||||
This setup guide uses the [npm client](https://docs.npmjs.com/cli/install) command line interface, which is installed with `Node.js` by default.
|
Angular, the Angular CLI, and Angular applications depend on [npm packages](https://docs.npmjs.com/getting-started/what-is-npm) for many features and functions.
|
||||||
|
To download and install npm packages, you need an npm package manager.
|
||||||
To check that you have the npm client installed, run `npm -v` in a terminal/console window.
|
This guide uses the [npm client](https://docs.npmjs.com/cli/install) command line interface, which is installed with `Node.js` by default.
|
||||||
|
To check that you have the npm client installed, run `npm -v` in a terminal window.
|
||||||
|
|
||||||
|
|
||||||
{@a install-cli}
|
{@a install-cli}
|
||||||
|
|
||||||
## Step 1: Install the Angular CLI
|
## Install the Angular CLI
|
||||||
|
|
||||||
You use the Angular CLI
|
You use the Angular CLI to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||||
to create projects, generate application and library code, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
|
||||||
|
|
||||||
Install the Angular CLI globally.
|
|
||||||
|
|
||||||
To install the CLI using `npm`, open a terminal/console window and enter the following command:
|
|
||||||
|
|
||||||
|
To install the Angular CLI, open a terminal window and run the following command:
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
<code-example language="sh" class="code-shell">
|
||||||
npm install -g @angular/cli
|
npm install -g @angular/cli
|
||||||
|
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a create-proj}
|
{@a create-proj}
|
||||||
|
|
||||||
## Step 2: Create a workspace and initial application
|
## Create a workspace and initial application
|
||||||
|
|
||||||
You develop apps in the context of an Angular [**workspace**](guide/glossary#workspace).
|
You develop apps in the context of an Angular [**workspace**](guide/glossary#workspace).
|
||||||
|
|
||||||
@ -86,16 +85,22 @@ The Angular CLI installs the necessary Angular npm packages and other dependenci
|
|||||||
|
|
||||||
The CLI creates a new workspace and a simple Welcome app, ready to run.
|
The CLI creates a new workspace and a simple Welcome app, ready to run.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
You also have the option to use Angular's strict mode, which can help you write better, more maintainable code.
|
||||||
|
For more information, see [Strict mode](/guide/strict-mode).
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
{@a serve}
|
{@a serve}
|
||||||
|
|
||||||
## Step 3: Run the application
|
## Run the application
|
||||||
|
|
||||||
The Angular CLI includes a server, so that you can easily build and serve your app locally.
|
The Angular CLI includes a server, so that you can build and serve your app locally.
|
||||||
|
|
||||||
1. Go to the workspace folder (`my-app`).
|
1. Navigate to the workspace folder, such as `my-app`.
|
||||||
|
|
||||||
1. Launch the server by using the CLI command `ng serve`, with the `--open` option.
|
1. Run the following command:
|
||||||
|
|
||||||
<code-example language="sh" class="code-shell">
|
<code-example language="sh" class="code-shell">
|
||||||
cd my-app
|
cd my-app
|
||||||
@ -108,7 +113,7 @@ and rebuilds the app as you make changes to those files.
|
|||||||
The `--open` (or just `-o`) option automatically opens your browser
|
The `--open` (or just `-o`) option automatically opens your browser
|
||||||
to `http://localhost:4200/`.
|
to `http://localhost:4200/`.
|
||||||
|
|
||||||
You will see:
|
If your installation and setup was successful, you should see a page similar to the following.
|
||||||
|
|
||||||
|
|
||||||
<div class="lightbox">
|
<div class="lightbox">
|
||||||
|
@ -44,7 +44,7 @@ For more information on how to choose, see the [next question](#how-do-i-choose)
|
|||||||
{@a how-do-i-choose}
|
{@a how-do-i-choose}
|
||||||
### How do I choose which `static` flag value to use: `true` or `false`?
|
### How do I choose which `static` flag value to use: `true` or `false`?
|
||||||
|
|
||||||
In the official API docs, we have always recommended retrieving query results in [`ngAfterViewInit` for view queries](https://angular.io/api/core/ViewChild#description) and [`ngAfterContentInit` for content queries](https://angular.io/api/core/ContentChild#description).
|
In the official API docs, we have always recommended retrieving query results in [`ngAfterViewInit` for view queries](api/core/ViewChild#description) and [`ngAfterContentInit` for content queries](api/core/ContentChild#description).
|
||||||
This is because by the time those lifecycle hooks run, change detection has completed for the relevant nodes and we can guarantee that we have collected all the possible query results.
|
This is because by the time those lifecycle hooks run, change detection has completed for the relevant nodes and we can guarantee that we have collected all the possible query results.
|
||||||
|
|
||||||
Most applications will want to use `{static: false}` for the same reason. This setting will ensure query matches that are dependent on binding resolution (e.g. results inside `*ngIf`s or `*ngFor`s) will be found by the query.
|
Most applications will want to use `{static: false}` for the same reason. This setting will ensure query matches that are dependent on binding resolution (e.g. results inside `*ngIf`s or `*ngFor`s) will be found by the query.
|
||||||
|
46
aio/content/guide/strict-mode.md
Normal file
46
aio/content/guide/strict-mode.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Strict mode
|
||||||
|
|
||||||
|
When you create a new workspace or a project you have an option to create them in a strict mode using the `--strict` flag.
|
||||||
|
|
||||||
|
Enabling this flag initializes your new workspace or project with a few new settings that improve maintainability, help you catch bugs ahead of time, and allow the CLI to perform advanced optimizations on your application.
|
||||||
|
Additionally, applications that use these stricter settings are easier to statically analyze, which can help the `ng update` command refactor code more safely and precisely when you are updating to future versions of Angular.
|
||||||
|
|
||||||
|
Specifically, the `strict` flag does the following:
|
||||||
|
|
||||||
|
* Enables [`strict` mode in TypeScript](https://www.staging-typescript.org/tsconfig#strict), as well as other strictness flags recommended by the TypeScript team. Specifically, `forceConsistentCasingInFileNames`, `noImplicitReturns`, `noFallthroughCasesInSwitch`.
|
||||||
|
* Turns on strict Angular compiler flags [`strictTemplates`](guide/angular-compiler-options#stricttemplates) and [`strictInjectionParameters`](guide/angular-compiler-options#strictinjectionparameters)
|
||||||
|
* [Bundle size budgets](guide/build#configuring-size-budgets) have been reduced by ~75%
|
||||||
|
* Turns on [`no-any` tslint rule](https://palantir.github.io/tslint/rules/no-any/) to prevent declarations of type `any`
|
||||||
|
* [Marks your application as side-effect free](https://webpack.js.org/guides/tree-shaking/#mark-the-file-as-side-effect-free) to enable more advanced tree-shaking
|
||||||
|
|
||||||
|
You can apply these settings at the workspace and project level.
|
||||||
|
|
||||||
|
To create a new workspace and application using the strict mode, run the following command:
|
||||||
|
|
||||||
|
<code-example language="sh" class="code-shell">
|
||||||
|
|
||||||
|
ng new [project-name] --strict
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
To create a new application in the strict mode within an existing non-strict workspace, run the following command:
|
||||||
|
|
||||||
|
<code-example language="sh" class="code-shell">
|
||||||
|
|
||||||
|
ng generate application [project-name] --strict
|
||||||
|
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
{@a side-effect}
|
||||||
|
|
||||||
|
### Non-local side effects in applications
|
||||||
|
|
||||||
|
When you create projects and workspaces using the `strict` mode, you'll notice an additional `package.json` file, located in `src/app/` directory.
|
||||||
|
This file informs tools and bundlers that the code under this directory is free of non-local side effects. Non-local side effects in the application code are not common and using them is not considered a good coding pattern.
|
||||||
|
More importantly, code with these types of side effects cannot be optimized, resulting in increased bundle sizes and applications that load more slowly.
|
||||||
|
|
||||||
|
If you need more information, the following links may be helpful.
|
||||||
|
|
||||||
|
* [Tree-shaking](https://webpack.js.org/guides/tree-shaking/)
|
||||||
|
* [Dealing with side effects and pure functions in JavaScript](https://dev.to/vonheikemen/dealing-with-side-effects-and-pure-functions-in-javascript-16mg)
|
||||||
|
* [How to deal with dirty side effects in your pure function JavaScript](https://jrsinclair.com/articles/2018/how-to-deal-with-dirty-side-effects-in-your-pure-functional-javascript/)
|
@ -251,7 +251,7 @@ You enable these features in the string assigned to `ngFor`, which you write in
|
|||||||
|
|
||||||
Everything _outside_ the `ngFor` string stays with the host element
|
Everything _outside_ the `ngFor` string stays with the host element
|
||||||
(the `<div>`) as it moves inside the `<ng-template>`.
|
(the `<div>`) as it moves inside the `<ng-template>`.
|
||||||
In this example, the `[ngClass]="odd"` stays on the `<div>`.
|
In this example, the `[class.odd]="odd"` stays on the `<div>`.
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@ -832,6 +832,89 @@ When the `condition` is truthy, the top (A) paragraph is removed and the bottom
|
|||||||
<img src='generated/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action">
|
<img src='generated/images/guide/structural-directives/unless-anim.gif' alt="UnlessDirective in action">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{@a directive-type-checks}
|
||||||
|
|
||||||
|
## Improving template type checking for custom directives
|
||||||
|
|
||||||
|
You can improve template type checking for custom directives by adding template guard properties to your directive definition.
|
||||||
|
These properties help the Angular template type checker find mistakes in the template at compile time, which can avoid runtime errors those mistakes can cause.
|
||||||
|
|
||||||
|
Use the type-guard properties to inform the template type checker of an expected type, thus improving compile-time type-checking for that template.
|
||||||
|
|
||||||
|
* A property `ngTemplateGuard_(someInputProperty)` lets you specify a more accurate type for an input expression within the template.
|
||||||
|
* The `ngTemplateContextGuard` static property declares the type of the template context.
|
||||||
|
|
||||||
|
This section provides example of both kinds of type-guard property.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
For more information, see [Template type checking guide](guide/template-typecheck "Template type-checking guide").
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{@a narrowing-input-types}
|
||||||
|
|
||||||
|
### Make in-template type requirements more specific with template guards
|
||||||
|
|
||||||
|
A structural directive in a template controls whether that template is rendered at run time, based on its input expression.
|
||||||
|
To help the compiler catch template type errors, you should specify as closely as possible the required type of a directive's input expression when it occurs inside the template.
|
||||||
|
|
||||||
|
A type guard function *narrows* the expected type of an input expression to a subset of types that might be passed to the directive within the template at run time.
|
||||||
|
You can provide such a function to help the type-checker infer the proper type for the expression at compile time.
|
||||||
|
|
||||||
|
For example, the `NgIf` implementation uses type-narrowing to ensure that the
|
||||||
|
template is only instantiated if the input expression to `*ngIf` is truthy.
|
||||||
|
To provide the specific type requirement, the `NgIf` directive defines a [static property `ngTemplateGuard_ngIf: 'binding'`](api/common/NgIf#static-properties).
|
||||||
|
The `binding` value is a special case for a common kind of type-narrowing where the input expression is evaluated in order to satisfy the type requirement.
|
||||||
|
|
||||||
|
To provide a more specific type for an input expression to a directive within the template, add a `ngTemplateGuard_xx` property to the directive, where the suffix to the static property name is the `@Input` field name.
|
||||||
|
The value of the property can be either a general type-narrowing function based on its return type, or the string `"binding"` as in the case of `NgIf`.
|
||||||
|
|
||||||
|
For example, consider the following structural directive that takes the result of a template expression as an input.
|
||||||
|
|
||||||
|
<code-example language="ts" header="IfLoadedDirective">
|
||||||
|
export type Loaded<T> = { type: 'loaded', data: T };
|
||||||
|
export type Loading = { type: 'loading' };
|
||||||
|
export type LoadingState<T> = Loaded<T> | Loading;
|
||||||
|
export class IfLoadedDirective<T> {
|
||||||
|
@Input('ifLoaded') set state(state: LoadingState<T>) {}
|
||||||
|
static ngTemplateGuard_state<T>(dir: IfLoadedDirective<T>, expr: LoadingState<T>): expr is Loaded<T> { return true; };
|
||||||
|
export interface Person {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: `<div *ifLoaded="state">{{ state.data }}</div>`,
|
||||||
|
})
|
||||||
|
export class AppComponent {
|
||||||
|
state: LoadingState<Person>;
|
||||||
|
}
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
In this example, the `LoadingState<T>` type permits either of two states, `Loaded<T>` or `Loading`. The expression used as the directive’s `state` input is of the umbrella type `LoadingState`, as it’s unknown what the loading state is at that point.
|
||||||
|
|
||||||
|
The `IfLoadedDirective` definition declares the static field `ngTemplateGuard_state`, which expresses the narrowing behavior.
|
||||||
|
Within the `AppComponent` template, the `*ifLoaded` structural directive should render this template only when `state` is actually `Loaded<Person>`.
|
||||||
|
The type guard allows the type checker to infer that the acceptable type of `state` within the template is a `Loaded<T>`, and further infer that `T` must be an instance of `Person`.
|
||||||
|
|
||||||
|
{@a narrowing-context-type}
|
||||||
|
|
||||||
|
### Typing the directive's context
|
||||||
|
|
||||||
|
If your structural directive provides a context to the instantiated template, you can properly type it inside the template by providing a static `ngTemplateContextGuard` function.
|
||||||
|
The following snippet shows an example of such a function.
|
||||||
|
|
||||||
|
<code-example language="ts" header="myDirective.ts">
|
||||||
|
@Directive({…})
|
||||||
|
export class ExampleDirective {
|
||||||
|
// Make sure the template checker knows the type of the context with which the
|
||||||
|
// template of this directive will be rendered
|
||||||
|
static ngTemplateContextGuard(dir: ExampleDirective, ctx: unknown): ctx is ExampleContext { return true; };
|
||||||
|
|
||||||
|
// …
|
||||||
|
}
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{@a summary}
|
{@a summary}
|
||||||
@ -879,7 +962,7 @@ Here is the source from the `src/app/` folder.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
You learned
|
You learned:
|
||||||
|
|
||||||
* that structural directives manipulate HTML layout.
|
* that structural directives manipulate HTML layout.
|
||||||
* to use [`<ng-container>`](guide/structural-directives#ngcontainer) as a grouping element when there is no suitable host element.
|
* to use [`<ng-container>`](guide/structural-directives#ngcontainer) as a grouping element when there is no suitable host element.
|
||||||
|
@ -432,7 +432,7 @@ Attributes can be changed by `setAttribute()`, which re-initializes correspondin
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
For more information, see the [MDN Interfaces documentation](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces) which has API docs for all the standard DOM elements and their properties.
|
For more information, see the [MDN Interfaces documentation](https://developer.mozilla.org/en-US/docs/Web/API#Interfaces) which has API docs for all the standard DOM elements and their properties.
|
||||||
Comparing the [`<td>` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td) attributes to the [`<td>` properties](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) provides a helpful example for differentiation.
|
Comparing the [`<td>` attributes](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/td) to the [`<td>` properties](https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableCellElement) provides a helpful example for differentiation.
|
||||||
In particular, you can navigate from the attributes page to the properties via "DOM interface" link, and navigate the inheritance hierarchy up to `HTMLTableCellElement`.
|
In particular, you can navigate from the attributes page to the properties via "DOM interface" link, and navigate the inheritance hierarchy up to `HTMLTableCellElement`.
|
||||||
|
|
||||||
|
|
||||||
@ -473,7 +473,7 @@ To control the state of the button, set the `disabled` *property*,
|
|||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
Though you could technically set the `[attr.disabled]` attribute binding, the values are different in that the property binding requires to a boolean value, while its corresponding attribute binding relies on whether the value is `null` or not. Consider the following:
|
Though you could technically set the `[attr.disabled]` attribute binding, the values are different in that the property binding requires to be a boolean value, while its corresponding attribute binding relies on whether the value is `null` or not. Consider the following:
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<input [disabled]="condition ? true : false">
|
<input [disabled]="condition ? true : false">
|
||||||
@ -821,8 +821,8 @@ content harmlessly. The following is the browser output
|
|||||||
of the `evilTitle` examples.
|
of the `evilTitle` examples.
|
||||||
|
|
||||||
<code-example language="bash">
|
<code-example language="bash">
|
||||||
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
|
"Template <script>alert("evil never sleeps")</script> Syntax" is the interpolated evil title.
|
||||||
"Template alert("evil never sleeps")Syntax" is the property bound evil title.
|
"Template Syntax" is the property bound evil title.
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
<hr/>
|
<hr/>
|
||||||
@ -1455,7 +1455,7 @@ Angular provides *value accessors* for all of the basic HTML form elements and t
|
|||||||
|
|
||||||
You can't apply `[(ngModel)]` to a non-form native element or a
|
You can't apply `[(ngModel)]` to a non-form native element or a
|
||||||
third-party custom component until you write a suitable value accessor. For more information, see
|
third-party custom component until you write a suitable value accessor. For more information, see
|
||||||
the API documentation on [DefaultValueAccessor](https://angular.io/api/forms/DefaultValueAccessor).
|
the API documentation on [DefaultValueAccessor](api/forms/DefaultValueAccessor).
|
||||||
|
|
||||||
You don't need a value accessor for an Angular component that
|
You don't need a value accessor for an Angular component that
|
||||||
you write because you can name the value and event properties
|
you write because you can name the value and event properties
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
## Overview of template type checking
|
## Overview of template type checking
|
||||||
|
|
||||||
Just as TypeScript catches type errors in your code, Angular checks the expressions and bindings within the templates of your application and can report any type errors it finds.
|
Just as TypeScript catches type errors in your code, Angular checks the expressions and bindings within the templates of your application and can report any type errors it finds.
|
||||||
Angular currently has three modes of doing this, depending on the value of the `fullTemplateTypeCheck` and `strictTemplates` flags in the [TypeScript configuration file](guide/typescript-configuration), `tsconfig.json`.
|
Angular currently has three modes of doing this, depending on the value of the `fullTemplateTypeCheck` and `strictTemplates` flags in the [TypeScript configuration file](guide/typescript-configuration).
|
||||||
|
|
||||||
### Basic mode
|
### Basic mode
|
||||||
|
|
||||||
@ -107,7 +107,7 @@ There can also be false positives when the typings of an Angular library are eit
|
|||||||
In case of a false positive like these, there are a few options:
|
In case of a false positive like these, there are a few options:
|
||||||
|
|
||||||
* Use the [`$any()` type-cast function](guide/template-syntax#any-type-cast-function) in certain contexts to opt out of type-checking for a part of the expression.
|
* Use the [`$any()` type-cast function](guide/template-syntax#any-type-cast-function) in certain contexts to opt out of type-checking for a part of the expression.
|
||||||
* You can disable strict checks entirely by setting `strictTemplates: false` in the application's TypeScript configuration file, `tsconfig.json`.
|
* You can disable strict checks entirely by setting `strictTemplates: false` in the application's TypeScript configuration file.
|
||||||
* You can disable certain type-checking operations individually, while maintaining strictness in other aspects, by setting a _strictness flag_ to `false`.
|
* You can disable certain type-checking operations individually, while maintaining strictness in other aspects, by setting a _strictness flag_ to `false`.
|
||||||
* If you want to use `strictTemplates` and `strictNullChecks` together, you can opt out of strict null type checking specifically for input bindings via `strictNullInputTypes`.
|
* If you want to use `strictTemplates` and `strictNullChecks` together, you can opt out of strict null type checking specifically for input bindings via `strictNullInputTypes`.
|
||||||
|
|
||||||
@ -167,6 +167,8 @@ Here, during type checking of the template for `AppComponent`, the `[user]="sele
|
|||||||
Therefore, Angular assigns the `selectedUser` property to `UserDetailComponent.user`, which would result in an error if their types were incompatible.
|
Therefore, Angular assigns the `selectedUser` property to `UserDetailComponent.user`, which would result in an error if their types were incompatible.
|
||||||
TypeScript checks the assignment according to its type system, obeying flags such as `strictNullChecks` as they are configured in the application.
|
TypeScript checks the assignment according to its type system, obeying flags such as `strictNullChecks` as they are configured in the application.
|
||||||
|
|
||||||
|
You can avoid run-time type errors by providing more specific in-template type requirements to the template type checker. Make the input type requirements for your own directives as specific as possible by providing template-guard functions in the directive definition. See [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks), and [Input setter coercion](#input-setter-coercion) in this guide.
|
||||||
|
|
||||||
|
|
||||||
### Strict null checks
|
### Strict null checks
|
||||||
|
|
||||||
@ -201,7 +203,7 @@ There are two potential workarounds to the above issues:
|
|||||||
|
|
||||||
As a library author, you can take several measures to provide an optimal experience for your users.
|
As a library author, you can take several measures to provide an optimal experience for your users.
|
||||||
First, enabling `strictNullChecks` and including `null` in an input's type, as appropriate, communicates to your consumers whether they can provide a nullable value or not.
|
First, enabling `strictNullChecks` and including `null` in an input's type, as appropriate, communicates to your consumers whether they can provide a nullable value or not.
|
||||||
Additionally, it is possible to provide type hints that are specific to the template type checker, see the [Input setter coercion](guide/template-typecheck#input-setter-coercion) section of this guide.
|
Additionally, it is possible to provide type hints that are specific to the template type checker. See [Improving template type checking for custom directives](guide/structural-directives#directive-type-checks), and [Input setter coercion](#input-setter-coercion) below.
|
||||||
|
|
||||||
|
|
||||||
{@a input-setter-coercion}
|
{@a input-setter-coercion}
|
||||||
|
@ -93,7 +93,7 @@ Adopt these two conventions in your own projects for _every kind_ of test file.
|
|||||||
|
|
||||||
## Set up continuous integration
|
## Set up continuous integration
|
||||||
|
|
||||||
One of the best ways to keep your project bug free is through a test suite, but it's easy to forget to run tests all the time.
|
One of the best ways to keep your project bug-free is through a test suite, but it's easy to forget to run tests all the time.
|
||||||
Continuous integration (CI) servers let you set up your project repository so that your tests run on every commit and pull request.
|
Continuous integration (CI) servers let you set up your project repository so that your tests run on every commit and pull request.
|
||||||
|
|
||||||
There are paid CI services like Circle CI and Travis CI, and you can also host your own for free using Jenkins and others.
|
There are paid CI services like Circle CI and Travis CI, and you can also host your own for free using Jenkins and others.
|
||||||
|
@ -153,7 +153,7 @@ You can define more than one animation trigger for a component. You can attach a
|
|||||||
|
|
||||||
### Parent-child animations
|
### Parent-child animations
|
||||||
|
|
||||||
Each time an animation is triggered in Angular, the parent animation always get priority and child animations are blocked. In order for a child animation to run, the parent animation must query each of the elements containing child animations and then allow the animations to run using the [`animateChild()`](https://angular.io/api/animations/animateChild) function.
|
Each time an animation is triggered in Angular, the parent animation always get priority and child animations are blocked. In order for a child animation to run, the parent animation must query each of the elements containing child animations and then allow the animations to run using the [`animateChild()`](api/animations/animateChild) function.
|
||||||
|
|
||||||
#### Disabling an animation on an HTML element
|
#### Disabling an animation on an HTML element
|
||||||
|
|
||||||
@ -178,7 +178,7 @@ You can't selectively disable multiple animations on a single element.
|
|||||||
|
|
||||||
However, selective child animations can still be run on a disabled parent in one of the following ways:
|
However, selective child animations can still be run on a disabled parent in one of the following ways:
|
||||||
|
|
||||||
* A parent animation can use the [`query()`](https://angular.io/api/animations/query) function to collect inner elements located in disabled areas of the HTML template.
|
* A parent animation can use the [`query()`](api/animations/query) function to collect inner elements located in disabled areas of the HTML template.
|
||||||
Those elements can still animate.
|
Those elements can still animate.
|
||||||
|
|
||||||
* A subanimation can be queried by a parent and then later animated with the `animateChild()` function.
|
* A subanimation can be queried by a parent and then later animated with the `animateChild()` function.
|
||||||
|
@ -15,22 +15,49 @@ that are important to Angular developers, including details about the following
|
|||||||
|
|
||||||
{@a tsconfig}
|
{@a tsconfig}
|
||||||
|
|
||||||
## TypeScript configuration
|
## Configuration files
|
||||||
|
|
||||||
A TypeScript configuration file called `tsconfig.json` guides the compiler as it generates JavaScript files for a project.
|
A given Angular workspace contains several TypeScript configuration files.
|
||||||
This file contains options and flags that are essential for Angular applications.
|
At the root level, there are two main TypeScript configuration files: a `tsconfig.json` file and a `tsconfig.base.json` file.
|
||||||
Typically, the file is found at the [root level of the workspace](guide/file-structure).
|
|
||||||
|
The `tsconfig.json` file is a ["Solution Style"](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-9.html#support-for-solution-style-tsconfigjson-files) TypeScript configuration file.
|
||||||
|
Code editors and TypeScript’s language server use this file to improve development experience.
|
||||||
|
Compilers do not use this file.
|
||||||
|
|
||||||
|
The `tsconfig.json` file contains a list of paths to the other TypeScript configuration files used in the workspace.
|
||||||
|
|
||||||
|
<code-example lang="json" header="tsconfig.json" linenums="false">
|
||||||
|
{
|
||||||
|
"files": [],
|
||||||
|
"references": [
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.app.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./tsconfig.spec.json"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "./projects/my-lib/tsconfig.lib.json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
The `tsconfig.base.json` file specifies the base TypeScript and Angular compiler options that all projects in the workspace inherit.
|
||||||
|
|
||||||
|
The TypeScript and Angular have a wide range of options which can be used to configure type-checking features and generated output.
|
||||||
|
For more information, see the [Configuration inheritance with extends](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#configuration-inheritance-with-extends) section of the TypeScript documentation.
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
For details about `tsconfig.json`, see the official
|
For more information TypeScript configuration files, see the official [TypeScript wiki](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html)
|
||||||
[TypeScript wiki](http://www.typescriptlang.org/docs/handbook/tsconfig-json.html).
|
For details about configuration inheritance, see the [Configuration inheritance with extends](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#configuration-inheritance-with-extends) section.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
The initial `tsconfig.json` for an Angular app typically looks like the following example.
|
The initial `tsconfig.base.json` for an Angular workspace typically looks like the following example.
|
||||||
|
|
||||||
<code-example lang="json" header="tsconfig.json" linenums="false">
|
<code-example lang="json" header="tsconfig.base.json" linenums="false">
|
||||||
{
|
{
|
||||||
"compileOnSave": false,
|
"compileOnSave": false,
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
@ -40,29 +67,25 @@ The initial `tsconfig.json` for an Angular app typically looks like the followin
|
|||||||
"declaration": false,
|
"declaration": false,
|
||||||
"downlevelIteration": true,
|
"downlevelIteration": true,
|
||||||
"experimentalDecorators": true,
|
"experimentalDecorators": true,
|
||||||
"module": "esnext",
|
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"target": "es2015",
|
"target": "es2015",
|
||||||
"typeRoots": [
|
"module": "es2020",
|
||||||
"node_modules/@types"
|
|
||||||
],
|
|
||||||
"lib": [
|
"lib": [
|
||||||
"es2018",
|
"es2018",
|
||||||
"dom"
|
"dom"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"fullTemplateTypeCheck": true,
|
|
||||||
"strictInjectionParameters": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
|
### Strict mode
|
||||||
|
|
||||||
|
When you create new workspaces and projects, you have the option to use Angular's strict mode, which can help you write better, more maintainable code.
|
||||||
|
For more information, see [Strict mode](/guide/strict-mode).
|
||||||
|
|
||||||
{@a noImplicitAny}
|
{@a noImplicitAny}
|
||||||
|
|
||||||
|
|
||||||
### *noImplicitAny* and *suppressImplicitAnyIndexErrors*
|
### *noImplicitAny* and *suppressImplicitAnyIndexErrors*
|
||||||
|
|
||||||
TypeScript developers disagree about whether the `noImplicitAny` flag should be `true` or `false`.
|
TypeScript developers disagree about whether the `noImplicitAny` flag should be `true` or `false`.
|
||||||
@ -96,6 +119,7 @@ For more information about how the TypeScript configuration affects compilation,
|
|||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{@a typings}
|
{@a typings}
|
||||||
|
|
||||||
## TypeScript typings
|
## TypeScript typings
|
||||||
@ -146,7 +170,6 @@ For instance, to install typings for `jasmine` you run `npm install @types/jasmi
|
|||||||
|
|
||||||
{@a target}
|
{@a target}
|
||||||
|
|
||||||
|
|
||||||
### *target*
|
### *target*
|
||||||
|
|
||||||
By default, the target is `es2015`, which is supported only in modern browsers. You can configure the target to `es5` to specifically support legacy browsers. [Differential loading](guide/deployment#differential-loading) is also provided by the Angular CLI to support modern, and legacy browsers with separate bundles.
|
By default, the target is `es2015`, which is supported only in modern browsers. You can configure the target to `es5` to specifically support legacy browsers. [Differential loading](guide/deployment#differential-loading) is also provided by the Angular CLI to support modern, and legacy browsers with separate bundles.
|
||||||
|
@ -48,11 +48,11 @@ src/
|
|||||||
app/ ... <i>application code</i>
|
app/ ... <i>application code</i>
|
||||||
app.server.module.ts <i>* server-side application module</i>
|
app.server.module.ts <i>* server-side application module</i>
|
||||||
server.ts <i>* express web server</i>
|
server.ts <i>* express web server</i>
|
||||||
tsconfig.json <i>TypeScript client configuration</i>
|
tsconfig.json <i>TypeScript solution style configuration</i>
|
||||||
tsconfig.app.json <i>TypeScript client configuration</i>
|
tsconfig.base.json <i>TypeScript base configuration</i>
|
||||||
tsconfig.server.json <i>* TypeScript server configuration</i>
|
tsconfig.app.json <i>TypeScript browser application configuration</i>
|
||||||
tsconfig.spec.json <i>TypeScript spec configuration</i>
|
tsconfig.server.json <i>TypeScript server application configuration</i>
|
||||||
package.json <i>npm configuration</i>
|
tsconfig.spec.json <i>TypeScript tests configuration</i>
|
||||||
</code-example>
|
</code-example>
|
||||||
|
|
||||||
The files marked with `*` are new and not in the original tutorial sample.
|
The files marked with `*` are new and not in the original tutorial sample.
|
||||||
|
82
aio/content/guide/updating-to-version-10.md
Normal file
82
aio/content/guide/updating-to-version-10.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# Updating to Angular version 10
|
||||||
|
|
||||||
|
This guide contains information related to updating to version 10 of Angular.
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
For information on upgrading to Angular version 9, see [Updating to Angular version 9](https://v9.angular.io/guide/updating-to-version-9).
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Updating CLI Apps
|
||||||
|
|
||||||
|
For step-by-step instructions on how to update to the latest Angular release (and leverage our automated migration tools to do so), use the interactive update guide at [update.angular.io](https://update.angular.io).
|
||||||
|
|
||||||
|
If you're curious about the specific migrations being run by the CLI, see the [automated migrations section](#migrations) for details on what code is changing and why.
|
||||||
|
|
||||||
|
## Changes and Deprecations in Version 10
|
||||||
|
|
||||||
|
<div class="alert is-helpful">
|
||||||
|
|
||||||
|
For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices").
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{@a breaking-changes}
|
||||||
|
### New Breaking Changes
|
||||||
|
|
||||||
|
* Typescript 3.6, 3.7, and 3.8 are no longer supported. Please update to Typescript 3.9.
|
||||||
|
* Input fields of type `number` fire the `valueChanges` event only once per value change (as opposed to twice in some cases). See [PR 36087](https://github.com/angular/angular/pull/36087).
|
||||||
|
* The `minLength` and `maxLength` validators only validate values that have a numeric `length` property. See [PR 36157](https://github.com/angular/angular/pull/36157).
|
||||||
|
* Templates with unknown property bindings or unknown element names now log errors instead of warnings. See [PR 36399](https://github.com/angular/angular/pull/36399).
|
||||||
|
* `UrlMatcher` can now return `null` values. See [PR 36402](https://github.com/angular/angular/pull/36402).
|
||||||
|
* Transplanted views now refresh at insertion point only. See PR 35968](https://github.com/angular/angular/pull/35968).
|
||||||
|
* Formatting times with the `b` or `B` format codes now supports time periods that cross midnight. See [PR 36611](https://github.com/angular/angular/pull/36611).
|
||||||
|
* Navigation is canceled for routes with at least one empty resolver. See [PR 24621](https://github.com/angular/angular/pull/24621).
|
||||||
|
|
||||||
|
{@a deprecations}
|
||||||
|
### New Deprecations
|
||||||
|
|
||||||
|
| Area | API or Feature | May be removed in |
|
||||||
|
| ----------------------------- | --------------------------------------------------------------------------- | ----------------- |
|
||||||
|
| `@angular/core` | [`WrappedValue`](guide/deprecations#wrapped-value) | <!--v10--> v12 |
|
||||||
|
| browser support | [`IE 9, 10, and IE Mobile`](guide/deprecations#ie-9-10-and-ie-mobile-support) | <!--v10--> v11 |
|
||||||
|
|
||||||
|
|
||||||
|
{@a removals}
|
||||||
|
### New Removals of Deprecated APIs
|
||||||
|
|
||||||
|
The following APIs have been removed starting with version 10.0.0*:
|
||||||
|
|
||||||
|
| Package | API | Replacement | Notes |
|
||||||
|
| ---------------- | -------------- | ----------- | ----- |
|
||||||
|
| `@angular/core` | Undecorated base classes that use Angular features | Add Angular decorator | See [migration guide](guide/migration-undecorated-classes) for more info |
|
||||||
|
| `@angular/core` | `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [migration guide](guide/migration-module-with-providers) for more info |
|
||||||
|
| `@angular/core` | Style Sanitization | no action needed | See [style sanitization API removal](/guide/deprecations#style-sanitization) for more info
|
||||||
|
| `@angular/bazel` | [`Bazel builder and schematics`](guide/deprecations#bazelbuilder) | `bazelbuild/rules_nodejs` | [More info](https://github.com/angular/angular/tree/10.0.x/packages/bazel/src/schematics) |
|
||||||
|
|
||||||
|
|
||||||
|
*To see APIs removed in version 9, check out this guide on the [version 9 docs site](https://v9.angular.io/guide/deprecations#removed).
|
||||||
|
|
||||||
|
{@a ivy}
|
||||||
|
|
||||||
|
## Ivy features and compatibility
|
||||||
|
|
||||||
|
Since version 9, Angular Ivy is the default rendering engine. If you haven't heard of Ivy, you can read more about it in the [Angular Ivy guide](guide/ivy).
|
||||||
|
|
||||||
|
* Among other features, Ivy introduces more comprehensive type-checking within templates. For details, see [Template Type-checking](guide/template-typecheck).
|
||||||
|
|
||||||
|
* For general guidance on debugging and a list of minor changes associated with Ivy, see the [Ivy compatibility guide](guide/ivy-compatibility).
|
||||||
|
|
||||||
|
* For help with opting out of Ivy, see the instructions [here](guide/ivy#opting-out-of-angular-ivy).
|
||||||
|
|
||||||
|
{@a migrations}
|
||||||
|
## Automated Migrations for Version 10
|
||||||
|
|
||||||
|
Read about the migrations the CLI handles for you automatically:
|
||||||
|
|
||||||
|
* [Migrating missing `@Directive()`/`@Component()` decorators](guide/migration-undecorated-classes)
|
||||||
|
* [Migrating `ModuleWithProviders`](guide/migration-module-with-providers)
|
||||||
|
* [Solution-style `tsconfig.json` migration](guide/migration-solution-style-tsconfig)
|
||||||
|
* [`tslib` direct dependency migration](guide/migration-update-libraries-tslib)
|
||||||
|
* [Update `module` and `target` compiler options migration](guide/migration-update-module-and-target-compiler-options)
|
@ -1,90 +0,0 @@
|
|||||||
# Updating to Angular version 9
|
|
||||||
|
|
||||||
This guide contains information related to updating to version 9 of Angular.
|
|
||||||
|
|
||||||
## Updating CLI Apps
|
|
||||||
|
|
||||||
For step-by-step instructions on how to update to the latest Angular release (and leverage our automated migration tools to do so), use the interactive update guide at [update.angular.io](https://update.angular.io).
|
|
||||||
|
|
||||||
If you're curious about the specific migrations being run by the CLI, see the [automated migrations section](#migrations) for details on what code is changing and why.
|
|
||||||
|
|
||||||
## Changes and Deprecations in Version 9
|
|
||||||
|
|
||||||
<div class="alert is-helpful">
|
|
||||||
|
|
||||||
For information about Angular's deprecation and removal practices, see [Angular Release Practices](guide/releases#deprecation-practices "Angular Release Practices: Deprecation practices").
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{@a breaking-changes}
|
|
||||||
### New Breaking Changes
|
|
||||||
|
|
||||||
- Angular now compiles with Ivy by default. See the [Ivy compatibility section](#ivy).
|
|
||||||
|
|
||||||
- CLI apps compile in [AOT mode](/guide/aot-compiler) by default (which includes template type-checking).
|
|
||||||
Users who only built with JIT before may see new type errors.
|
|
||||||
See our [template type-checking guide](guide/template-typecheck) for more information and debugging tips.
|
|
||||||
|
|
||||||
- Typescript 3.4 and 3.5 are no longer supported. Please update to Typescript 3.7.
|
|
||||||
|
|
||||||
- `tslib` is now listed as a peer dependency rather than a direct dependency. If you are not using the CLI, you must manually install `tslib`, using `yarn add tslib` or `npm install tslib --save`.
|
|
||||||
|
|
||||||
{@a deprecations}
|
|
||||||
### New Deprecations
|
|
||||||
|
|
||||||
| API | Replacement | Notes |
|
|
||||||
| ------------------------------------------------------------------------| ------------------------------------ | ----- |
|
|
||||||
| [`entryComponents`](api/core/NgModule#entryComponents) | none | See [`entryComponents`](guide/deprecations#entryComponents) |
|
|
||||||
| [`CurrencyPipe` - `DEFAULT_CURRENCY_CODE`](api/common/CurrencyPipe#currency-code-deprecation)| `{provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}` | From v11 the default code will be extracted from the locale data given by `LOCAL_ID`, rather than `USD`. |
|
|
||||||
| [`ANALYZE_FOR_ENTRY_COMPONENTS`](api/core/ANALYZE_FOR_ENTRY_COMPONENTS) | none | See [`ANALYZE_FOR_ENTRY_COMPONENTS`](guide/deprecations#entryComponents) |
|
|
||||||
| `ModuleWithProviders` without a generic | `ModuleWithProviders` with a generic | See [`ModuleWithProviders` section](guide/deprecations#moduleWithProviders) |
|
|
||||||
| Undecorated base classes that use Angular features | Base classes with `@Directive()` decorator that use Angular features | See [undecorated base classes section](guide/deprecations#undecorated-base-classes) |
|
|
||||||
| `esm5` and `fesm5` distribution in `@angular/*` npm packages | `esm2015` and `fesm2015` entrypoints | See [`esm5` and `fesm5`](guide/deprecations#esm5-fesm5) |
|
|
||||||
| [`TestBed.get`](api/core/testing/TestBed#get) | [`TestBed.inject`](api/core/testing/TestBed#inject) | Same behavior, but type safe. |
|
|
||||||
|
|
||||||
|
|
||||||
{@a removals}
|
|
||||||
### New Removals of Deprecated APIs
|
|
||||||
|
|
||||||
| Package | API | Replacement | Notes |
|
|
||||||
| ------- | -------------- | ----------- | ----- |
|
|
||||||
| `@angular/core` | [`Renderer`](https://v8.angular.io/api/core/Renderer) | [`Renderer2`](api/core/Renderer2) | [Migration guide.](guide/migration-renderer) |
|
|
||||||
| `@angular/core` | [`RootRenderer`](https://v8.angular.io/api/core/RootRenderer) | [`RendererFactory2`](api/core/RendererFactory2) | none |
|
|
||||||
| `@angular/core` | [`RenderComponentType`](https://v8.angular.io/api/core/RenderComponentType) | [`RendererType2`](api/core/RendererType2) | none |
|
|
||||||
| `@angular/core` | [`WtfScopeFn`](https://v8.angular.io/api/core/WtfScopeFn) | none | v8 | See [Web Tracing Framework](#wtf) |
|
|
||||||
| `@angular/core` | [`wtfCreateScope`](https://v8.angular.io/api/core/wtfCreateScope) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
|
||||||
| `@angular/core` | [`wtfStartTimeRange`](https://v8.angular.io/api/core/wtfStartTimeRange) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
|
||||||
| `@angular/core` | [`wtfEndTimeRange`](https://v8.angular.io/api/core/wtfEndTimeRange) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
|
||||||
| `@angular/core` | [`wtfLeave`](https://v8.angular.io/api/core/wtfLeave) | none | v8 | See [Web Tracing Framework](guide/deprecations#wtf) |
|
|
||||||
| `@angular/common` | `DeprecatedI18NPipesModule` | [`CommonModule`](api/common/CommonModule#pipes) | none |
|
|
||||||
| `@angular/common` | `DeprecatedCurrencyPipe` | [`CurrencyPipe`](api/common/CurrencyPipe) | none |
|
|
||||||
| `@angular/common` | `DeprecatedDatePipe` | [`DatePipe`](api/common/DatePipe) | none |
|
|
||||||
| `@angular/common` | `DeprecatedDecimalPipe` | [`DecimalPipe`](api/common/DecimalPipe) | none |
|
|
||||||
| `@angular/common` | `DeprecatedPercentPipe` | [`PercentPipe`](api/common/PercentPipe) | none |
|
|
||||||
| `@angular/forms` | [`NgFormSelectorWarning`](https://v8.angular.io/api/forms/NgFormSelectorWarning) | none |
|
|
||||||
| `@angular/forms` | `ngForm` element selector | `ng-form` element selector | none |
|
|
||||||
| `@angular/service-worker` | `versionedFiles` | `files` | In the service worker configuration file `ngsw-config.json`, replace `versionedFiles` with `files`. See [Service Worker Configuration](guide/service-worker-config#assetgroups). |
|
|
||||||
|
|
||||||
{@a ivy}
|
|
||||||
|
|
||||||
## Ivy features and compatibility
|
|
||||||
|
|
||||||
In Version 9, Angular Ivy is the default rendering engine. If you haven't heard of Ivy, you can read more about it in the [Angular Ivy guide](guide/ivy).
|
|
||||||
|
|
||||||
* Among other features, Ivy introduces more comprehensive type-checking within templates. For details, see [Template Type-checking](guide/template-typecheck).
|
|
||||||
|
|
||||||
* For general guidance on debugging and a list of minor changes associated with Ivy, see the [Ivy compatibility guide](guide/ivy-compatibility).
|
|
||||||
|
|
||||||
* For help with opting out of Ivy, see the instructions [here](guide/ivy#opting-out-of-angular-ivy).
|
|
||||||
|
|
||||||
{@a migrations}
|
|
||||||
## Automated Migrations for Version 9
|
|
||||||
|
|
||||||
Read about the migrations the CLI handles for you automatically:
|
|
||||||
|
|
||||||
- [Migrating from `Renderer` to `Renderer2`](guide/migration-renderer)
|
|
||||||
- [Migrating missing `@Directive()`/`@Component()` decorators](guide/migration-undecorated-classes)
|
|
||||||
- [Migrating missing `@Injectable()` decorators and incomplete provider definitions](guide/migration-injectable)
|
|
||||||
- [Migrating dynamic queries](guide/migration-dynamic-flag)
|
|
||||||
- [Migrating to the new `$localize` i18n support](guide/migration-localize)
|
|
||||||
- [Migrating `ModuleWithProviders`](guide/migration-module-with-providers)
|
|
@ -1,7 +1,7 @@
|
|||||||
# Using published libraries
|
# Using published libraries
|
||||||
|
|
||||||
When building Angular applications you can take advantage of sophisticated first-party libraries, such as [Angular Material](https://material.angular.io/), as well as rich ecosystem of third-party libraries.
|
When building Angular applications you can take advantage of sophisticated first-party libraries, such as [Angular Material](https://material.angular.io/), as well as rich ecosystem of third-party libraries.
|
||||||
See the [Angular Resources](https://angular.io/resources) page for links to the most popular ones.
|
See the [Angular Resources](resources) page for links to the most popular ones.
|
||||||
|
|
||||||
## Installing libraries
|
## Installing libraries
|
||||||
|
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user