Compare commits
349 Commits
add-topnav
...
6.1.2
Author | SHA1 | Date | |
---|---|---|---|
3beb7116af | |||
4b1a825efc | |||
01e62551f5 | |||
2f23533a25 | |||
054fbbe8b8 | |||
155d938e04 | |||
94a2ac7884 | |||
b75a98522a | |||
d7dc1b5e44 | |||
e075ea7ae7 | |||
415519acd3 | |||
8cbb836985 | |||
8d0f8bd657 | |||
66547d8fd0 | |||
6e7d5f0925 | |||
29dfa5570a | |||
0c028a03ec | |||
a54c049051 | |||
40904ce0c4 | |||
88f01f5653 | |||
c66794c265 | |||
e4acd83541 | |||
a57f8a1301 | |||
ae9b4e6fa7 | |||
478eca31c7 | |||
2e1603938c | |||
0c9c2accc2 | |||
0fb41e5ced | |||
3f43dbb642 | |||
5069c06906 | |||
58698d7806 | |||
e26c25a062 | |||
0a6434b066 | |||
ff3550c304 | |||
6d4a14082c | |||
9ddf269c2c | |||
25a76a1492 | |||
8439a6ec2a | |||
1ef2eae3aa | |||
d5d034a0ff | |||
5ca35b3cd2 | |||
0a6a3f3163 | |||
3a601382e6 | |||
7a1fdde69e | |||
cbc2ea1b1a | |||
bdf801b0e8 | |||
fe5e8b7177 | |||
11f0f98ad8 | |||
801b534421 | |||
0fc83215e2 | |||
3d3a1a4642 | |||
32a40ba5de | |||
045271230d | |||
ec31f6bf9a | |||
4798d77088 | |||
08c6762039 | |||
26516045e7 | |||
a83b9f7911 | |||
1b7c77e49f | |||
3ab31a4be6 | |||
43dcf77123 | |||
d4bf2da3bd | |||
fa3882845a | |||
fa59748e00 | |||
c38ecb3b5b | |||
875efa8492 | |||
74964bde99 | |||
785fb5cc5a | |||
26d9f0278b | |||
22ebd53c17 | |||
a972c039c3 | |||
f5e18029fa | |||
317c7087c5 | |||
39abe7b7c1 | |||
36a7705a44 | |||
50a21885cf | |||
e86f3d9a49 | |||
738f2961ba | |||
f2bf8287ba | |||
9d5b34e1e7 | |||
d237f4014a | |||
8743a9bfd6 | |||
514d03f2d0 | |||
48d7205873 | |||
e1c6fd5453 | |||
968f153491 | |||
1e28495c89 | |||
0bcf20c9fa | |||
cf81823b07 | |||
d4ac9698ba | |||
c205516f0d | |||
777bd412b2 | |||
1e79014fc4 | |||
d0c066a223 | |||
65e18dc1bf | |||
1ceddb6290 | |||
22731a7588 | |||
72dd10f78f | |||
c0e3852384 | |||
2cb0f68a7b | |||
8450e0ab2f | |||
e38b2b502c | |||
445b9a5627 | |||
d523630ea2 | |||
d6016f1d1d | |||
be3cca4fd5 | |||
169e9dd2c8 | |||
13f3157823 | |||
edef58f466 | |||
7c89af34a9 | |||
bd576bb83f | |||
168c2a645b | |||
7729bb2bdc | |||
426324513d | |||
4d6f467fea | |||
6b859daea4 | |||
7960d1879d | |||
f1ab394218 | |||
86203736e9 | |||
41ef75869c | |||
2b8b647006 | |||
ed1db40322 | |||
d3594fc1c5 | |||
9fd70c9715 | |||
b7bbc82e3e | |||
139f5b3672 | |||
6f8ec256ef | |||
5d7005eef5 | |||
2e724ec68b | |||
76f8f78920 | |||
6eb6ac7c12 | |||
9644873023 | |||
ae4563202c | |||
42d4287153 | |||
f9a6a175bf | |||
53a16006d6 | |||
8a986d4642 | |||
e346c3c2f2 | |||
60aeee7abf | |||
1008bb6287 | |||
8a5cd2200a | |||
f58f3dc07a | |||
bb58138579 | |||
b8f740b253 | |||
23766b85e9 | |||
3cd9645daa | |||
2d38fa104b | |||
56b3f1703e | |||
c438b5eeda | |||
70b51a6255 | |||
7ebd8e59a8 | |||
1c533c913d | |||
ead3f926cb | |||
9be222f448 | |||
b137f09345 | |||
453693fd33 | |||
270176bbe4 | |||
5840a86f98 | |||
2aab1c9dd6 | |||
f9669e50ff | |||
99a393e84f | |||
d76531d16e | |||
23dc9a90b0 | |||
0b28732d77 | |||
06a33984af | |||
ba3eb8b654 | |||
c8ad9657c9 | |||
9be8abd012 | |||
74b250b146 | |||
d8c828c9b1 | |||
97277bc9fb | |||
1821b75530 | |||
82004c76ac | |||
a663565403 | |||
85d9c20b1d | |||
80a74b450a | |||
9a6f27c34c | |||
d723a69b31 | |||
d98b1c3bc4 | |||
02b5087685 | |||
48394c64ae | |||
cde0b4b361 | |||
9f20dd937a | |||
a1b630ee8f | |||
d05d28629d | |||
ee50ee493d | |||
161ff5c79d | |||
71e0df039c | |||
0399c6972a | |||
328971ffcc | |||
4d8b8ad372 | |||
0d6b74dd87 | |||
52d11f63cf | |||
a14f25c338 | |||
0b4d85e9f1 | |||
b9e095aa31 | |||
05e3e4d71e | |||
81a9db2b0a | |||
b7823e7087 | |||
3f8ab80583 | |||
ffb9dc6cf9 | |||
86d254d386 | |||
505b54b86b | |||
a527c695aa | |||
80576641a8 | |||
50fbed8e5f | |||
7d27ecc319 | |||
03616bcb43 | |||
3a19f70d1c | |||
dc1f1295ee | |||
49df4ef454 | |||
e1146f3d06 | |||
0d5f2d3c7e | |||
a167bca927 | |||
e3709f5d48 | |||
197387d05e | |||
1089261717 | |||
ddb792da28 | |||
89203c96ad | |||
3d20c50156 | |||
dcabb05102 | |||
68814040e3 | |||
3980640d53 | |||
52d43a99ef | |||
45feb10c46 | |||
250527ca68 | |||
94076c934c | |||
f936b8cbd2 | |||
d571a51739 | |||
86b1cc7313 | |||
787c54736c | |||
19544060d3 | |||
c0e2dba07b | |||
e01b539ee5 | |||
809e8f742e | |||
00c110b055 | |||
1e74ea9e60 | |||
f62876bbcb | |||
fddd2af4fc | |||
d5a9396017 | |||
3e6a722ddb | |||
5fe1e74dd3 | |||
f974c48885 | |||
568612349f | |||
b719905f9b | |||
56a8533cf3 | |||
b72dbc843f | |||
8fe8b8fcff | |||
b6af8700ce | |||
3d52174bf1 | |||
dbdcfed2bd | |||
ffbacdf4ac | |||
7f3242affb | |||
e3064d5432 | |||
0c3738a780 | |||
0922228024 | |||
c94a2c9e3f | |||
948e2236c0 | |||
a294e0dd79 | |||
3553977bd7 | |||
1ae3f87383 | |||
4e7a44c816 | |||
d1805d04d5 | |||
d243baf48a | |||
ff84c5c4da | |||
87ddbdf919 | |||
9803cb011e | |||
13d60eac61 | |||
d876700c26 | |||
99bdd257a6 | |||
3db9d57de3 | |||
66e50f28d2 | |||
0ede987ced | |||
71100e6d72 | |||
676ec411b9 | |||
01e7ff682c | |||
34c42836cf | |||
50d4a4fe5c | |||
69510acb20 | |||
ef1c6d8c26 | |||
2ecaa40e64 | |||
fc4dc35426 | |||
104d30507a | |||
c57b491778 | |||
1dc7d0d29e | |||
39c8baea31 | |||
abed2cd52c | |||
22758912a0 | |||
bb6b59128f | |||
4258c3d1df | |||
70156bc4ed | |||
2ac2ab7ff6 | |||
ca0a55f4ee | |||
0b3d25d67e | |||
24e0c3d43d | |||
922908818f | |||
8dec381145 | |||
32da3e1602 | |||
6d68f3e39a | |||
50fb13fb09 | |||
fe8fcc834c | |||
5c0e681bf3 | |||
7d6e833a6f | |||
49e900d6fc | |||
5feb9e1935 | |||
002a5afa98 | |||
cf0968f98e | |||
855e8ad9f6 | |||
89c442270a | |||
ae9418c7de | |||
166d90d2a9 | |||
7d318743c1 | |||
2a68ba4cbb | |||
d244523ae6 | |||
941d2cdaaf | |||
7d1f9c8a7c | |||
f841e36543 | |||
f229449c67 | |||
6e20e0aac8 | |||
1e139d4339 | |||
fba3f10938 | |||
c95437f15d | |||
39c7769c9e | |||
8c51ce6f3b | |||
71b0c3d469 | |||
b8760a0ca5 | |||
50fb58fd01 | |||
fe8fe9ba9e | |||
637805a0c9 | |||
7b2b1afe71 | |||
7d3fd4d655 | |||
10da6a45c6 | |||
84272e2227 | |||
cb31381734 | |||
3e1a3b2e32 | |||
1e6a226703 | |||
b91254fc43 | |||
8b8168262d | |||
a26965812b | |||
def354de16 | |||
9782736e00 | |||
e8354edcd2 | |||
5b76f04b7f | |||
a57825acf3 | |||
efc7639352 | |||
3e26cabe02 | |||
9d114c052a | |||
43e61c25e1 | |||
4e1493a1d6 |
@ -3,7 +3,10 @@
|
|||||||
# See remote cache documentation in /docs/BAZEL.md
|
# See remote cache documentation in /docs/BAZEL.md
|
||||||
|
|
||||||
# Don't be spammy in the logs
|
# Don't be spammy in the logs
|
||||||
build --noshow_progress
|
# TODO(gmagolan): Hide progress again once build performance improves
|
||||||
|
# Presently, CircleCI can timeout during bazel test ... with the following
|
||||||
|
# error: Too long with no output (exceeded 10m0s)
|
||||||
|
# build --noshow_progress
|
||||||
|
|
||||||
# Don't run manual tests
|
# Don't run manual tests
|
||||||
test --test_tag_filters=-manual
|
test --test_tag_filters=-manual
|
||||||
|
@ -12,8 +12,8 @@
|
|||||||
## IMPORTANT
|
## IMPORTANT
|
||||||
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
|
# If you change the `docker_image` version, also change the `cache_key` suffix and the version of
|
||||||
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
|
# `com_github_bazelbuild_buildtools` in the `/WORKSPACE` file.
|
||||||
var_1: &docker_image angular/ngcontainer:0.3.2
|
var_1: &docker_image angular/ngcontainer:0.3.3
|
||||||
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.3.2
|
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.3.3
|
||||||
|
|
||||||
# Define common ENV vars
|
# Define common ENV vars
|
||||||
var_3: &define_env_vars
|
var_3: &define_env_vars
|
||||||
|
197
.pullapprove.yml
197
.pullapprove.yml
@ -8,6 +8,7 @@
|
|||||||
# alexeagle - Alex Eagle
|
# alexeagle - Alex Eagle
|
||||||
# alxhub - Alex Rickabaugh
|
# alxhub - Alex Rickabaugh
|
||||||
# andrewseguin - Andrew Seguin
|
# andrewseguin - Andrew Seguin
|
||||||
|
# benlesh - Ben Lesh
|
||||||
# brandonroberts - Brandon Roberts
|
# brandonroberts - Brandon Roberts
|
||||||
# brocco - Mike Brocchi
|
# brocco - Mike Brocchi
|
||||||
# filipesilva - Filipe Silva
|
# filipesilva - Filipe Silva
|
||||||
@ -15,7 +16,7 @@
|
|||||||
# hansl - Hans Larsen
|
# hansl - Hans Larsen
|
||||||
# IgorMinar - Igor Minar
|
# IgorMinar - Igor Minar
|
||||||
# jasonaden - Jason Aden
|
# jasonaden - Jason Aden
|
||||||
# kapunahelewong - Kapunahele Wong
|
# jenniferfell - Jennifer Fell
|
||||||
# kara - Kara Erickson
|
# kara - Kara Erickson
|
||||||
# kyliau - Keen Yee Liau
|
# kyliau - Keen Yee Liau
|
||||||
# matsko - Matias Niemelä
|
# matsko - Matias Niemelä
|
||||||
@ -23,7 +24,6 @@
|
|||||||
# petebacondarwin - Pete Bacon Darwin
|
# petebacondarwin - Pete Bacon Darwin
|
||||||
# pkozlowski-opensource - Pawel Kozlowski
|
# pkozlowski-opensource - Pawel Kozlowski
|
||||||
# robwormald - Rob Wormald
|
# robwormald - Rob Wormald
|
||||||
# tinayuangao - Tina Gao
|
|
||||||
# vicb - Victor Berchet
|
# vicb - Victor Berchet
|
||||||
# vikerman - Vikram Subramanian
|
# vikerman - Vikram Subramanian
|
||||||
|
|
||||||
@ -92,6 +92,7 @@ groups:
|
|||||||
- "*.bzl"
|
- "*.bzl"
|
||||||
- "packages/bazel/*"
|
- "packages/bazel/*"
|
||||||
- "tools/bazel.rc"
|
- "tools/bazel.rc"
|
||||||
|
- "/docs/BAZEL.md"
|
||||||
users:
|
users:
|
||||||
- alexeagle #primary
|
- alexeagle #primary
|
||||||
- kyliau
|
- kyliau
|
||||||
@ -131,42 +132,113 @@ groups:
|
|||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/core/*"
|
- "packages/core/*"
|
||||||
|
- "aio/content/guide/bootstrapping.md"
|
||||||
|
- "aio/content/examples/bootstrapping/*"
|
||||||
|
- "aio/content/guide/attribute-directives.md"
|
||||||
|
- "aio/content/examples/attribute-directives/*"
|
||||||
|
- "aio/content/images/guide/attribute-directives/*"
|
||||||
|
- "aio/content/guide/structural-directives.md"
|
||||||
|
- "aio/content/examples/structural-directives/*"
|
||||||
|
- "aio/content/images/guide/structural-directives/*"
|
||||||
|
- "aio/content/guide/dynamic-component-loader.md"
|
||||||
|
- "aio/content/examples/dynamic-component-loader/*"
|
||||||
|
- "aio/content/images/guide/dynamic-component-loader/*"
|
||||||
|
- "aio/content/guide/template-syntax.md"
|
||||||
|
- "aio/content/examples/template-syntax/*"
|
||||||
|
- "aio/content/images/guide/template-syntax/*"
|
||||||
|
- "aio/content/guide/dependency-injection.md"
|
||||||
|
- "aio/content/examples/dependency-injection/*"
|
||||||
|
- "aio/content/images/guide/dependency-injection/*"
|
||||||
|
- "aio/content/guide/dependency-injection-in-action.md"
|
||||||
|
- "aio/content/examples/dependency-injection-in-action/*"
|
||||||
|
- "aio/content/images/guide/dependency-injection-in-action/*"
|
||||||
|
- "aio/content/guide/hierarchical-dependency-injection.md"
|
||||||
|
- "aio/content/examples/hierarchical-dependency-injection/*"
|
||||||
|
- "aio/content/guide/singleton-services.md"
|
||||||
|
- "aio/content/guide/dependency-injection-pattern.md"
|
||||||
|
- "aio/content/guide/providers.md"
|
||||||
|
- "aio/content/examples/providers/*"
|
||||||
|
- "aio/content/guide/component-interaction.md"
|
||||||
|
- "aio/content/examples/component-interaction/*"
|
||||||
|
- "aio/content/images/guide/component-interaction/*"
|
||||||
|
- "aio/content/guide/component-styles.md"
|
||||||
|
- "aio/content/examples/component-styles/*"
|
||||||
|
- "aio/content/guide/lifecycle-hooks.md"
|
||||||
|
- "aio/content/examples/lifecycle-hooks/*"
|
||||||
|
- "aio/content/images/guide/lifecycle-hooks/*"
|
||||||
|
- "aio/content/examples/ngcontainer/*"
|
||||||
|
- "aio/content/images/guide/ngcontainer/*"
|
||||||
|
- "aio/content/guide/pipes.md"
|
||||||
|
- "aio/content/examples/pipes/*"
|
||||||
|
- "aio/content/images/guide/pipes/*"
|
||||||
|
- "aio/content/guide/entry-components.md"
|
||||||
|
- "aio/content/guide/set-document-title.md"
|
||||||
|
- "aio/content/examples/set-document-title/*"
|
||||||
|
- "aio/content/images/guide/set-document-title/*"
|
||||||
|
- "aio/content/guide/ngmodules.md"
|
||||||
|
- "aio/content/examples/ngmodules/*"
|
||||||
|
- "aio/content/examples/ngmodule/*"
|
||||||
|
- "aio/content/images/guide/ngmodule/*"
|
||||||
|
- "aio/content/guide/ngmodule-faq.md"
|
||||||
|
- "aio/content/examples/ngmodule-faq/*"
|
||||||
|
- "aio/content/guide/module-types.md"
|
||||||
|
- "aio/content/guide/sharing-ngmodules.md"
|
||||||
|
- "aio/content/guide/frequent-ngmodules.md"
|
||||||
|
- "aio/content/images/guide/frequent-ngmodules/*"
|
||||||
|
- "aio/content/guide/ngmodule-api.md"
|
||||||
|
- "aio/content/guide/ngmodule-vs-jsmodule.md"
|
||||||
|
- "aio/content/guide/feature-modules.md"
|
||||||
|
- "aio/content/examples/feature-modules/*"
|
||||||
|
- "aio/content/images/guide/feature-modules/*"
|
||||||
|
- "aio/content/guide/lazy-loading-ngmodules.md"
|
||||||
|
- "aio/content/examples/lazy-loading-ngmodules/*"
|
||||||
|
- "aio/content/images/guide/lazy-loading-ngmodules"
|
||||||
users:
|
users:
|
||||||
- mhevery #primary
|
- mhevery #primary
|
||||||
- jasonaden
|
- jasonaden
|
||||||
- kara
|
- kara
|
||||||
- vicb
|
- vicb
|
||||||
- IgorMinar #fallback
|
- IgorMinar
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
animations:
|
animations:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/animations/*"
|
- "packages/animations/*"
|
||||||
- "packages/platform-browser/animations/*"
|
- "packages/platform-browser/animations/*"
|
||||||
|
- "aio/content/guide/animations.md"
|
||||||
|
- "aio/content/examples/animations/*"
|
||||||
|
- "aio/content/images/guide/animations/*"
|
||||||
users:
|
users:
|
||||||
- matsko #primary
|
- matsko #primary
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
compiler/i18n:
|
compiler/i18n:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/compiler/src/i18n/*"
|
- "packages/compiler/src/i18n/*"
|
||||||
|
- "aio/content/guide/i18n.md"
|
||||||
|
- "aio/content/examples/i18n/*"
|
||||||
users:
|
users:
|
||||||
- vicb #primary
|
- vicb #primary
|
||||||
- alxhub
|
- alxhub
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
compiler:
|
compiler:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/compiler/*"
|
- "packages/compiler/*"
|
||||||
|
- "aio/content/guide/aot-compiler.md"
|
||||||
users:
|
users:
|
||||||
- alxhub #primary
|
- alxhub #primary
|
||||||
- vicb
|
- vicb
|
||||||
- mhevery
|
- mhevery
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
compiler-cli/ngtools:
|
compiler-cli/ngtools:
|
||||||
conditions:
|
conditions:
|
||||||
@ -175,7 +247,6 @@ groups:
|
|||||||
users:
|
users:
|
||||||
- hansl
|
- hansl
|
||||||
- filipesilva #fallback
|
- filipesilva #fallback
|
||||||
- brocco #fallback
|
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
|
|
||||||
compiler-cli:
|
compiler-cli:
|
||||||
@ -211,57 +282,97 @@ groups:
|
|||||||
files:
|
files:
|
||||||
- "packages/forms/*"
|
- "packages/forms/*"
|
||||||
- "aio/content/guide/forms.md"
|
- "aio/content/guide/forms.md"
|
||||||
- "aio/content/guide/form-validation.md"
|
|
||||||
- "aio/content/guide/reactive-forms.md"
|
|
||||||
- "aio/content/examples/forms/*"
|
- "aio/content/examples/forms/*"
|
||||||
|
- "aio/content/images/guide/forms/*"
|
||||||
|
- "aio/content/guide/form-validation.md"
|
||||||
- "aio/content/examples/form-validation/*"
|
- "aio/content/examples/form-validation/*"
|
||||||
|
- "aio/content/images/guide/form-validation/*"
|
||||||
|
- "aio/content/guide/dynamic-form.md"
|
||||||
|
- "aio/content/examples/dynamic-form/*"
|
||||||
|
- "aio/content/images/guide/dynamic-form/*"
|
||||||
|
- "aio/content/guide/reactive-forms.md"
|
||||||
- "aio/content/examples/reactive-forms/*"
|
- "aio/content/examples/reactive-forms/*"
|
||||||
|
- "aio/content/images/guide/reactive-forms/*"
|
||||||
users:
|
users:
|
||||||
- kara #primary
|
- kara #primary
|
||||||
- tinayuangao #secondary
|
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
http:
|
http:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/common/http/*"
|
- "packages/common/http/*"
|
||||||
- "packages/http/*"
|
- "packages/http/*"
|
||||||
|
- "aio/content/guide/http.md"
|
||||||
|
- "aio/content/examples/http/*"
|
||||||
|
- "aio/content/images/guide/http/*"
|
||||||
users:
|
users:
|
||||||
- alxhub #primary
|
- alxhub #primary
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
language-service:
|
language-service:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/language-service/*"
|
- "packages/language-service/*"
|
||||||
|
- "aio/content/guide/language-service.md"
|
||||||
|
- "aio/content/images/guide/language-service/*"
|
||||||
users:
|
users:
|
||||||
- kyliau #primary
|
- kyliau #primary
|
||||||
# needs secondary
|
# needs secondary
|
||||||
- vicb
|
- vicb
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
router:
|
router:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/router/*"
|
- "packages/router/*"
|
||||||
|
- "aio/content/guide/router.md"
|
||||||
|
- "aio/content/examples/router/*"
|
||||||
|
- "aio/content/images/guide/router/*"
|
||||||
users:
|
users:
|
||||||
- jasonaden #primary
|
- jasonaden #primary
|
||||||
- vicb
|
- vicb
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
|
testing:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "*/testing/*"
|
||||||
|
- "aio/content/guide/testing.md"
|
||||||
|
- "aio/content/examples/testing/*"
|
||||||
|
- "aio/content/images/guide/testing/*"
|
||||||
|
users:
|
||||||
|
- vikerman
|
||||||
|
- IgorMinar #fallback
|
||||||
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
upgrade:
|
upgrade:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/upgrade/*"
|
- "packages/upgrade/*"
|
||||||
|
- "aio/content/guide/upgrade.md"
|
||||||
|
- "aio/content/examples/upgrade-module/*"
|
||||||
|
- "aio/content/images/guide/upgrade/*"
|
||||||
|
- "aio/content/examples/upgrade-phonecat-1-typescript/*"
|
||||||
|
- "aio/content/examples/upgrade-phonecat-2-hybrid/*"
|
||||||
|
- "aio/content/examples/upgrade-phonecat-3-final/*"
|
||||||
|
- "aio/content/guide/upgrade-performance.md"
|
||||||
|
- "aio/content/guide/ajs-quick-reference.md"
|
||||||
|
- "aio/content/examples/ajs-quick-reference/*"
|
||||||
users:
|
users:
|
||||||
- petebacondarwin #primary
|
- petebacondarwin #primary
|
||||||
- gkalpak
|
- gkalpak
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
platform-browser:
|
platform-browser:
|
||||||
conditions:
|
conditions:
|
||||||
@ -277,12 +388,15 @@ groups:
|
|||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/platform-server/*"
|
- "packages/platform-server/*"
|
||||||
|
- "aio/content/guide/universal.md"
|
||||||
|
- "aio/content/examples/universal/*"
|
||||||
users:
|
users:
|
||||||
- vikerman #primary
|
- vikerman #primary
|
||||||
- alxhub #secondary
|
- alxhub #secondary
|
||||||
- vicb
|
- vicb
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
platform-webworker:
|
platform-webworker:
|
||||||
conditions:
|
conditions:
|
||||||
@ -298,22 +412,34 @@ groups:
|
|||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/service-worker/*"
|
- "packages/service-worker/*"
|
||||||
|
- "aio/content/guide/service-worker-getting-started.md"
|
||||||
|
- "aio/content/examples/service-worker-getting-started/*"
|
||||||
|
- "aio/content/guide/service-worker-communications.md"
|
||||||
|
- "aio/content/guide/service-worker-config.md"
|
||||||
|
- "aio/content/guide/service-worker-devops.md"
|
||||||
|
- "aio/content/guide/service-worker-intro.md"
|
||||||
|
- "aio/content/images/guide/service-worker/*"
|
||||||
users:
|
users:
|
||||||
- alxhub #primary
|
- gkalpak #primary
|
||||||
- gkalpak
|
- alxhub
|
||||||
- IgorMinar #fallback
|
- IgorMinar
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
elements:
|
elements:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/elements/*"
|
- "packages/elements/*"
|
||||||
|
- "aio/content/examples/elements/*"
|
||||||
|
- "aio/content/images/guide/elements/*"
|
||||||
|
- "aio/content/guide/elements.md"
|
||||||
users:
|
users:
|
||||||
- andrewseguin #primary
|
- andrewseguin #primary
|
||||||
- gkalpak
|
- gkalpak
|
||||||
- robwormald
|
- robwormald
|
||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
benchpress:
|
benchpress:
|
||||||
conditions:
|
conditions:
|
||||||
@ -325,7 +451,7 @@ groups:
|
|||||||
- IgorMinar #fallback
|
- IgorMinar #fallback
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
|
||||||
angular.io:
|
docs-infra:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
include:
|
include:
|
||||||
@ -338,7 +464,7 @@ groups:
|
|||||||
- gkalpak
|
- gkalpak
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
|
||||||
angular.io-guide-and-tutorial:
|
docs/guide-and-tutorial:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
include:
|
include:
|
||||||
@ -348,19 +474,20 @@ groups:
|
|||||||
- "aio/content/navigation.json"
|
- "aio/content/navigation.json"
|
||||||
- "aio/content/license.md"
|
- "aio/content/license.md"
|
||||||
users:
|
users:
|
||||||
- kapunahelewong
|
|
||||||
- stephenfluin
|
- stephenfluin
|
||||||
|
- jenniferfell
|
||||||
|
- brandonroberts
|
||||||
- petebacondarwin
|
- petebacondarwin
|
||||||
- gkalpak
|
- gkalpak
|
||||||
- IgorMinar
|
- IgorMinar
|
||||||
- brandonroberts
|
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
|
||||||
angular.io-marketing:
|
docs/marketing:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
include:
|
include:
|
||||||
- "aio/content/marketing/*"
|
- "aio/content/marketing/*"
|
||||||
|
- "aio/content/images/marketing/*"
|
||||||
- "aio/content/navigation.json"
|
- "aio/content/navigation.json"
|
||||||
- "aio/content/license.md"
|
- "aio/content/license.md"
|
||||||
users:
|
users:
|
||||||
@ -370,3 +497,43 @@ groups:
|
|||||||
- IgorMinar
|
- IgorMinar
|
||||||
- robwormald
|
- robwormald
|
||||||
- mhevery #fallback
|
- mhevery #fallback
|
||||||
|
|
||||||
|
docs/observables:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "aio/content/examples/observables/*"
|
||||||
|
- "aio/content/images/guide/observables/*"
|
||||||
|
- "aio/content/guide/observables.md"
|
||||||
|
- "aio/content/guide/comparing-observables.md"
|
||||||
|
- "aio/content/examples/observables-in-angular/*"
|
||||||
|
- "aio/content/images/guide/observables-in-angular/*"
|
||||||
|
- "aio/content/guide/observables-in-angular.md"
|
||||||
|
- "aio/content/examples/practical-observable-usage/*"
|
||||||
|
- "aio/content/guide/practical-observable-usage.md"
|
||||||
|
- "aio/content/examples/rx-library/*"
|
||||||
|
- "aio/content/guide/rx-library.md"
|
||||||
|
users:
|
||||||
|
- jasonaden
|
||||||
|
- benlesh
|
||||||
|
- IgorMinar
|
||||||
|
- mhevery
|
||||||
|
- jenniferfell #docs only
|
||||||
|
|
||||||
|
docs/packaging:
|
||||||
|
conditions:
|
||||||
|
files:
|
||||||
|
- "aio/content/guide/npm-packages.md"
|
||||||
|
- "aio/content/guide/browser-support.md"
|
||||||
|
- "aio/content/guide/typescript-configuration.md"
|
||||||
|
- "aio/content/guide/setup-systemjs-anatomy.md"
|
||||||
|
- "aio/content/examples/setup/*"
|
||||||
|
- "aio/content/guide/setup.md"
|
||||||
|
- "aio/content/guide/deployment.md"
|
||||||
|
- "aio/content/guide/releases.md"
|
||||||
|
- "aio/content/guide/updating.md"
|
||||||
|
users:
|
||||||
|
- IgorMinar #primary
|
||||||
|
- alexeagle
|
||||||
|
- hansl
|
||||||
|
- mhevery #fallback
|
||||||
|
- jenniferfell #docs only
|
||||||
|
40
BUILD.bazel
40
BUILD.bazel
@ -5,6 +5,7 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "node_modules_filegroup")
|
|||||||
exports_files([
|
exports_files([
|
||||||
"tsconfig.json",
|
"tsconfig.json",
|
||||||
"LICENSE",
|
"LICENSE",
|
||||||
|
"protractor-perf.conf.js",
|
||||||
])
|
])
|
||||||
|
|
||||||
# Developers should always run `bazel run :install`
|
# Developers should always run `bazel run :install`
|
||||||
@ -14,36 +15,19 @@ alias(
|
|||||||
actual = "@nodejs//:yarn",
|
actual = "@nodejs//:yarn",
|
||||||
)
|
)
|
||||||
|
|
||||||
node_modules_filegroup(
|
alias(
|
||||||
name = "node_modules",
|
name = "node_modules",
|
||||||
packages = [
|
actual = "@angular_deps//:node_modules",
|
||||||
"bytebuffer",
|
|
||||||
"hammerjs",
|
|
||||||
"jasmine",
|
|
||||||
"minimist",
|
|
||||||
"protobufjs",
|
|
||||||
"reflect-metadata",
|
|
||||||
"source-map-support",
|
|
||||||
"tsickle",
|
|
||||||
"tslib",
|
|
||||||
"tsutils",
|
|
||||||
"typescript",
|
|
||||||
"zone.js",
|
|
||||||
"@angular-devkit/core",
|
|
||||||
"@angular-devkit/schematics",
|
|
||||||
"@types",
|
|
||||||
"@webcomponents/custom-elements",
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "web_test_bootstrap_scripts",
|
name = "web_test_bootstrap_scripts",
|
||||||
# do not sort
|
# do not sort
|
||||||
srcs = [
|
srcs = [
|
||||||
"//:node_modules/reflect-metadata/Reflect.js",
|
"@angular_deps//:node_modules/reflect-metadata/Reflect.js",
|
||||||
"//:node_modules/zone.js/dist/zone.js",
|
"@angular_deps//:node_modules/zone.js/dist/zone.js",
|
||||||
"//:node_modules/zone.js/dist/zone-testing.js",
|
"@angular_deps//:node_modules/zone.js/dist/zone-testing.js",
|
||||||
"//:node_modules/zone.js/dist/task-tracking.js",
|
"@angular_deps//:node_modules/zone.js/dist/task-tracking.js",
|
||||||
"//:test-events.js",
|
"//:test-events.js",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -51,9 +35,11 @@ filegroup(
|
|||||||
filegroup(
|
filegroup(
|
||||||
name = "angularjs_scripts",
|
name = "angularjs_scripts",
|
||||||
srcs = [
|
srcs = [
|
||||||
"//:node_modules/angular-1.5/angular.js",
|
"@angular_deps//:node_modules/angular-1.5/angular.js",
|
||||||
"//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
"@angular_deps//:node_modules/angular-1.6/angular.js",
|
||||||
"//:node_modules/angular-mocks/angular-mocks.js",
|
"@angular_deps//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
||||||
"//:node_modules/angular/angular.js",
|
"@angular_deps//:node_modules/angular-mocks-1.6/angular-mocks.js",
|
||||||
|
"@angular_deps//:node_modules/angular-mocks/angular-mocks.js",
|
||||||
|
"@angular_deps//:node_modules/angular/angular.js",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
217
CHANGELOG.md
217
CHANGELOG.md
@ -1,33 +1,169 @@
|
|||||||
<a name="6.1.0-beta.1"></a>
|
<a name="6.1.2"></a>
|
||||||
# [6.1.0-beta.1](https://github.com/angular/angular/compare/6.1.0-beta.0...6.1.0-beta.1) (2018-06-13)
|
## [6.1.2](https://github.com/angular/angular/compare/6.1.1...6.1.2) (2018-08-08)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **router:** take base uri into account in `setUpLocationSync()` ([#20244](https://github.com/angular/angular/issues/20244)) ([ae9b4e6](https://github.com/angular/angular/commit/ae9b4e6)), closes [#20061](https://github.com/angular/angular/issues/20061)
|
||||||
|
* add mappings for ngfactory & ngsummary files to their module names in aot summary resolver ([#25335](https://github.com/angular/angular/issues/25335)) ([054fbbe](https://github.com/angular/angular/commit/054fbbe))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="6.1.1"></a>
|
||||||
|
## [6.1.1](https://github.com/angular/angular/compare/6.1.0...6.1.1) (2018-08-02)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler-cli:** correct tsickle dependency version to fix typescript 2.9 compatibility ([fec29fa](https://github.com/angular/angular/commit/317c7087c56b72aa74cd6d6a8f719e6e7fec29fa))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="6.1.0"></a>
|
||||||
|
# [6.1.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0) (2018-07-25)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([dc4a3d0](https://github.com/angular/angular/commit/dc4a3d0))
|
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([dc4a3d0](https://github.com/angular/angular/commit/dc4a3d0))
|
||||||
|
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
|
||||||
|
* **animations:** do not throw errors when a destroyed component is animated ([#23836](https://github.com/angular/angular/issues/23836)) ([d2a8687](https://github.com/angular/angular/commit/d2a8687))
|
||||||
|
* **animations:** Fix browser detection logic ([#24188](https://github.com/angular/angular/issues/24188)) ([b492b9e](https://github.com/angular/angular/commit/b492b9e))
|
||||||
|
* **animations:** properly clean up queried element styles in safari/edge ([#23633](https://github.com/angular/angular/issues/23633)) ([da9ff25](https://github.com/angular/angular/commit/da9ff25))
|
||||||
|
* **animations:** retain state styling for nodes that are moved around ([#23534](https://github.com/angular/angular/issues/23534)) ([65211f4](https://github.com/angular/angular/commit/65211f4))
|
||||||
|
* **animations:** retain trigger-state for nodes that are moved around ([#24238](https://github.com/angular/angular/issues/24238)) ([8db928d](https://github.com/angular/angular/commit/8db928d))
|
||||||
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([282d351](https://github.com/angular/angular/commit/282d351))
|
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([282d351](https://github.com/angular/angular/commit/282d351))
|
||||||
* **docs-infra:** use script nomodule to load IE polyfills, skip other polyfills ([#24317](https://github.com/angular/angular/issues/24317)) ([8be6892](https://github.com/angular/angular/commit/8be6892)), closes [#23647](https://github.com/angular/angular/issues/23647)
|
* **benchpress:** Fix promise chain in chrome_driver_extension. ([#23458](https://github.com/angular/angular/issues/23458)) ([d4b6c41](https://github.com/angular/angular/commit/d4b6c41))
|
||||||
* **ivy:** compute transitive scopes from NgModuleDef only ([#24334](https://github.com/angular/angular/issues/24334)) ([1135563](https://github.com/angular/angular/commit/1135563))
|
* **common:** do not round factional seconds ([#24831](https://github.com/angular/angular/issues/24831)) ([a527c69](https://github.com/angular/angular/commit/a527c69)), closes [#24384](https://github.com/angular/angular/issues/24384)
|
||||||
* **ivy:** correctly handle queries with embedded views ([#24418](https://github.com/angular/angular/issues/24418)) ([014949f](https://github.com/angular/angular/commit/014949f))
|
* **common:** format fractional seconds ([#24844](https://github.com/angular/angular/issues/24844)) ([0b4d85e](https://github.com/angular/angular/commit/0b4d85e)), closes [#24831](https://github.com/angular/angular/issues/24831)
|
||||||
* **ivy:** remove debugger statement ([#24480](https://github.com/angular/angular/issues/24480)) ([70ef061](https://github.com/angular/angular/commit/70ef061))
|
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([ff84c5c](https://github.com/angular/angular/commit/ff84c5c)), closes [#24155](https://github.com/angular/angular/issues/24155)
|
||||||
* **ivy:** special case [style] and [class] bindings for future use ([#23232](https://github.com/angular/angular/issues/23232)) ([1b253e1](https://github.com/angular/angular/commit/1b253e1))
|
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([dcabb05](https://github.com/angular/angular/commit/dcabb05)), closes [#24609](https://github.com/angular/angular/issues/24609)
|
||||||
|
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
|
||||||
|
* **compiler:** fix a few non-tree-shakeable code patterns ([#24677](https://github.com/angular/angular/issues/24677)) ([50d4a4f](https://github.com/angular/angular/commit/50d4a4f))
|
||||||
|
* **compiler:** i18n_extractor now outputs the correct source file name ([#24885](https://github.com/angular/angular/issues/24885)) ([c8ad965](https://github.com/angular/angular/commit/c8ad965)), closes [#24884](https://github.com/angular/angular/issues/24884)
|
||||||
|
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([d8f7b29](https://github.com/angular/angular/commit/d8f7b29)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||||
|
* **compiler:** avoid a crash in ngc-wrapped. ([#23468](https://github.com/angular/angular/issues/23468)) ([e1c4930](https://github.com/angular/angular/commit/e1c4930))
|
||||||
|
* **compiler:** generate constant array for i18n attributes ([#23837](https://github.com/angular/angular/issues/23837)) ([cfde36d](https://github.com/angular/angular/commit/cfde36d))
|
||||||
|
* **compiler:** generate core-compliant hostBindings property ([#24087](https://github.com/angular/angular/issues/24087)) ([01b5acd](https://github.com/angular/angular/commit/01b5acd)), closes [#24013](https://github.com/angular/angular/issues/24013)
|
||||||
|
* **compiler:** handle undefined annotation metadata ([#23349](https://github.com/angular/angular/issues/23349)) ([ca776c5](https://github.com/angular/angular/commit/ca776c5))
|
||||||
|
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([0d5f2d3](https://github.com/angular/angular/commit/0d5f2d3))
|
||||||
|
* **compiler-cli:** don't rely on incompatible TS method ([#23550](https://github.com/angular/angular/issues/23550)) ([b1f040f](https://github.com/angular/angular/commit/b1f040f))
|
||||||
|
* **core:** stop reusing provider definitions across NgModuleRef instances ([#25022](https://github.com/angular/angular/issues/25022)) ([6b859da](https://github.com/angular/angular/commit/6b859da)), closes [#25018](https://github.com/angular/angular/issues/25018)
|
||||||
|
* **core:** mark NgModule as not the root if APP_ROOT is set to false ([#24814](https://github.com/angular/angular/issues/24814)) ([1089261](https://github.com/angular/angular/commit/1089261))
|
||||||
|
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([0922228](https://github.com/angular/angular/commit/0922228)), closes [#22939](https://github.com/angular/angular/issues/22939)
|
||||||
|
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([ccbda9d](https://github.com/angular/angular/commit/ccbda9d))
|
||||||
|
* **core:** avoid eager providers re-initialization ([#23559](https://github.com/angular/angular/issues/23559)) ([0c6dc45](https://github.com/angular/angular/commit/0c6dc45))
|
||||||
|
* **core:** call ngOnDestroy on all services that have it ([#23755](https://github.com/angular/angular/issues/23755)) ([fc03427](https://github.com/angular/angular/commit/fc03427)), closes [#22466](https://github.com/angular/angular/issues/22466) [#22240](https://github.com/angular/angular/issues/22240) [#14818](https://github.com/angular/angular/issues/14818)
|
||||||
|
* **docs-infra:** fix table header layout in API pages ([#24919](https://github.com/angular/angular/issues/24919)) ([3cd9645](https://github.com/angular/angular/commit/3cd9645))
|
||||||
|
* **elements:** always check to create strategy ([#23825](https://github.com/angular/angular/issues/23825)) ([b1cda36](https://github.com/angular/angular/commit/b1cda36))
|
||||||
|
* **elements:** prevent closure renaming of platform properties ([#23843](https://github.com/angular/angular/issues/23843)) ([d4b8b24](https://github.com/angular/angular/commit/d4b8b24))
|
||||||
|
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([9367e91](https://github.com/angular/angular/commit/9367e91)), closes [#17195](https://github.com/angular/angular/issues/17195)
|
||||||
|
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([6881404](https://github.com/angular/angular/commit/6881404)), closes [#21420](https://github.com/angular/angular/issues/21420)
|
||||||
|
* **platform-browser:** add missing deps for HammerGesturesPlugin ([#24682](https://github.com/angular/angular/issues/24682)) ([13d60ea](https://github.com/angular/angular/commit/13d60ea))
|
||||||
|
* **platform-browser:** mark Meta and Title services as tree shakable providers ([#24815](https://github.com/angular/angular/issues/24815)) ([197387d](https://github.com/angular/angular/commit/197387d))
|
||||||
|
* **platform-browser:** workaround wrong import path generated by ngc for DOCUMENT ([#24830](https://github.com/angular/angular/issues/24830)) ([7d27ecc](https://github.com/angular/angular/commit/7d27ecc))
|
||||||
|
* **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([b96a3c8](https://github.com/angular/angular/commit/b96a3c8))
|
||||||
|
* **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([60aa943](https://github.com/angular/angular/commit/60aa943)), closes [#23023](https://github.com/angular/angular/issues/23023)
|
||||||
|
* **platform-server:** don't reflect innerHTML property to attribute ([#24213](https://github.com/angular/angular/issues/24213)) ([6a663a4](https://github.com/angular/angular/commit/6a663a4)), closes [#19278](https://github.com/angular/angular/issues/19278)
|
||||||
|
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([c73196e](https://github.com/angular/angular/commit/c73196e)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
|
||||||
|
* **router:** Fix _lastPathIndex in deeply nested empty paths ([#22394](https://github.com/angular/angular/issues/22394)) ([968f153](https://github.com/angular/angular/commit/968f153))
|
||||||
|
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([86d254d](https://github.com/angular/angular/commit/86d254d)), closes [#21468](https://github.com/angular/angular/issues/21468)
|
||||||
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([5731d07](https://github.com/angular/angular/commit/5731d07)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([5731d07](https://github.com/angular/angular/commit/5731d07)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
||||||
|
* **router:** avoid freezing queryParams in-place ([#22663](https://github.com/angular/angular/issues/22663)) ([89f64e5](https://github.com/angular/angular/commit/89f64e5)), closes [#22617](https://github.com/angular/angular/issues/22617)
|
||||||
|
* **router:** cache route handle if found ([#22475](https://github.com/angular/angular/issues/22475)) ([4cfa571](https://github.com/angular/angular/commit/4cfa571)), closes [#22474](https://github.com/angular/angular/issues/22474)
|
||||||
|
* **router:** correct the segment parsing so it won't break on ampersand ([#23684](https://github.com/angular/angular/issues/23684)) ([553a680](https://github.com/angular/angular/commit/553a680))
|
||||||
|
* **service-worker:** don't include sourceMappingURL in ngsw-worker ([#24877](https://github.com/angular/angular/issues/24877)) ([8620373](https://github.com/angular/angular/commit/8620373)), closes [#23596](https://github.com/angular/angular/issues/23596)
|
||||||
|
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([52d43a9](https://github.com/angular/angular/commit/52d43a9))
|
||||||
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([3ed2d75](https://github.com/angular/angular/commit/3ed2d75)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([3ed2d75](https://github.com/angular/angular/commit/3ed2d75)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
||||||
|
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([fb59b2d](https://github.com/angular/angular/commit/fb59b2d)), closes [#23196](https://github.com/angular/angular/issues/23196)
|
||||||
|
* **service-worker:** check platformBrowser before accessing navigator.serviceWorker ([#21231](https://github.com/angular/angular/issues/21231)) ([0bdd30e](https://github.com/angular/angular/commit/0bdd30e))
|
||||||
|
* **service-worker:** correctly handle requests with empty `clientId` ([#23625](https://github.com/angular/angular/issues/23625)) ([e0ed59e](https://github.com/angular/angular/commit/e0ed59e)), closes [#23526](https://github.com/angular/angular/issues/23526)
|
||||||
|
* **service-worker:** deprecate `versionedFiles` in asset-group resources ([#23584](https://github.com/angular/angular/issues/23584)) ([1d378e2](https://github.com/angular/angular/commit/1d378e2))
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* **bazel:** Initial commit of protractor_web_test_suite ([#24787](https://github.com/angular/angular/issues/24787)) ([71e0df0](https://github.com/angular/angular/commit/71e0df0))
|
||||||
|
* **bazel:** protractor_web_test_suite for release ([#24787](https://github.com/angular/angular/issues/24787)) ([161ff5c](https://github.com/angular/angular/commit/161ff5c))
|
||||||
|
* **common:** introduce KeyValuePipe ([#24319](https://github.com/angular/angular/issues/24319)) ([2b49bf7](https://github.com/angular/angular/commit/2b49bf7))
|
||||||
|
* **compiler:** support `// ...` and `// TODO` in mock compiler expectations ([#23441](https://github.com/angular/angular/issues/23441)) ([c6b206e](https://github.com/angular/angular/commit/c6b206e))
|
||||||
|
* **compiler-cli:** update `tsickle` to `0.29.x` ([#24233](https://github.com/angular/angular/issues/24233)) ([f69ac67](https://github.com/angular/angular/commit/f69ac67))
|
||||||
|
* **core:** export defaultKeyValueDiffers to private api ([#24319](https://github.com/angular/angular/issues/24319)) ([92b278c](https://github.com/angular/angular/commit/92b278c))
|
||||||
|
* **core:** expose a Compiler API for accessing module ids from NgModule types ([#24258](https://github.com/angular/angular/issues/24258)) ([bd02b27](https://github.com/angular/angular/commit/bd02b27))
|
||||||
|
* **core:** KeyValueDiffer#diff allows null values ([#24319](https://github.com/angular/angular/issues/24319)) ([52ce9d5](https://github.com/angular/angular/commit/52ce9d5))
|
||||||
|
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([3553977](https://github.com/angular/angular/commit/3553977))
|
||||||
|
(https://github.com/angular/angular/commit/328971f)), closes [#24616](https://github.com/angular/angular/issues/24616)
|
||||||
|
* **platform-browser:** add HammerJS lazy-loader symbols to public API ([#23943](https://github.com/angular/angular/issues/23943)) ([26fbf1d](https://github.com/angular/angular/commit/26fbf1d))
|
||||||
|
* **platform-browser:** allow lazy-loading HammerJS ([#23906](https://github.com/angular/angular/issues/23906)) ([313bdce](https://github.com/angular/angular/commit/313bdce))
|
||||||
|
* **platform-server:** use EventManagerPlugin on the server ([#24132](https://github.com/angular/angular/issues/24132)) ([d6595eb](https://github.com/angular/angular/commit/d6595eb))
|
||||||
|
* **router:** add urlUpdateStrategy allow updating the browser URL at the beginning of navigation ([#24820](https://github.com/angular/angular/issues/24820)) ([328971f]
|
||||||
|
* **router:** add navigation execution context info to activation hooks ([#24204](https://github.com/angular/angular/issues/24204)) ([20c463e](https://github.com/angular/angular/commit/20c463e)), closes [#24202](https://github.com/angular/angular/issues/24202)
|
||||||
|
* **router:** implement scrolling restoration service ([#20030](https://github.com/angular/angular/issues/20030)) ([49c5234](https://github.com/angular/angular/commit/49c5234)), closes [#13636](https://github.com/angular/angular/issues/13636) [#10929](https://github.com/angular/angular/issues/10929) [#7791](https://github.com/angular/angular/issues/7791) [#6595](https://github.com/angular/angular/issues/6595)
|
||||||
|
* **service-worker:** add support for `?` in SW config globbing ([#24105](https://github.com/angular/angular/issues/24105)) ([250527c](https://github.com/angular/angular/commit/250527c))
|
||||||
|
* typescript 2.9 support ([#24652](https://github.com/angular/angular/issues/24652)) ([e3064d5](https://github.com/angular/angular/commit/e3064d5))
|
||||||
|
|
||||||
|
### build
|
||||||
|
|
||||||
|
* **bazel:** turn on preserve-symlinks ([#24881](https://github.com/angular/angular/issues/24881)) ([c438b5e](https://github.com/angular/angular/commit/c438b5e))
|
||||||
|
|
||||||
|
### BREAKING CHANGES
|
||||||
|
|
||||||
|
* **bazel:** Use of @angular/bazel rules now requires calling ng_setup_workspace() in your WORKSPACE file.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
local_repository(
|
||||||
|
name = "angular",
|
||||||
|
path = "node_modules/@angular/bazel",
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@angular//:index.bzl", "ng_setup_workspace")
|
||||||
|
|
||||||
|
ng_setup_workspace()
|
||||||
|
|
||||||
|
<a name="6.0.9"></a>
|
||||||
|
## [6.0.9](https://github.com/angular/angular/compare/6.0.8...6.0.9) (2018-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
* **common:** format fractional seconds ([#24844](https://github.com/angular/angular/issues/24844)) ([3c93d07](https://github.com/angular/angular/commit/3c93d07)), closes [#24831](https://github.com/angular/angular/issues/24831)
|
||||||
|
|
||||||
|
|
||||||
|
<a name="6.0.8"></a>
|
||||||
|
## [6.0.8](https://github.com/angular/angular/compare/6.0.7...6.0.8) (2018-07-11)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** do not round factional seconds ([#24831](https://github.com/angular/angular/issues/24831)) ([0746485](https://github.com/angular/angular/commit/0746485)), closes [#24384](https://github.com/angular/angular/issues/24384)
|
||||||
|
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([9a98de9](https://github.com/angular/angular/commit/9a98de9)), closes [#24155](https://github.com/angular/angular/issues/24155)
|
||||||
|
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([a92f111](https://github.com/angular/angular/commit/a92f111)), closes [#24609](https://github.com/angular/angular/issues/24609)
|
||||||
|
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([7717ff1](https://github.com/angular/angular/commit/7717ff1))
|
||||||
|
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([b8975a9](https://github.com/angular/angular/commit/b8975a9)), closes [#22939](https://github.com/angular/angular/issues/22939)
|
||||||
|
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([de1c44f](https://github.com/angular/angular/commit/de1c44f)), closes [#21420](https://github.com/angular/angular/issues/21420)
|
||||||
|
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([2d4f4b5](https://github.com/angular/angular/commit/2d4f4b5)), closes [#21468](https://github.com/angular/angular/issues/21468)
|
||||||
|
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([183b079](https://github.com/angular/angular/commit/183b079))
|
||||||
|
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* **common:** introduce KeyValuePipe ([#24319](https://github.com/angular/angular/issues/24319)) ([2b49bf7](https://github.com/angular/angular/commit/2b49bf7))
|
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([6c55a13](https://github.com/angular/angular/commit/6c55a13))
|
||||||
* **core:** export defaultKeyValueDiffers to private api ([#24319](https://github.com/angular/angular/issues/24319)) ([92b278c](https://github.com/angular/angular/commit/92b278c))
|
|
||||||
* **core:** expose a Compiler API for accessing module ids from NgModule types ([#24258](https://github.com/angular/angular/issues/24258)) ([bd02b27](https://github.com/angular/angular/commit/bd02b27))
|
<a name="6.0.7"></a>
|
||||||
* **core:** KeyValueDiffer#diff allows null values ([#24319](https://github.com/angular/angular/issues/24319)) ([52ce9d5](https://github.com/angular/angular/commit/52ce9d5))
|
## [6.0.7](https://github.com/angular/angular/compare/6.0.6...6.0.7) (2018-06-27)
|
||||||
* **ivy:** a generic visitor which allows prefixing nodes for ngtsc ([#24230](https://github.com/angular/angular/issues/24230)) ([ca79e11](https://github.com/angular/angular/commit/ca79e11))
|
|
||||||
* **ivy:** add support of ApplicationRef.bootstrapModuleFactory ([#23811](https://github.com/angular/angular/issues/23811)) ([e3759f7](https://github.com/angular/angular/commit/e3759f7))
|
|
||||||
* **ivy:** namespaced attributes added to output instructions ([#24386](https://github.com/angular/angular/issues/24386)) ([82c5313](https://github.com/angular/angular/commit/82c5313))
|
|
||||||
* **ivy:** now supports SVG and MathML elements ([#24377](https://github.com/angular/angular/issues/24377)) ([8c1ac28](https://github.com/angular/angular/commit/8c1ac28))
|
|
||||||
* **router:** implement scrolling restoration service ([#20030](https://github.com/angular/angular/issues/20030)) ([49c5234](https://github.com/angular/angular/commit/49c5234)), closes [#13636](https://github.com/angular/angular/issues/13636) [#10929](https://github.com/angular/angular/issues/10929) [#7791](https://github.com/angular/angular/issues/7791) [#6595](https://github.com/angular/angular/issues/6595)
|
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
|
||||||
|
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
|
||||||
|
|
||||||
|
<a name="6.0.6"></a>
|
||||||
|
## [6.0.6](https://github.com/angular/angular/compare/6.0.5...6.0.6) (2018-06-20)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([e543c73](https://github.com/angular/angular/commit/e543c73)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||||
|
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([f5b3661](https://github.com/angular/angular/commit/f5b3661))
|
||||||
|
|
||||||
<a name="6.0.5"></a>
|
<a name="6.0.5"></a>
|
||||||
## [6.0.5](https://github.com/angular/angular/compare/6.0.4...6.0.5) (2018-06-13)
|
## [6.0.5](https://github.com/angular/angular/compare/6.0.4...6.0.5) (2018-06-13)
|
||||||
@ -38,53 +174,6 @@
|
|||||||
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([d20877b](https://github.com/angular/angular/commit/d20877b)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
* **router:** fix lazy loading of aux routes ([#23459](https://github.com/angular/angular/issues/23459)) ([d20877b](https://github.com/angular/angular/commit/d20877b)), closes [#10981](https://github.com/angular/angular/issues/10981)
|
||||||
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([ea2987c](https://github.com/angular/angular/commit/ea2987c)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
* **service-worker:** fix `SwPush.unsubscribe()` ([#24162](https://github.com/angular/angular/issues/24162)) ([ea2987c](https://github.com/angular/angular/commit/ea2987c)), closes [#24095](https://github.com/angular/angular/issues/24095)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="6.1.0-beta.0"></a>
|
|
||||||
## [6.1.0-beta.0](https://github.com/angular/angular/compare/6.0.0-rc.5...6.1.0-beta.0) (2018-06-06)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* **animations:** do not throw errors when a destroyed component is animated ([#23836](https://github.com/angular/angular/issues/23836)) ([d2a8687](https://github.com/angular/angular/commit/d2a8687))
|
|
||||||
* **animations:** Fix browser detection logic ([#24188](https://github.com/angular/angular/issues/24188)) ([b492b9e](https://github.com/angular/angular/commit/b492b9e))
|
|
||||||
* **animations:** properly clean up queried element styles in safari/edge ([#23633](https://github.com/angular/angular/issues/23633)) ([da9ff25](https://github.com/angular/angular/commit/da9ff25))
|
|
||||||
* **animations:** retain state styling for nodes that are moved around ([#23534](https://github.com/angular/angular/issues/23534)) ([65211f4](https://github.com/angular/angular/commit/65211f4))
|
|
||||||
* **animations:** retain trigger-state for nodes that are moved around ([#24238](https://github.com/angular/angular/issues/24238)) ([8db928d](https://github.com/angular/angular/commit/8db928d))
|
|
||||||
* **benchpress:** Fix promise chain in chrome_driver_extension. ([#23458](https://github.com/angular/angular/issues/23458)) ([d4b6c41](https://github.com/angular/angular/commit/d4b6c41))
|
|
||||||
* **compiler:** avoid a crash in ngc-wrapped. ([#23468](https://github.com/angular/angular/issues/23468)) ([e1c4930](https://github.com/angular/angular/commit/e1c4930))
|
|
||||||
* **compiler:** generate constant array for i18n attributes ([#23837](https://github.com/angular/angular/issues/23837)) ([cfde36d](https://github.com/angular/angular/commit/cfde36d))
|
|
||||||
* **compiler:** generate core-compliant hostBindings property ([#24087](https://github.com/angular/angular/issues/24087)) ([01b5acd](https://github.com/angular/angular/commit/01b5acd)), closes [#24013](https://github.com/angular/angular/issues/24013)
|
|
||||||
* **compiler:** handle undefined annotation metadata ([#23349](https://github.com/angular/angular/issues/23349)) ([ca776c5](https://github.com/angular/angular/commit/ca776c5))
|
|
||||||
* **compiler-cli:** don't rely on incompatible TS method ([#23550](https://github.com/angular/angular/issues/23550)) ([b1f040f](https://github.com/angular/angular/commit/b1f040f))
|
|
||||||
* **core:** avoid eager providers re-initialization ([#23559](https://github.com/angular/angular/issues/23559)) ([0c6dc45](https://github.com/angular/angular/commit/0c6dc45))
|
|
||||||
* **core:** call ngOnDestroy on all services that have it ([#23755](https://github.com/angular/angular/issues/23755)) ([fc03427](https://github.com/angular/angular/commit/fc03427)), closes [#22466](https://github.com/angular/angular/issues/22466) [#22240](https://github.com/angular/angular/issues/22240) [#14818](https://github.com/angular/angular/issues/14818)
|
|
||||||
* **elements:** always check to create strategy ([#23825](https://github.com/angular/angular/issues/23825)) ([b1cda36](https://github.com/angular/angular/commit/b1cda36))
|
|
||||||
* **elements:** prevent closure renaming of platform properties ([#23843](https://github.com/angular/angular/issues/23843)) ([d4b8b24](https://github.com/angular/angular/commit/d4b8b24))
|
|
||||||
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([9367e91](https://github.com/angular/angular/commit/9367e91)), closes [#17195](https://github.com/angular/angular/issues/17195)
|
|
||||||
* **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([b96a3c8](https://github.com/angular/angular/commit/b96a3c8))
|
|
||||||
* **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([60aa943](https://github.com/angular/angular/commit/60aa943)), closes [#23023](https://github.com/angular/angular/issues/23023)
|
|
||||||
* **platform-server:** don't reflect innerHTML property to attibute ([#24213](https://github.com/angular/angular/issues/24213)) ([6a663a4](https://github.com/angular/angular/commit/6a663a4)), closes [#19278](https://github.com/angular/angular/issues/19278)
|
|
||||||
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([c73196e](https://github.com/angular/angular/commit/c73196e)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
|
|
||||||
* **router:** avoid freezing queryParams in-place ([#22663](https://github.com/angular/angular/issues/22663)) ([89f64e5](https://github.com/angular/angular/commit/89f64e5)), closes [#22617](https://github.com/angular/angular/issues/22617)
|
|
||||||
* **router:** cache route handle if found ([#22475](https://github.com/angular/angular/issues/22475)) ([4cfa571](https://github.com/angular/angular/commit/4cfa571)), closes [#22474](https://github.com/angular/angular/issues/22474)
|
|
||||||
* **router:** correct the segment parsing so it won't break on ampersand ([#23684](https://github.com/angular/angular/issues/23684)) ([553a680](https://github.com/angular/angular/commit/553a680))
|
|
||||||
* **service-worker:** add badge to NOTIFICATION_OPTION_NAMES ([#23241](https://github.com/angular/angular/issues/23241)) ([fb59b2d](https://github.com/angular/angular/commit/fb59b2d)), closes [#23196](https://github.com/angular/angular/issues/23196)
|
|
||||||
* **service-worker:** check platformBrowser before accessing navigator.serviceWorker ([#21231](https://github.com/angular/angular/issues/21231)) ([0bdd30e](https://github.com/angular/angular/commit/0bdd30e))
|
|
||||||
* **service-worker:** correctly handle requests with empty `clientId` ([#23625](https://github.com/angular/angular/issues/23625)) ([e0ed59e](https://github.com/angular/angular/commit/e0ed59e)), closes [#23526](https://github.com/angular/angular/issues/23526)
|
|
||||||
* **service-worker:** deprecate `versionedFiles` in asset-group resources ([#23584](https://github.com/angular/angular/issues/23584)) ([1d378e2](https://github.com/angular/angular/commit/1d378e2))
|
|
||||||
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
* **compiler:** support `// ...` and `// TODO` in mock compiler expectations ([#23441](https://github.com/angular/angular/issues/23441)) ([c6b206e](https://github.com/angular/angular/commit/c6b206e))
|
|
||||||
* **compiler-cli:** update `tsickle` to `0.29.x` ([#24233](https://github.com/angular/angular/issues/24233)) ([f69ac67](https://github.com/angular/angular/commit/f69ac67))
|
|
||||||
* **platform-browser:** add HammerJS lazy-loader symbols to public API ([#23943](https://github.com/angular/angular/issues/23943)) ([26fbf1d](https://github.com/angular/angular/commit/26fbf1d))
|
|
||||||
* **platform-browser:** allow lazy-loading HammerJS ([#23906](https://github.com/angular/angular/issues/23906)) ([313bdce](https://github.com/angular/angular/commit/313bdce))
|
|
||||||
* **platform-server:** use EventManagerPlugin on the server ([#24132](https://github.com/angular/angular/issues/24132)) ([d6595eb](https://github.com/angular/angular/commit/d6595eb))
|
|
||||||
* **router:** add navigation execution context info to activation hooks ([#24204](https://github.com/angular/angular/issues/24204)) ([20c463e](https://github.com/angular/angular/commit/20c463e)), closes [#24202](https://github.com/angular/angular/issues/24202)
|
|
||||||
|
|
||||||
|
|
||||||
<a name="6.0.4"></a>
|
<a name="6.0.4"></a>
|
||||||
## [6.0.4](https://github.com/angular/angular/compare/6.0.3...6.0.4) (2018-06-06)
|
## [6.0.4](https://github.com/angular/angular/compare/6.0.3...6.0.4) (2018-06-06)
|
||||||
|
|
||||||
@ -163,7 +252,6 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
|||||||
* **animations:** only use the WA-polyfill alongside AnimationBuilder ([#22143](https://github.com/angular/angular/issues/22143)) ([b2f366b](https://github.com/angular/angular/commit/b2f366b)), closes [#17496](https://github.com/angular/angular/issues/17496)
|
* **animations:** only use the WA-polyfill alongside AnimationBuilder ([#22143](https://github.com/angular/angular/issues/22143)) ([b2f366b](https://github.com/angular/angular/commit/b2f366b)), closes [#17496](https://github.com/angular/angular/issues/17496)
|
||||||
* **animations:** expose `element` and `params` within transition matchers ([#22693](https://github.com/angular/angular/issues/22693)) ([58b94e6](https://github.com/angular/angular/commit/58b94e6))
|
* **animations:** expose `element` and `params` within transition matchers ([#22693](https://github.com/angular/angular/issues/22693)) ([58b94e6](https://github.com/angular/angular/commit/58b94e6))
|
||||||
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
|
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
|
||||||
* **common:** better error message when non-template element used in NgIf ([#22274](https://github.com/angular/angular/issues/22274)) ([67cf11d](https://github.com/angular/angular/commit/67cf11d)), closes [#16410](https://github.com/angular/angular/issues/16410)
|
|
||||||
* **common:** export functions to format numbers, percents, currencies & dates ([#22423](https://github.com/angular/angular/issues/22423)) ([4180912](https://github.com/angular/angular/commit/4180912)), closes [#20536](https://github.com/angular/angular/issues/20536)
|
* **common:** export functions to format numbers, percents, currencies & dates ([#22423](https://github.com/angular/angular/issues/22423)) ([4180912](https://github.com/angular/angular/commit/4180912)), closes [#20536](https://github.com/angular/angular/issues/20536)
|
||||||
* **compiler:** lower @NgModule ids if needed ([#23031](https://github.com/angular/angular/issues/23031)) ([bd024c0](https://github.com/angular/angular/commit/bd024c0))
|
* **compiler:** lower @NgModule ids if needed ([#23031](https://github.com/angular/angular/issues/23031)) ([bd024c0](https://github.com/angular/angular/commit/bd024c0))
|
||||||
* **compiler:** implement "enableIvy" compiler option ([#21427](https://github.com/angular/angular/issues/21427)) ([64d16de](https://github.com/angular/angular/commit/64d16de))
|
* **compiler:** implement "enableIvy" compiler option ([#21427](https://github.com/angular/angular/issues/21427)) ([64d16de](https://github.com/angular/angular/commit/64d16de))
|
||||||
@ -203,7 +291,6 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
|||||||
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
|
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
|
||||||
* **animations:** avoid animation insertions during router back/refresh ([#21977](https://github.com/angular/angular/issues/21977)) ([f88fba0](https://github.com/angular/angular/commit/f88fba0)), closes [#19712](https://github.com/angular/angular/issues/19712)
|
* **animations:** avoid animation insertions during router back/refresh ([#21977](https://github.com/angular/angular/issues/21977)) ([f88fba0](https://github.com/angular/angular/commit/f88fba0)), closes [#19712](https://github.com/angular/angular/issues/19712)
|
||||||
* **animations:** treat numeric state name values as strings ([#22923](https://github.com/angular/angular/issues/22923)) ([e5e1b0d](https://github.com/angular/angular/commit/e5e1b0d))
|
* **animations:** treat numeric state name values as strings ([#22923](https://github.com/angular/angular/issues/22923)) ([e5e1b0d](https://github.com/angular/angular/commit/e5e1b0d))
|
||||||
* **animations:** report correct totalTime value even during noOp animations ([#22225](https://github.com/angular/angular/issues/22225)) ([e1bf067](https://github.com/angular/angular/commit/e1bf067))
|
|
||||||
* **animations:** fix increment/decrement aliases example ([#18323](https://github.com/angular/angular/issues/18323)) ([d2aa8ac](https://github.com/angular/angular/commit/d2aa8ac))
|
* **animations:** fix increment/decrement aliases example ([#18323](https://github.com/angular/angular/issues/18323)) ([d2aa8ac](https://github.com/angular/angular/commit/d2aa8ac))
|
||||||
* **common:** NgClass should properly take className changes into account ([#21937](https://github.com/angular/angular/issues/21937)) ([4a42669](https://github.com/angular/angular/commit/4a42669)), closes [#21932](https://github.com/angular/angular/issues/21932)
|
* **common:** NgClass should properly take className changes into account ([#21937](https://github.com/angular/angular/issues/21937)) ([4a42669](https://github.com/angular/angular/commit/4a42669)), closes [#21932](https://github.com/angular/angular/issues/21932)
|
||||||
* **common:** fix the titlecase pipe ([#22600](https://github.com/angular/angular/issues/22600)) ([7966744](https://github.com/angular/angular/commit/7966744))
|
* **common:** fix the titlecase pipe ([#22600](https://github.com/angular/angular/issues/22600)) ([7966744](https://github.com/angular/angular/commit/7966744))
|
||||||
|
92
WORKSPACE
92
WORKSPACE
@ -6,23 +6,30 @@ workspace(name = "angular")
|
|||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_nodejs",
|
name = "build_bazel_rules_nodejs",
|
||||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.9.1.zip",
|
urls = ["https://github.com/bazelbuild/rules_nodejs/archive/0.11.2.zip"],
|
||||||
strip_prefix = "rules_nodejs-0.9.1",
|
strip_prefix = "rules_nodejs-0.11.2",
|
||||||
sha256 = "6139762b62b37c1fd171d7f22aa39566cb7dc2916f0f801d505a9aaf118c117f",
|
sha256 = "c00d5381adeefb56e0ef959a7b168cae628535dab933cfad1c2cd1870cd7c9de",
|
||||||
|
)
|
||||||
|
|
||||||
|
http_archive(
|
||||||
|
name = "bazel_skylib",
|
||||||
|
urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.3.1.zip"],
|
||||||
|
strip_prefix = "bazel-skylib-0.3.1",
|
||||||
|
sha256 = "95518adafc9a2b656667bbf517a952e54ce7f350779d0dd95133db4eb5c27fb1",
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "io_bazel_rules_webtesting",
|
name = "io_bazel_rules_webtesting",
|
||||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/v0.2.0.zip",
|
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
|
||||||
strip_prefix = "rules_webtesting-0.2.0",
|
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
|
||||||
sha256 = "cecc12f07e95740750a40d38e8b14b76fefa1551bef9332cb432d564d693723c",
|
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "build_bazel_rules_typescript",
|
name = "build_bazel_rules_typescript",
|
||||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.0.zip",
|
url = "https://github.com/bazelbuild/rules_typescript/archive/1d9a4b0087f307e31af91e2b221a6447288994c6.zip",
|
||||||
strip_prefix = "rules_typescript-0.15.0",
|
strip_prefix = "rules_typescript-1d9a4b0087f307e31af91e2b221a6447288994c6",
|
||||||
sha256 = "1aa75917330b820cb239b3c10a936a10f0a46fe215063d4492dd76dc6e1616f4",
|
sha256 = "e17ac3f33d5d3cd2a0c385c4fd28b814d0ad46c6c67ccaef97160be99d7a24eb",
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
@ -58,10 +65,10 @@ http_archive(
|
|||||||
# Even better, things like aspects will visit the entire graph including
|
# Even better, things like aspects will visit the entire graph including
|
||||||
# ts_library rules in the devkit repository.
|
# ts_library rules in the devkit repository.
|
||||||
http_archive(
|
http_archive(
|
||||||
name = "angular_devkit",
|
name = "angular_cli",
|
||||||
url = "https://github.com/angular/devkit/archive/v0.3.1.zip",
|
url = "https://github.com/angular/angular-cli/archive/v6.1.0-rc.0.zip",
|
||||||
strip_prefix = "devkit-0.3.1",
|
strip_prefix = "angular-cli-6.1.0-rc.0",
|
||||||
sha256 = "31d4b597fe9336650acf13df053c1c84dcbe9c29c6a833bcac3819cd3fd8cad3",
|
sha256 = "8cf320ea58c321e103f39087376feea502f20eaf79c61a4fdb05c7286c8684fd",
|
||||||
)
|
)
|
||||||
|
|
||||||
http_archive(
|
http_archive(
|
||||||
@ -71,32 +78,6 @@ http_archive(
|
|||||||
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
|
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
|
||||||
# Load and install our dependencies downloaded above.
|
|
||||||
#
|
|
||||||
|
|
||||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
|
||||||
|
|
||||||
check_bazel_version("0.14.0")
|
|
||||||
node_repositories(package_json = ["//:package.json"])
|
|
||||||
|
|
||||||
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
|
||||||
|
|
||||||
go_rules_dependencies()
|
|
||||||
go_register_toolchains()
|
|
||||||
|
|
||||||
load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
|
|
||||||
|
|
||||||
web_test_repositories()
|
|
||||||
browser_repositories(
|
|
||||||
chromium = True,
|
|
||||||
firefox = True,
|
|
||||||
)
|
|
||||||
|
|
||||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
|
||||||
|
|
||||||
ts_setup_workspace()
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
# Point Bazel to WORKSPACEs that live in subdirectories
|
||||||
#
|
#
|
||||||
@ -113,6 +94,39 @@ local_repository(
|
|||||||
path = "integration/bazel",
|
path = "integration/bazel",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Load and install our dependencies downloaded above.
|
||||||
|
#
|
||||||
|
|
||||||
|
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
||||||
|
|
||||||
|
check_bazel_version("0.15.0")
|
||||||
|
node_repositories(
|
||||||
|
package_json = ["//:package.json"],
|
||||||
|
preserve_symlinks = True,
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains")
|
||||||
|
|
||||||
|
go_rules_dependencies()
|
||||||
|
go_register_toolchains()
|
||||||
|
|
||||||
|
load("@io_bazel_rules_webtesting//web:repositories.bzl", "browser_repositories", "web_test_repositories")
|
||||||
|
|
||||||
|
web_test_repositories()
|
||||||
|
browser_repositories(
|
||||||
|
chromium = True,
|
||||||
|
firefox = True,
|
||||||
|
)
|
||||||
|
|
||||||
|
load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||||
|
|
||||||
|
ts_setup_workspace()
|
||||||
|
|
||||||
|
load("@angular//:index.bzl", "ng_setup_workspace")
|
||||||
|
|
||||||
|
ng_setup_workspace()
|
||||||
|
|
||||||
#
|
#
|
||||||
# Ask Bazel to manage these toolchain dependencies for us.
|
# Ask Bazel to manage these toolchain dependencies for us.
|
||||||
# Bazel will run `yarn install` when one of these toolchains is requested during
|
# Bazel will run `yarn install` when one of these toolchains is requested during
|
||||||
|
@ -43,6 +43,17 @@ Here are the most important tasks you might need to use:
|
|||||||
|
|
||||||
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
|
* `yarn build-ie-polyfills` - generates a js file of polyfills that can be loaded in Internet Explorer.
|
||||||
|
|
||||||
|
## Developing on Windows
|
||||||
|
The `packages/` directory may contain Linux-specific symlinks, which are not recognized by Windows.
|
||||||
|
These unresolved links cause the docs generation process to fail because it cannot locate certain files.
|
||||||
|
|
||||||
|
> Hint: The following steps require administration rights or [Windows Developer Mode](https://docs.microsoft.com/en-us/windows/uwp/get-started/enable-your-device-for-development) enabled!
|
||||||
|
|
||||||
|
To fix this problem, run `scripts/windows/create-symlinks.sh`. This command creates temporary files where the symlinks used to be. Make sure not to commit those files with your documentation changes.
|
||||||
|
When you are done making and testing your documentation changes, you can restore the original symlinks and delete the temporary files by running `scripts/windows/remove-symlinks.sh`.
|
||||||
|
|
||||||
|
It's necessary to remove the temporary files, because otherwise they're displayed as local changes in your git working copy and certain operations are blocked.
|
||||||
|
|
||||||
## Using ServiceWorker locally
|
## Using ServiceWorker locally
|
||||||
|
|
||||||
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
|
Since abb36e3cb, running `yarn start --prod` will no longer set up the ServiceWorker, which
|
||||||
|
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -60,6 +60,8 @@ dist/
|
|||||||
!rollup-config.js
|
!rollup-config.js
|
||||||
aot-compiler/**/*.d.ts
|
aot-compiler/**/*.d.ts
|
||||||
aot-compiler/**/*.factory.d.ts
|
aot-compiler/**/*.factory.d.ts
|
||||||
|
upgrade-phonecat-2-hybrid/aot/**/*
|
||||||
|
!upgrade-phonecat-2-hybrid/aot/index.html
|
||||||
|
|
||||||
# i18n
|
# i18n
|
||||||
!i18n/src/systemjs-text-plugin.js
|
!i18n/src/systemjs-text-plugin.js
|
||||||
|
@ -40,5 +40,7 @@ export class HighlightDirective {
|
|||||||
// #docregion color-2
|
// #docregion color-2
|
||||||
@Input() appHighlight: string;
|
@Input() appHighlight: string;
|
||||||
// #enddocregion color-2
|
// #enddocregion color-2
|
||||||
}
|
|
||||||
|
|
||||||
|
// #docregion
|
||||||
|
}
|
||||||
|
// #enddocregion
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HttpModule } from '@angular/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
// #docregion directive-import
|
// #docregion directive-import
|
||||||
@ -24,7 +24,7 @@ import { ItemDirective } from './item.directive';
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpModule
|
HttpClientModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
bootstrap: [AppComponent]
|
bootstrap: [AppComponent]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HttpModule } from '@angular/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
|
||||||
// import { AppRoutingModule } from './app-routing.module';
|
// import { AppRoutingModule } from './app-routing.module';
|
||||||
import { LocationStrategy,
|
import { LocationStrategy,
|
||||||
@ -54,7 +54,7 @@ const c_components = [
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpModule,
|
HttpClientModule,
|
||||||
InMemoryWebApiModule.forRoot(HeroData)
|
InMemoryWebApiModule.forRoot(HeroData)
|
||||||
// AppRoutingModule TODO: add routes
|
// AppRoutingModule TODO: add routes
|
||||||
],
|
],
|
||||||
|
@ -1,26 +1,38 @@
|
|||||||
import { ReflectiveInjector } from '@angular/core';
|
import { Injector } from '@angular/core';
|
||||||
|
|
||||||
import { Car, Engine, Tires } from './car';
|
import { Car, Engine, Tires } from './car';
|
||||||
import { Logger } from '../logger.service';
|
import { Logger } from '../logger.service';
|
||||||
|
|
||||||
// #docregion injector
|
// #docregion injector
|
||||||
export function useInjector() {
|
export function useInjector() {
|
||||||
let injector: ReflectiveInjector;
|
let injector: Injector;
|
||||||
// #enddocregion injector
|
// #enddocregion injector
|
||||||
/*
|
/*
|
||||||
// #docregion injector-no-new
|
// #docregion injector-no-new
|
||||||
// Cannot instantiate an ReflectiveInjector like this!
|
// Cannot instantiate an Injector like this!
|
||||||
let injector = new ReflectiveInjector([Car, Engine, Tires]);
|
let injector = new Injector([
|
||||||
|
{ provide: Car, deps: [Engine, Tires] },
|
||||||
|
{ provide: Engine, deps: [] },
|
||||||
|
{ provide: Tires, deps: [] }
|
||||||
|
]);
|
||||||
// #enddocregion injector-no-new
|
// #enddocregion injector-no-new
|
||||||
*/
|
*/
|
||||||
// #docregion injector, injector-create-and-call
|
// #docregion injector, injector-create-and-call
|
||||||
injector = ReflectiveInjector.resolveAndCreate([Car, Engine, Tires]);
|
injector = Injector.create({
|
||||||
|
providers: [
|
||||||
|
{ provide: Car, deps: [Engine, Tires] },
|
||||||
|
{ provide: Engine, deps: [] },
|
||||||
|
{ provide: Tires, deps: [] }
|
||||||
|
]
|
||||||
|
});
|
||||||
// #docregion injector-call
|
// #docregion injector-call
|
||||||
let car = injector.get(Car);
|
let car = injector.get(Car);
|
||||||
// #enddocregion injector-call, injector-create-and-call
|
// #enddocregion injector-call, injector-create-and-call
|
||||||
car.description = 'Injector';
|
car.description = 'Injector';
|
||||||
|
|
||||||
injector = ReflectiveInjector.resolveAndCreate([Logger]);
|
injector = Injector.create({
|
||||||
|
providers: [{ provide: Logger, deps: [] }]
|
||||||
|
});
|
||||||
let logger = injector.get(Logger);
|
let logger = injector.get(Logger);
|
||||||
logger.log('Injector car.drive() said: ' + car.drive());
|
logger.log('Injector car.drive() said: ' + car.drive());
|
||||||
return car;
|
return car;
|
||||||
|
69
aio/content/examples/elements/e2e/src/app.e2e-spec.ts
Normal file
69
aio/content/examples/elements/e2e/src/app.e2e-spec.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
'use strict'; // necessary for es6 output in node
|
||||||
|
|
||||||
|
import { browser, by, element } from 'protractor';
|
||||||
|
|
||||||
|
/* tslint:disable:quotemark */
|
||||||
|
describe('Elements', () => {
|
||||||
|
const messageInput = element(by.css('input'));
|
||||||
|
const popupButtons = element.all(by.css('button'));
|
||||||
|
|
||||||
|
beforeEach(() => browser.get(''));
|
||||||
|
|
||||||
|
describe('popup component', () => {
|
||||||
|
const popupComponentButton = popupButtons.get(0);
|
||||||
|
const popupComponent = element(by.css('popup-component'));
|
||||||
|
const closeButton = popupComponent.element(by.css('button'));
|
||||||
|
|
||||||
|
it('should be displayed on button click', () => {
|
||||||
|
expect(popupComponent.isPresent()).toBe(false);
|
||||||
|
|
||||||
|
popupComponentButton.click();
|
||||||
|
expect(popupComponent.isPresent()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the specified message', () => {
|
||||||
|
messageInput.clear();
|
||||||
|
messageInput.sendKeys('Angular rocks!');
|
||||||
|
|
||||||
|
popupComponentButton.click();
|
||||||
|
expect(popupComponent.getText()).toContain('Popup: Angular rocks!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be closed on "close" button click', () => {
|
||||||
|
popupComponentButton.click();
|
||||||
|
expect(popupComponent.isPresent()).toBe(true);
|
||||||
|
|
||||||
|
closeButton.click();
|
||||||
|
expect(popupComponent.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('popup element', () => {
|
||||||
|
const popupElementButton = popupButtons.get(1);
|
||||||
|
const popupElement = element(by.css('popup-element'));
|
||||||
|
const closeButton = popupElement.element(by.css('button'));
|
||||||
|
|
||||||
|
it('should be displayed on button click', () => {
|
||||||
|
expect(popupElement.isPresent()).toBe(false);
|
||||||
|
|
||||||
|
popupElementButton.click();
|
||||||
|
expect(popupElement.isPresent()).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the specified message', () => {
|
||||||
|
messageInput.clear();
|
||||||
|
messageInput.sendKeys('Angular rocks!');
|
||||||
|
|
||||||
|
popupElementButton.click();
|
||||||
|
expect(popupElement.getText()).toContain('Popup: Angular rocks!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be closed on "close" button click', () => {
|
||||||
|
popupElementButton.click();
|
||||||
|
expect(popupElement.isPresent()).toBe(true);
|
||||||
|
|
||||||
|
closeButton.click();
|
||||||
|
expect(popupElement.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
3
aio/content/examples/elements/example-config.json
Normal file
3
aio/content/examples/elements/example-config.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"projectType": "elements"
|
||||||
|
}
|
@ -1,6 +1,5 @@
|
|||||||
// #docregion
|
|
||||||
import { Component, Injector } from '@angular/core';
|
import { Component, Injector } from '@angular/core';
|
||||||
import { createNgElementConstructor } from '../elements-dist';
|
import { createCustomElement } from '@angular/elements';
|
||||||
import { PopupService } from './popup.service';
|
import { PopupService } from './popup.service';
|
||||||
import { PopupComponent } from './popup.component';
|
import { PopupComponent } from './popup.component';
|
||||||
|
|
||||||
@ -8,19 +7,15 @@ import { PopupComponent } from './popup.component';
|
|||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
template: `
|
template: `
|
||||||
<input #input value="Message">
|
<input #input value="Message">
|
||||||
<button (click)="popup.showAsComponent(input.value)">
|
<button (click)="popup.showAsComponent(input.value)">Show as component</button>
|
||||||
Show as component </button>
|
<button (click)="popup.showAsElement(input.value)">Show as element</button>
|
||||||
<button (click)="popup.showAsElement(input.value)">
|
`,
|
||||||
Show as element </button>
|
|
||||||
`
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
constructor(private injector: Injector, public popup: PopupService) {
|
constructor(injector: Injector, public popup: PopupService) {
|
||||||
// on init, convert PopupComponent to a custom element
|
// Convert `PopupComponent` to a custom element.
|
||||||
const PopupElement =
|
const PopupElement = createCustomElement(PopupComponent, {injector});
|
||||||
createNgElementConstructor(PopupComponent, {injector: this.injector});
|
// Register the custom element with the browser.
|
||||||
// register the custom element with the browser.
|
customElements.define('popup-element', PopupElement);
|
||||||
customElements.define('popup-element', PopupElement);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
// #docregion
|
import { NgModule } from '@angular/core';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { PopupService } from './popup.service';
|
|
||||||
import { PopupComponent } from './popup.component';
|
import { PopupComponent } from './popup.component';
|
||||||
|
import { PopupService } from './popup.service';
|
||||||
|
|
||||||
// include the PopupService provider,
|
// Include the `PopupService` provider,
|
||||||
// but exclude PopupComponent from compilation,
|
// but exclude `PopupComponent` from compilation,
|
||||||
// because it will be added dynamically
|
// because it will be added dynamically.
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [AppComponent, PopupComponent],
|
|
||||||
imports: [BrowserModule, BrowserAnimationsModule],
|
imports: [BrowserModule, BrowserAnimationsModule],
|
||||||
providers: [PopupService],
|
providers: [PopupService],
|
||||||
|
declarations: [AppComponent, PopupComponent],
|
||||||
bootstrap: [AppComponent],
|
bootstrap: [AppComponent],
|
||||||
entryComponents: [PopupComponent],
|
entryComponents: [PopupComponent],
|
||||||
})
|
})
|
||||||
|
export class AppModule {
|
||||||
export class AppModule {}
|
}
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
// #docregion
|
|
||||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||||
import { AnimationEvent } from '@angular/animations';
|
|
||||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'my-popup',
|
selector: 'my-popup',
|
||||||
template: 'Popup: {{message}}',
|
template: `
|
||||||
|
<span>Popup: {{message}}</span>
|
||||||
|
<button (click)="closed.next()">✖</button>
|
||||||
|
`,
|
||||||
host: {
|
host: {
|
||||||
'[@state]': 'state',
|
'[@state]': 'state',
|
||||||
'(@state.done)': 'onAnimationDone($event)',
|
|
||||||
},
|
},
|
||||||
animations: [
|
animations: [
|
||||||
trigger('state', [
|
trigger('state', [
|
||||||
@ -27,13 +27,17 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
|||||||
height: 48px;
|
height: 48px;
|
||||||
padding: 16px;
|
padding: 16px;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-top: 1px solid black;
|
border-top: 1px solid black;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
`]
|
`]
|
||||||
})
|
})
|
||||||
|
|
||||||
export class PopupComponent {
|
export class PopupComponent {
|
||||||
private state: 'opened' | 'closed' = 'closed';
|
private state: 'opened' | 'closed' = 'closed';
|
||||||
|
|
||||||
@ -41,18 +45,10 @@ export class PopupComponent {
|
|||||||
set message(message: string) {
|
set message(message: string) {
|
||||||
this._message = message;
|
this._message = message;
|
||||||
this.state = 'opened';
|
this.state = 'opened';
|
||||||
|
|
||||||
setTimeout(() => this.state = 'closed', 2000);
|
|
||||||
}
|
}
|
||||||
get message(): string { return this._message; }
|
get message(): string { return this._message; }
|
||||||
_message: string;
|
_message: string;
|
||||||
|
|
||||||
@Output()
|
@Output()
|
||||||
closed = new EventEmitter();
|
closed = new EventEmitter();
|
||||||
|
|
||||||
onAnimationDone(e: AnimationEvent) {
|
|
||||||
if (e.toState === 'closed') {
|
|
||||||
this.closed.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
|
|
||||||
// #docregion
|
|
||||||
import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
import { ApplicationRef, ComponentFactoryResolver, Injectable, Injector } from '@angular/core';
|
||||||
|
import { NgElement, WithProperties } from '@angular/elements';
|
||||||
import { PopupComponent } from './popup.component';
|
import { PopupComponent } from './popup.component';
|
||||||
import { NgElementConstructor } from '../elements-dist';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PopupService {
|
export class PopupService {
|
||||||
@ -40,7 +39,7 @@ export class PopupService {
|
|||||||
// This uses the new custom-element method to add the popup to the DOM.
|
// This uses the new custom-element method to add the popup to the DOM.
|
||||||
showAsElement(message: string) {
|
showAsElement(message: string) {
|
||||||
// Create element
|
// Create element
|
||||||
const popupEl = document.createElement('popup-element');
|
const popupEl: NgElement & WithProperties<PopupComponent> = document.createElement('popup-element') as any;
|
||||||
|
|
||||||
// Listen to the close event
|
// Listen to the close event
|
||||||
popupEl.addEventListener('closed', () => document.body.removeChild(popupEl));
|
popupEl.addEventListener('closed', () => document.body.removeChild(popupEl));
|
||||||
|
12
aio/content/examples/elements/src/index.html
Normal file
12
aio/content/examples/elements/src/index.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<base href="/">
|
||||||
|
<title>Elements</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<app-root></app-root>
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,4 +1,3 @@
|
|||||||
// tslint:disable:no-unused-variable
|
|
||||||
import { enableProdMode } from '@angular/core';
|
import { enableProdMode } from '@angular/core';
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
@ -10,4 +9,3 @@ if (environment.production) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
|
9
aio/content/examples/elements/stackblitz.json
Normal file
9
aio/content/examples/elements/stackblitz.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"description": "Angular Elements",
|
||||||
|
"files":[
|
||||||
|
"!**/*.d.ts",
|
||||||
|
"!**/*.js",
|
||||||
|
"!**/*.[1].*"
|
||||||
|
],
|
||||||
|
"tags":["cookbook"]
|
||||||
|
}
|
@ -1,15 +1,14 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion app-module
|
// #docregion app-module
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HttpModule } from '@angular/http';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
// import the feature module here so you can add it to the imports array below
|
// import the feature module here so you can add it to the imports array below
|
||||||
import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard.module';
|
import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard.module';
|
||||||
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent
|
AppComponent
|
||||||
@ -17,7 +16,7 @@ import { CustomerDashboardModule } from './customer-dashboard/customer-dashboard
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpModule,
|
HttpClientModule,
|
||||||
CustomerDashboardModule // add the feature module here
|
CustomerDashboardModule // add the feature module here
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
@ -16,6 +16,7 @@ describe('Form Validation Tests', function () {
|
|||||||
|
|
||||||
tests('Template-Driven Form');
|
tests('Template-Driven Form');
|
||||||
bobTests();
|
bobTests();
|
||||||
|
crossValidationTests();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Reactive form', () => {
|
describe('Reactive form', () => {
|
||||||
@ -25,6 +26,7 @@ describe('Form Validation Tests', function () {
|
|||||||
|
|
||||||
tests('Reactive Form');
|
tests('Reactive Form');
|
||||||
bobTests();
|
bobTests();
|
||||||
|
crossValidationTests();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,7 +44,8 @@ let page: {
|
|||||||
powerOption: ElementFinder,
|
powerOption: ElementFinder,
|
||||||
errorMessages: ElementArrayFinder,
|
errorMessages: ElementArrayFinder,
|
||||||
heroFormButtons: ElementArrayFinder,
|
heroFormButtons: ElementArrayFinder,
|
||||||
heroSubmitted: ElementFinder
|
heroSubmitted: ElementFinder,
|
||||||
|
crossValidationErrorMessage: ElementFinder,
|
||||||
};
|
};
|
||||||
|
|
||||||
function getPage(sectionTag: string) {
|
function getPage(sectionTag: string) {
|
||||||
@ -59,7 +62,8 @@ function getPage(sectionTag: string) {
|
|||||||
powerOption: section.element(by.css('#power option')),
|
powerOption: section.element(by.css('#power option')),
|
||||||
errorMessages: section.all(by.css('div.alert')),
|
errorMessages: section.all(by.css('div.alert')),
|
||||||
heroFormButtons: buttons,
|
heroFormButtons: buttons,
|
||||||
heroSubmitted: section.element(by.css('.submitted-message'))
|
heroSubmitted: section.element(by.css('.submitted-message')),
|
||||||
|
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -172,3 +176,29 @@ function bobTests() {
|
|||||||
expectFormIsValid();
|
expectFormIsValid();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function crossValidationTests() {
|
||||||
|
const emsg = 'Name cannot match alter ego.';
|
||||||
|
|
||||||
|
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () {
|
||||||
|
page.nameInput.clear();
|
||||||
|
page.nameInput.sendKeys('Batman');
|
||||||
|
|
||||||
|
page.alterEgoInput.clear();
|
||||||
|
page.alterEgoInput.sendKeys('Batman');
|
||||||
|
|
||||||
|
expectFormIsInvalid();
|
||||||
|
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be ok again with different values', function () {
|
||||||
|
page.nameInput.clear();
|
||||||
|
page.nameInput.sendKeys('Batman');
|
||||||
|
|
||||||
|
page.alterEgoInput.clear();
|
||||||
|
page.alterEgoInput.sendKeys('Superman');
|
||||||
|
|
||||||
|
expectFormIsValid();
|
||||||
|
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ import { AppComponent } from './app.component';
|
|||||||
import { HeroFormTemplateComponent } from './template/hero-form-template.component';
|
import { HeroFormTemplateComponent } from './template/hero-form-template.component';
|
||||||
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
||||||
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||||
|
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -19,7 +19,8 @@ import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
|||||||
AppComponent,
|
AppComponent,
|
||||||
HeroFormTemplateComponent,
|
HeroFormTemplateComponent,
|
||||||
HeroFormReactiveComponent,
|
HeroFormReactiveComponent,
|
||||||
ForbiddenValidatorDirective
|
ForbiddenValidatorDirective,
|
||||||
|
IdentityRevealedValidatorDirective
|
||||||
],
|
],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [ AppComponent ]
|
||||||
})
|
})
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
/* tslint:disable: member-ordering forin */
|
||||||
|
// #docplaster
|
||||||
|
// #docregion
|
||||||
|
import { Component, OnInit } from '@angular/core';
|
||||||
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
|
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-hero-form-reactive',
|
||||||
|
templateUrl: './hero-form-reactive.component.html',
|
||||||
|
styleUrls: ['./hero-form-reactive.component.css'],
|
||||||
|
})
|
||||||
|
export class HeroFormReactiveComponent implements OnInit {
|
||||||
|
|
||||||
|
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||||
|
|
||||||
|
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||||
|
|
||||||
|
heroForm: FormGroup;
|
||||||
|
|
||||||
|
// #docregion form-group
|
||||||
|
ngOnInit(): void {
|
||||||
|
// #docregion custom-validator
|
||||||
|
this.heroForm = new FormGroup({
|
||||||
|
'name': new FormControl(this.hero.name, [
|
||||||
|
Validators.required,
|
||||||
|
Validators.minLength(4),
|
||||||
|
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||||
|
]),
|
||||||
|
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||||
|
'power': new FormControl(this.hero.power, Validators.required)
|
||||||
|
});
|
||||||
|
// #enddocregion custom-validator
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
get name() { return this.heroForm.get('name'); }
|
||||||
|
|
||||||
|
get power() { return this.heroForm.get('power'); }
|
||||||
|
// #enddocregion form-group
|
||||||
|
}
|
||||||
|
// #enddocregion
|
@ -0,0 +1,5 @@
|
|||||||
|
/* #docregion cross-validation-error-css */
|
||||||
|
.cross-validation-error input {
|
||||||
|
border-left: 5px solid red;
|
||||||
|
}
|
||||||
|
/* #enddocregion cross-validation-error-css */
|
@ -7,33 +7,41 @@
|
|||||||
|
|
||||||
<div [hidden]="formDir.submitted">
|
<div [hidden]="formDir.submitted">
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||||
|
<div class="form-group">
|
||||||
|
|
||||||
<label for="name">Name</label>
|
<label for="name">Name</label>
|
||||||
<!-- #docregion name-with-error-msg -->
|
<!-- #docregion name-with-error-msg -->
|
||||||
<input id="name" class="form-control"
|
<input id="name" class="form-control"
|
||||||
formControlName="name" required >
|
formControlName="name" required >
|
||||||
|
|
||||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||||
class="alert alert-danger">
|
class="alert alert-danger">
|
||||||
|
|
||||||
<div *ngIf="name.errors.required">
|
<div *ngIf="name.errors.required">
|
||||||
Name is required.
|
Name is required.
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="name.errors.minlength">
|
<div *ngIf="name.errors.minlength">
|
||||||
Name must be at least 4 characters long.
|
Name must be at least 4 characters long.
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="name.errors.forbiddenName">
|
<div *ngIf="name.errors.forbiddenName">
|
||||||
Name cannot be Bob.
|
Name cannot be Bob.
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- #enddocregion name-with-error-msg -->
|
||||||
</div>
|
</div>
|
||||||
<!-- #enddocregion name-with-error-msg -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="alterEgo">Alter Ego</label>
|
<label for="alterEgo">Alter Ego</label>
|
||||||
<input id="alterEgo" class="form-control"
|
<input id="alterEgo" class="form-control"
|
||||||
formControlName="alterEgo" >
|
formControlName="alterEgo" >
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- #docregion cross-validation-error-message -->
|
||||||
|
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||||
|
Name cannot match alter ego.
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion cross-validation-error-message -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
@ -1,40 +1,36 @@
|
|||||||
/* tslint:disable: member-ordering forin */
|
/* tslint:disable: member-ordering forin */
|
||||||
// #docplaster
|
|
||||||
// #docregion
|
// #docregion
|
||||||
import { Component, OnInit } from '@angular/core';
|
import { Component, OnInit } from '@angular/core';
|
||||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||||
|
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-form-reactive',
|
selector: 'app-hero-form-reactive',
|
||||||
templateUrl: './hero-form-reactive.component.html'
|
templateUrl: './hero-form-reactive.component.html',
|
||||||
|
styleUrls: ['./hero-form-reactive.component.css'],
|
||||||
})
|
})
|
||||||
export class HeroFormReactiveComponent implements OnInit {
|
export class HeroFormReactiveComponent implements OnInit {
|
||||||
|
|
||||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||||
|
|
||||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
|
||||||
|
|
||||||
heroForm: FormGroup;
|
heroForm: FormGroup;
|
||||||
|
|
||||||
// #docregion form-group
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
// #docregion custom-validator
|
|
||||||
this.heroForm = new FormGroup({
|
this.heroForm = new FormGroup({
|
||||||
'name': new FormControl(this.hero.name, [
|
'name': new FormControl(this.hero.name, [
|
||||||
Validators.required,
|
Validators.required,
|
||||||
Validators.minLength(4),
|
Validators.minLength(4),
|
||||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
forbiddenNameValidator(/bob/i)
|
||||||
]),
|
]),
|
||||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||||
'power': new FormControl(this.hero.power, Validators.required)
|
'power': new FormControl(this.hero.power, Validators.required)
|
||||||
});
|
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
||||||
// #enddocregion custom-validator
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() { return this.heroForm.get('name'); }
|
get name() { return this.heroForm.get('name'); }
|
||||||
|
|
||||||
get power() { return this.heroForm.get('power'); }
|
get power() { return this.heroForm.get('power'); }
|
||||||
// #enddocregion form-group
|
|
||||||
}
|
}
|
||||||
// #enddocregion
|
|
||||||
|
@ -0,0 +1,25 @@
|
|||||||
|
// #docregion
|
||||||
|
import { Directive } from '@angular/core';
|
||||||
|
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
|
||||||
|
|
||||||
|
// #docregion cross-validation-validator
|
||||||
|
/** A hero's name can't match the hero's alter ego */
|
||||||
|
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
|
||||||
|
const name = control.get('name');
|
||||||
|
const alterEgo = control.get('alterEgo');
|
||||||
|
|
||||||
|
return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
|
||||||
|
};
|
||||||
|
// #enddocregion cross-validation-validator
|
||||||
|
|
||||||
|
// #docregion cross-validation-directive
|
||||||
|
@Directive({
|
||||||
|
selector: '[appIdentityRevealed]',
|
||||||
|
providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
|
||||||
|
})
|
||||||
|
export class IdentityRevealedValidatorDirective implements Validator {
|
||||||
|
validate(control: AbstractControl): ValidationErrors {
|
||||||
|
return identityRevealedValidator(control)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #enddocregion cross-validation-directive
|
@ -0,0 +1,4 @@
|
|||||||
|
/* #docregion */
|
||||||
|
.cross-validation-error input {
|
||||||
|
border-left: 5px solid red;
|
||||||
|
}
|
@ -2,41 +2,48 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<h1>Template-Driven Form</h1>
|
<h1>Template-Driven Form</h1>
|
||||||
<!-- #docregion form-tag-->
|
<!-- #docregion cross-validation-register-validator -->
|
||||||
<form #heroForm="ngForm">
|
<form #heroForm="ngForm" appIdentityRevealed>
|
||||||
<!-- #enddocregion form-tag-->
|
<!-- #enddocregion cross-validation-register-validator -->
|
||||||
<div [hidden]="heroForm.submitted">
|
<div [hidden]="heroForm.submitted">
|
||||||
|
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name">Name</label>
|
||||||
|
<!-- #docregion name-with-error-msg -->
|
||||||
|
<!-- #docregion name-input -->
|
||||||
|
<input id="name" name="name" class="form-control"
|
||||||
|
required minlength="4" appForbiddenName="bob"
|
||||||
|
[(ngModel)]="hero.name" #name="ngModel" >
|
||||||
|
<!-- #enddocregion name-input -->
|
||||||
|
|
||||||
<div class="form-group">
|
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||||
<label for="name">Name</label>
|
class="alert alert-danger">
|
||||||
<!-- #docregion name-with-error-msg -->
|
|
||||||
<!-- #docregion name-input -->
|
|
||||||
<input id="name" name="name" class="form-control"
|
|
||||||
required minlength="4" appForbiddenName="bob"
|
|
||||||
[(ngModel)]="hero.name" #name="ngModel" >
|
|
||||||
<!-- #enddocregion name-input -->
|
|
||||||
|
|
||||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
<div *ngIf="name.errors.required">
|
||||||
class="alert alert-danger">
|
Name is required.
|
||||||
|
</div>
|
||||||
|
<div *ngIf="name.errors.minlength">
|
||||||
|
Name must be at least 4 characters long.
|
||||||
|
</div>
|
||||||
|
<div *ngIf="name.errors.forbiddenName">
|
||||||
|
Name cannot be Bob.
|
||||||
|
</div>
|
||||||
|
|
||||||
<div *ngIf="name.errors.required">
|
|
||||||
Name is required.
|
|
||||||
</div>
|
</div>
|
||||||
<div *ngIf="name.errors.minlength">
|
<!-- #enddocregion name-with-error-msg -->
|
||||||
Name must be at least 4 characters long.
|
|
||||||
</div>
|
|
||||||
<div *ngIf="name.errors.forbiddenName">
|
|
||||||
Name cannot be Bob.
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<!-- #enddocregion name-with-error-msg -->
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="alterEgo">Alter Ego</label>
|
<label for="alterEgo">Alter Ego</label>
|
||||||
<input id="alterEgo" class="form-control"
|
<input id="alterEgo" class="form-control"
|
||||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- #docregion cross-validation-error-message -->
|
||||||
|
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||||
|
Name cannot match alter ego.
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion cross-validation-error-message -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@ -62,5 +69,4 @@
|
|||||||
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,9 +3,11 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
// #docregion component
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-hero-form-template',
|
selector: 'app-hero-form-template',
|
||||||
templateUrl: './hero-form-template.component.html'
|
templateUrl: './hero-form-template.component.html',
|
||||||
|
styleUrls: ['./hero-form-template.component.css'],
|
||||||
})
|
})
|
||||||
export class HeroFormTemplateComponent {
|
export class HeroFormTemplateComponent {
|
||||||
|
|
||||||
@ -14,3 +16,4 @@ export class HeroFormTemplateComponent {
|
|||||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
// #enddocregion
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
"description": "Validation",
|
"description": "Validation",
|
||||||
"files":[
|
"files":[
|
||||||
"!**/*.d.ts",
|
"!**/*.d.ts",
|
||||||
"!**/*.js"
|
"!**/*.js",
|
||||||
|
"!**/*.[1].*"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { HttpModule } from '@angular/http';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { AppRoutingModule } from './app-routing.module';
|
|
||||||
|
|
||||||
|
import { AppRoutingModule } from './app-routing.module';
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
@ -13,7 +13,7 @@ import { AppComponent } from './app.component';
|
|||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
FormsModule,
|
FormsModule,
|
||||||
HttpModule,
|
HttpClientModule,
|
||||||
AppRoutingModule
|
AppRoutingModule
|
||||||
],
|
],
|
||||||
providers: [],
|
providers: [],
|
||||||
|
@ -4,7 +4,8 @@ button {
|
|||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
code, .code {
|
code,
|
||||||
|
.code {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
color: black;
|
color: black;
|
||||||
font-family: Courier, sans-serif;
|
font-family: Courier, sans-serif;
|
||||||
@ -21,14 +22,18 @@ div.code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
margin: 40px 0
|
margin: 40px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
td, th {
|
td,
|
||||||
|
th {
|
||||||
text-align: left;
|
text-align: left;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* #docregion p-span */
|
/* #docregion p-span */
|
||||||
p span { color: red; font-size: 70%; }
|
p span {
|
||||||
|
color: red;
|
||||||
|
font-size: 70%;
|
||||||
|
}
|
||||||
/* #enddocregion p-span */
|
/* #enddocregion p-span */
|
||||||
|
@ -132,7 +132,7 @@
|
|||||||
<!-- #docregion select-span -->
|
<!-- #docregion select-span -->
|
||||||
<select [(ngModel)]="hero">
|
<select [(ngModel)]="hero">
|
||||||
<span *ngFor="let h of heroes">
|
<span *ngFor="let h of heroes">
|
||||||
<span *ngIf="showSad || h?.emotion != 'sad'">
|
<span *ngIf="showSad || h?.emotion !== 'sad'">
|
||||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||||
</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
@ -147,7 +147,7 @@
|
|||||||
<!-- #docregion select-ngcontainer -->
|
<!-- #docregion select-ngcontainer -->
|
||||||
<select [(ngModel)]="hero">
|
<select [(ngModel)]="hero">
|
||||||
<ng-container *ngFor="let h of heroes">
|
<ng-container *ngFor="let h of heroes">
|
||||||
<ng-container *ngIf="showSad || h?.emotion != 'sad'">
|
<ng-container *ngIf="showSad || h?.emotion !== 'sad'">
|
||||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -6,14 +6,15 @@ import { heroes } from './hero';
|
|||||||
@Component({
|
@Component({
|
||||||
selector: 'my-app',
|
selector: 'my-app',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: [ './app.component.css' ]
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent {
|
export class AppComponent {
|
||||||
heroes = heroes;
|
heroes = heroes;
|
||||||
hero = this.heroes[0];
|
hero = this.heroes[0];
|
||||||
heroTraits = [ 'honest', 'brave', 'considerate' ];
|
heroTraits = ['honest', 'brave', 'considerate'];
|
||||||
|
|
||||||
// flags for the table
|
// flags for the table
|
||||||
|
|
||||||
attrDirs = true;
|
attrDirs = true;
|
||||||
strucDirs = true;
|
strucDirs = true;
|
||||||
divNgIf = false;
|
divNgIf = false;
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
import { FormsModule } from '@angular/forms';
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import { ContentComponent } from './content.component';
|
import { ContentComponent } from './content.component';
|
||||||
import { heroComponents } from './hero.components';
|
import { heroComponents } from './hero.components';
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Component, Input } from '@angular/core';
|
import { Component, Input } from '@angular/core';
|
||||||
|
|
||||||
import { Hero } from './hero';
|
import { Hero } from './hero';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -33,11 +34,15 @@ export class ConfusedHeroComponent {
|
|||||||
export class UnknownHeroComponent {
|
export class UnknownHeroComponent {
|
||||||
@Input() hero: Hero;
|
@Input() hero: Hero;
|
||||||
get message() {
|
get message() {
|
||||||
return this.hero && this.hero.name ?
|
return this.hero && this.hero.name
|
||||||
`${this.hero.name} is strange and mysterious.` :
|
? `${this.hero.name} is strange and mysterious.`
|
||||||
'Are you feeling indecisive?';
|
: 'Are you feeling indecisive?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const heroComponents =
|
export const heroComponents = [
|
||||||
[ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
|
HappyHeroComponent,
|
||||||
|
SadHeroComponent,
|
||||||
|
ConfusedHeroComponent,
|
||||||
|
UnknownHeroComponent
|
||||||
|
];
|
||||||
|
@ -6,8 +6,8 @@ export class Hero {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const heroes: Hero[] = [
|
export const heroes: Hero[] = [
|
||||||
{ id: 1, name: 'Mr. Nice', emotion: 'happy'},
|
{ id: 1, name: 'Mr. Nice', emotion: 'happy' },
|
||||||
{ id: 2, name: 'Narco', emotion: 'sad' },
|
{ id: 2, name: 'Narco', emotion: 'sad' },
|
||||||
{ id: 3, name: 'Windstorm', emotion: 'confused' },
|
{ id: 3, name: 'Windstorm', emotion: 'confused' },
|
||||||
{ id: 4, name: 'Magneta'}
|
{ id: 4, name: 'Magneta' }
|
||||||
];
|
];
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpModule } from '@angular/http';
|
|
||||||
|
|
||||||
/* App Root */
|
/* App Root */
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
|
@ -15,7 +15,7 @@ function sequenceSubscriber(observer) {
|
|||||||
if (idx === arr.length - 1) {
|
if (idx === arr.length - 1) {
|
||||||
observer.complete();
|
observer.complete();
|
||||||
} else {
|
} else {
|
||||||
doSequence(arr, idx++);
|
doSequence(arr, ++idx);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
@ -95,7 +95,7 @@ function multicastSequenceSubscriber() {
|
|||||||
},
|
},
|
||||||
complete() {
|
complete() {
|
||||||
// Notify all complete callbacks
|
// Notify all complete callbacks
|
||||||
observers.forEach(obs => obs.complete());
|
observers.slice(0).forEach(obs => obs.complete());
|
||||||
}
|
}
|
||||||
}, seq, 0);
|
}, seq, 0);
|
||||||
}
|
}
|
||||||
@ -121,13 +121,13 @@ function doSequence(observer, arr, idx) {
|
|||||||
if (idx === arr.length - 1) {
|
if (idx === arr.length - 1) {
|
||||||
observer.complete();
|
observer.complete();
|
||||||
} else {
|
} else {
|
||||||
doSequence(observer, arr, idx++);
|
doSequence(observer, arr, ++idx);
|
||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new Observable that will deliver the above sequence
|
// Create a new Observable that will deliver the above sequence
|
||||||
const multicastSequence = new Observable(multicastSequenceSubscriber);
|
const multicastSequence = new Observable(multicastSequenceSubscriber());
|
||||||
|
|
||||||
// Subscribe starts the clock, and begins to emit after 1 second
|
// Subscribe starts the clock, and begins to emit after 1 second
|
||||||
multicastSequence.subscribe({
|
multicastSequence.subscribe({
|
||||||
|
@ -1,26 +1,21 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { FormsModule } from '@angular/forms';
|
|
||||||
import { HttpClientModule } from '@angular/common/http';
|
import { HttpClientModule } from '@angular/common/http';
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { FormsModule } from '@angular/forms';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
import { AppComponent } from './app.component';
|
||||||
import {
|
import { ExponentialStrengthPipe } from './exponential-strength.pipe';
|
||||||
FlyingHeroesComponent,
|
import { FetchJsonPipe } from './fetch-json.pipe';
|
||||||
FlyingHeroesImpureComponent
|
import { FlyingHeroesComponent, FlyingHeroesImpureComponent } from './flying-heroes.component';
|
||||||
} from './flying-heroes.component';
|
import { FlyingHeroesImpurePipe, FlyingHeroesPipe } from './flying-heroes.pipe';
|
||||||
import { HeroAsyncMessageComponent } from './hero-async-message.component';
|
import { HeroAsyncMessageComponent } from './hero-async-message.component';
|
||||||
import { HeroBirthdayComponent } from './hero-birthday1.component';
|
import { HeroBirthdayComponent } from './hero-birthday1.component';
|
||||||
import { HeroBirthday2Component } from './hero-birthday2.component';
|
import { HeroBirthday2Component } from './hero-birthday2.component';
|
||||||
import { HeroListComponent } from './hero-list.component';
|
import { HeroListComponent } from './hero-list.component';
|
||||||
import { PowerBoosterComponent } from './power-booster.component';
|
|
||||||
import { PowerBoostCalculatorComponent } from './power-boost-calculator.component';
|
import { PowerBoostCalculatorComponent } from './power-boost-calculator.component';
|
||||||
import {
|
import { PowerBoosterComponent } from './power-booster.component';
|
||||||
FlyingHeroesPipe,
|
|
||||||
FlyingHeroesImpurePipe
|
|
||||||
} from './flying-heroes.pipe';
|
|
||||||
import { FetchJsonPipe } from './fetch-json.pipe';
|
|
||||||
import { ExponentialStrengthPipe } from './exponential-strength.pipe';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
@ -43,6 +38,6 @@ import { ExponentialStrengthPipe } from './exponential-strength.pipe';
|
|||||||
FetchJsonPipe,
|
FetchJsonPipe,
|
||||||
ExponentialStrengthPipe
|
ExponentialStrengthPipe
|
||||||
],
|
],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { Pipe, PipeTransform } from '@angular/core';
|
|
||||||
import { HttpClient } from '@angular/common/http';
|
import { HttpClient } from '@angular/common/http';
|
||||||
|
import { Pipe, PipeTransform } from '@angular/core';
|
||||||
|
|
||||||
// #docregion pipe-metadata
|
// #docregion pipe-metadata
|
||||||
@Pipe({
|
@Pipe({
|
||||||
name: 'fetch',
|
name: 'fetch',
|
||||||
pure: false
|
pure: false
|
||||||
})
|
})
|
||||||
// #enddocregion pipe-metadata
|
// #enddocregion pipe-metadata
|
||||||
export class FetchJsonPipe implements PipeTransform {
|
export class FetchJsonPipe implements PipeTransform {
|
||||||
private cachedData: any = null;
|
private cachedData: any = null;
|
||||||
private cachedUrl = '';
|
private cachedUrl = '';
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ export class FetchJsonPipe implements PipeTransform {
|
|||||||
if (url !== this.cachedUrl) {
|
if (url !== this.cachedUrl) {
|
||||||
this.cachedData = null;
|
this.cachedData = null;
|
||||||
this.cachedUrl = url;
|
this.cachedUrl = url;
|
||||||
this.http.get(url).subscribe( result => this.cachedData = result );
|
this.http.get(url).subscribe(result => this.cachedData = result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.cachedData;
|
return this.cachedData;
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
[1030/162525.401:ERROR:process_reader_win.cc(123)] NtOpenThread: {Acceso denegado} Un proceso ha solicitado acceso a un objeto, pero no se le han concedido esos derechos de acceso. (0xc0000022)
|
|
||||||
[1030/162525.402:ERROR:exception_snapshot_win.cc(87)] thread ID 26896 not found in process
|
|
||||||
[1030/162525.402:WARNING:crash_report_exception_handler.cc(62)] ProcessSnapshotWin::Initialize failed
|
|
File diff suppressed because it is too large
Load Diff
@ -1,24 +0,0 @@
|
|||||||
{
|
|
||||||
"description": "Angular Reactive Forms (final)",
|
|
||||||
"files":[
|
|
||||||
"src/styles.css",
|
|
||||||
|
|
||||||
"src/app/app.component.ts",
|
|
||||||
"src/app/app.component.html",
|
|
||||||
"src/app/app.component.css",
|
|
||||||
"src/app/app.module.ts",
|
|
||||||
"src/app/data-model.ts",
|
|
||||||
"src/app/hero.service.ts",
|
|
||||||
"src/app/hero-detail/hero-detail.component.html",
|
|
||||||
"src/app/hero-detail/hero-detail.component.ts",
|
|
||||||
"src/app/hero-detail/hero-detail.component.css",
|
|
||||||
"src/app/hero-list/hero-list.component.html",
|
|
||||||
"src/app/hero-list/hero-list.component.ts",
|
|
||||||
"src/app/hero-list/hero-list.component.css",
|
|
||||||
|
|
||||||
"src/main-final.ts",
|
|
||||||
"src/index-final.html"
|
|
||||||
],
|
|
||||||
"main": "src/index-final.html",
|
|
||||||
"tags": ["reactive", "forms"]
|
|
||||||
}
|
|
@ -1,4 +1,10 @@
|
|||||||
<div class="container">
|
<!-- #docplaster -->
|
||||||
<h1>Reactive Forms</h1>
|
<h1>Reactive Forms</h1>
|
||||||
<app-hero-detail></app-hero-detail>
|
|
||||||
</div>
|
<!-- #docregion app-name-editor-->
|
||||||
|
<app-name-editor></app-name-editor>
|
||||||
|
<!-- #enddocregion app-name-editor-->
|
||||||
|
|
||||||
|
<!-- #docregion app-profile-editor -->
|
||||||
|
<app-profile-editor></app-profile-editor>
|
||||||
|
<!-- #enddocregion app-profile-editor -->
|
@ -1,4 +1,17 @@
|
|||||||
<div class="container">
|
<!-- #docplaster -->
|
||||||
<h1>Reactive Forms</h1>
|
<!-- #docregion app-name-editor -->
|
||||||
<app-hero-list></app-hero-list>
|
<h1>Reactive Forms</h1>
|
||||||
</div>
|
|
||||||
|
<!-- #enddocregion app-name-editor -->
|
||||||
|
<nav>
|
||||||
|
<a (click)="toggleEditor('name')">Name Editor</a>
|
||||||
|
<a (click)="toggleEditor('profile')">Profile Editor</a>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- #docregion app-name-editor -->
|
||||||
|
<app-name-editor *ngIf="showNameEditor"></app-name-editor>
|
||||||
|
<!-- #enddocregion app-name-editor -->
|
||||||
|
|
||||||
|
<!-- #docregion app-profile-editor -->
|
||||||
|
<app-profile-editor *ngIf="showProfileEditor"></app-profile-editor>
|
||||||
|
<!-- #enddocregion app-profile-editor -->
|
||||||
|
@ -0,0 +1,27 @@
|
|||||||
|
import { TestBed, async } from '@angular/core/testing';
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
describe('AppComponent', () => {
|
||||||
|
beforeEach(async(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
declarations: [
|
||||||
|
AppComponent
|
||||||
|
],
|
||||||
|
}).compileComponents();
|
||||||
|
}));
|
||||||
|
it('should create the app', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app).toBeTruthy();
|
||||||
|
}));
|
||||||
|
it(`should have as title 'app'`, async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
const app = fixture.debugElement.componentInstance;
|
||||||
|
expect(app.title).toEqual('app');
|
||||||
|
}));
|
||||||
|
it('should render title in a h1 tag', async(() => {
|
||||||
|
const fixture = TestBed.createComponent(AppComponent);
|
||||||
|
fixture.detectChanges();
|
||||||
|
const compiled = fixture.debugElement.nativeElement;
|
||||||
|
expect(compiled.querySelector('h1').textContent).toContain('Welcome to reactive-forms!');
|
||||||
|
}));
|
||||||
|
});
|
@ -1,9 +1,24 @@
|
|||||||
// #docregion
|
|
||||||
import { Component } from '@angular/core';
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
export type EditorType = 'name' | 'profile';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-root',
|
selector: 'app-root',
|
||||||
templateUrl: './app.component.html',
|
templateUrl: './app.component.html',
|
||||||
styleUrls: ['./app.component.css']
|
styleUrls: ['./app.component.css']
|
||||||
})
|
})
|
||||||
export class AppComponent { }
|
export class AppComponent {
|
||||||
|
editor: EditorType = 'name';
|
||||||
|
|
||||||
|
get showNameEditor() {
|
||||||
|
return this.editor === 'name';
|
||||||
|
}
|
||||||
|
|
||||||
|
get showProfileEditor() {
|
||||||
|
return this.editor === 'profile';
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleEditor(type: EditorType) {
|
||||||
|
this.editor = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,45 +1,34 @@
|
|||||||
// #docplaster
|
// #docplaster
|
||||||
// #docregion
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
// #docregion v1
|
import { NgModule } from '@angular/core';
|
||||||
import { NgModule } from '@angular/core';
|
// #docregion imports
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { ReactiveFormsModule } from '@angular/forms';
|
||||||
import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module
|
|
||||||
|
|
||||||
import { AppComponent } from './app.component';
|
// #enddocregion imports
|
||||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
import { AppComponent } from './app.component';
|
||||||
// #enddocregion v1
|
import { NameEditorComponent } from './name-editor/name-editor.component';
|
||||||
// #docregion hero-service-list
|
import { ProfileEditorComponent } from './profile-editor/profile-editor.component';
|
||||||
// add JavaScript imports
|
|
||||||
import { HeroListComponent } from './hero-list/hero-list.component';
|
|
||||||
import { HeroService } from './hero.service';
|
|
||||||
// #docregion v1
|
|
||||||
|
|
||||||
|
// #docregion imports
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
// #enddocregion imports
|
||||||
declarations: [
|
declarations: [
|
||||||
AppComponent,
|
AppComponent,
|
||||||
HeroDetailComponent,
|
NameEditorComponent,
|
||||||
// #enddocregion v1
|
ProfileEditorComponent
|
||||||
HeroListComponent // <--declare HeroListComponent
|
|
||||||
// #docregion v1
|
|
||||||
],
|
],
|
||||||
// #enddocregion hero-service-list
|
// #docregion imports
|
||||||
imports: [
|
imports: [
|
||||||
|
// #enddocregion imports
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
ReactiveFormsModule // <-- #2 add to @NgModule imports
|
// #docregion imports
|
||||||
|
// other imports ...
|
||||||
|
ReactiveFormsModule
|
||||||
],
|
],
|
||||||
// #enddocregion v1
|
// #enddocregion imports
|
||||||
// export for the DemoModule
|
providers: [],
|
||||||
// #docregion hero-service-list
|
bootstrap: [AppComponent]
|
||||||
// ...
|
// #docregion imports
|
||||||
exports: [
|
|
||||||
AppComponent,
|
|
||||||
HeroDetailComponent,
|
|
||||||
HeroListComponent // <-- export HeroListComponent
|
|
||||||
],
|
|
||||||
providers: [ HeroService ], // <-- provide HeroService
|
|
||||||
// #enddocregion hero-service-list
|
|
||||||
// #docregion v1
|
|
||||||
bootstrap: [ AppComponent ]
|
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
// #enddocregion v1
|
// #enddocregion imports
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
// #docregion
|
|
||||||
// #docregion model-classes
|
|
||||||
export class Hero {
|
|
||||||
id = 0;
|
|
||||||
name = '';
|
|
||||||
addresses: Address[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Address {
|
|
||||||
street = '';
|
|
||||||
city = '';
|
|
||||||
state = '';
|
|
||||||
zip = '';
|
|
||||||
}
|
|
||||||
// #enddocregion model-classes
|
|
||||||
|
|
||||||
export const heroes: Hero[] = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
name: 'Whirlwind',
|
|
||||||
addresses: [
|
|
||||||
{street: '123 Main', city: 'Anywhere', state: 'CA', zip: '94801'},
|
|
||||||
{street: '456 Maple', city: 'Somewhere', state: 'VA', zip: '23226'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
name: 'Bombastic',
|
|
||||||
addresses: [
|
|
||||||
{street: '789 Elm', city: 'Smallville', state: 'OH', zip: '04501'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
name: 'Magneta',
|
|
||||||
addresses: [ ]
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const states = ['CA', 'MD', 'OH', 'VA'];
|
|
@ -1,40 +0,0 @@
|
|||||||
<div class="container">
|
|
||||||
<h1>Reactive Forms</h1>
|
|
||||||
<h4><i>Pick a demo:</i>
|
|
||||||
<select [selectedIndex]="demo - 1" (change)="selectDemo($event.target.selectedIndex)">
|
|
||||||
<option *ngFor="let demo of demos">{{demo}}</option>
|
|
||||||
</select>
|
|
||||||
</h4>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<div class="demo">
|
|
||||||
<app-hero-list *ngIf="demo===final"></app-hero-list>
|
|
||||||
<app-hero-detail-1 *ngIf="demo===1"></app-hero-detail-1>
|
|
||||||
<app-hero-detail-2 *ngIf="demo===2"></app-hero-detail-2>
|
|
||||||
<app-hero-detail-3 *ngIf="demo===3"></app-hero-detail-3>
|
|
||||||
<app-hero-detail-4 *ngIf="demo===4"></app-hero-detail-4>
|
|
||||||
<app-hero-detail-5 *ngIf="demo===5"></app-hero-detail-5>
|
|
||||||
|
|
||||||
<div *ngIf="demo >= 6 && demo !== final" >
|
|
||||||
|
|
||||||
<h3 *ngIf="isLoading"><i>Loading heroes ... </i></h3>
|
|
||||||
<h3 *ngIf="!isLoading">Select a hero:</h3>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<button (click)="getHeroes()" class="btn btn-primary">Refresh</button>
|
|
||||||
<a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div *ngIf="selectedHero">
|
|
||||||
<hr>
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3>Editing: {{selectedHero.name}}</h3>
|
|
||||||
<app-hero-detail-6 [hero]=selectedHero *ngIf="demo===6"></app-hero-detail-6>
|
|
||||||
<app-hero-detail-7 [hero]=selectedHero *ngIf="demo===7"></app-hero-detail-7>
|
|
||||||
<app-hero-detail-8 [hero]=selectedHero *ngIf="demo===8"></app-hero-detail-8>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
@ -1,49 +0,0 @@
|
|||||||
/* tslint:disable:member-ordering */
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { finalize } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { Hero } from './data-model';
|
|
||||||
import { HeroService } from './hero.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-root',
|
|
||||||
templateUrl: './demo.component.html'
|
|
||||||
})
|
|
||||||
export class DemoComponent {
|
|
||||||
|
|
||||||
demos: string[] = [
|
|
||||||
'Just a FormControl',
|
|
||||||
'FormControl in a FormGroup',
|
|
||||||
'Simple FormBuilder group',
|
|
||||||
'Group with multiple controls',
|
|
||||||
'Nested FormBuilder group',
|
|
||||||
'PatchValue',
|
|
||||||
'SetValue',
|
|
||||||
'FormArray',
|
|
||||||
'Final'].map(n => n + ' Demo');
|
|
||||||
|
|
||||||
final = this.demos.length;
|
|
||||||
demo = this.final; // current demo
|
|
||||||
|
|
||||||
heroes: Observable<Hero[]>;
|
|
||||||
isLoading = false;
|
|
||||||
selectedHero: Hero;
|
|
||||||
|
|
||||||
constructor(private heroService: HeroService) { }
|
|
||||||
|
|
||||||
getHeroes() {
|
|
||||||
this.isLoading = true;
|
|
||||||
this.heroes = this.heroService.getHeroes().pipe(
|
|
||||||
finalize(() => this.isLoading = false)
|
|
||||||
);
|
|
||||||
this.selectedHero = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
select(hero: Hero) { this.selectedHero = hero; }
|
|
||||||
|
|
||||||
selectDemo(demo: number) {
|
|
||||||
this.demo = demo + 1;
|
|
||||||
this.getHeroes();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
import { NgModule } from '@angular/core';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
|
||||||
import { ReactiveFormsModule } from '@angular/forms';
|
|
||||||
|
|
||||||
import { AppModule } from './app.module';
|
|
||||||
import { DemoComponent } from './demo.component';
|
|
||||||
import { HeroDetailComponent1 } from './hero-detail/hero-detail-1.component';
|
|
||||||
import { HeroDetailComponent2 } from './hero-detail/hero-detail-2.component';
|
|
||||||
import { HeroDetailComponent3 } from './hero-detail/hero-detail-3.component';
|
|
||||||
import { HeroDetailComponent4 } from './hero-detail/hero-detail-4.component';
|
|
||||||
import { HeroDetailComponent5 } from './hero-detail/hero-detail-5.component';
|
|
||||||
import { HeroDetailComponent6 } from './hero-detail/hero-detail-6.component';
|
|
||||||
import { HeroDetailComponent7 } from './hero-detail/hero-detail-7.component';
|
|
||||||
import { HeroDetailComponent8 } from './hero-detail/hero-detail-8.component';
|
|
||||||
|
|
||||||
@NgModule({
|
|
||||||
imports: [
|
|
||||||
BrowserModule,
|
|
||||||
ReactiveFormsModule,
|
|
||||||
AppModule,
|
|
||||||
],
|
|
||||||
declarations: [ DemoComponent,
|
|
||||||
HeroDetailComponent1,
|
|
||||||
HeroDetailComponent2,
|
|
||||||
HeroDetailComponent3,
|
|
||||||
HeroDetailComponent4,
|
|
||||||
HeroDetailComponent5,
|
|
||||||
HeroDetailComponent6,
|
|
||||||
HeroDetailComponent7,
|
|
||||||
HeroDetailComponent8],
|
|
||||||
bootstrap: [ DemoComponent ]
|
|
||||||
})
|
|
||||||
export class DemoModule { }
|
|
@ -1,8 +0,0 @@
|
|||||||
<!-- #docregion simple-control-->
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3><i>Just a FormControl</i></h3>
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" [formControl]="name">
|
|
||||||
</label>
|
|
||||||
<!-- #enddocregion simple-control-->
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
// #docregion import
|
|
||||||
import { FormControl } from '@angular/forms';
|
|
||||||
// #enddocregion import
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-1',
|
|
||||||
templateUrl: './hero-detail-1.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v1
|
|
||||||
export class HeroDetailComponent1 {
|
|
||||||
name = new FormControl();
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
<!-- #docregion basic-form-->
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3><i>FormControl in a FormGroup</i></h3>
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- #enddocregion basic-form-->
|
|
||||||
|
|
||||||
<!-- #docregion form-value-json -->
|
|
||||||
<p>Form value: {{ heroForm.value | json }}</p>
|
|
||||||
<!-- #enddocregion form-value-json -->
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,17 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docregion imports
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { FormControl, FormGroup } from '@angular/forms';
|
|
||||||
// #enddocregion imports
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-2',
|
|
||||||
templateUrl: './hero-detail-2.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v2
|
|
||||||
export class HeroDetailComponent2 {
|
|
||||||
heroForm = new FormGroup ({
|
|
||||||
name: new FormControl()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// #enddocregion v2
|
|
@ -1,16 +0,0 @@
|
|||||||
<!-- #docregion basic-form-->
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3><i>A FormGroup with a single FormControl using FormBuilder</i></h3>
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- #enddocregion basic-form-->
|
|
||||||
|
|
||||||
<!-- #docregion form-value-json -->
|
|
||||||
<p>Form value: {{ heroForm.value | json }}</p>
|
|
||||||
<p>Form status: {{ heroForm.status | json }}</p>
|
|
||||||
<!-- #enddocregion form-value-json -->
|
|
@ -1,27 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docregion imports
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
||||||
// #enddocregion imports
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-3',
|
|
||||||
templateUrl: './hero-detail-3.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v3
|
|
||||||
export class HeroDetailComponent3 {
|
|
||||||
heroForm: FormGroup; // <--- heroForm is of type FormGroup
|
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) { // <--- inject FormBuilder
|
|
||||||
this.createForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
// #docregion required
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: ['', Validators.required ],
|
|
||||||
});
|
|
||||||
// #enddocregion required
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #enddocregion v3
|
|
@ -1,25 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docregion imports
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { FormBuilder, FormGroup } from '@angular/forms';
|
|
||||||
// #enddocregion imports
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-3',
|
|
||||||
templateUrl: './hero-detail-3.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v3a
|
|
||||||
export class HeroDetailComponent3 {
|
|
||||||
heroForm: FormGroup; // <--- heroForm is of type FormGroup
|
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) { // <--- inject FormBuilder
|
|
||||||
this.createForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: '', // <--- the FormControl called "name"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #enddocregion v3a
|
|
@ -1,46 +0,0 @@
|
|||||||
<!-- #docregion -->
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3><i>A FormGroup with multiple FormControls</i></h3>
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Street:
|
|
||||||
<input class="form-control" formControlName="street">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">City:
|
|
||||||
<input class="form-control" formControlName="city">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">State:
|
|
||||||
<select class="form-control" formControlName="state">
|
|
||||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Zip Code:
|
|
||||||
<input class="form-control" formControlName="zip">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group radio">
|
|
||||||
<h4>Super power:</h4>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="center-block">
|
|
||||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<p>Form value: {{ heroForm.value | json }}</p>
|
|
@ -1,34 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docregion imports
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
||||||
|
|
||||||
import { states } from '../data-model';
|
|
||||||
// #enddocregion imports
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-4',
|
|
||||||
templateUrl: './hero-detail-4.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v4
|
|
||||||
export class HeroDetailComponent4 {
|
|
||||||
heroForm: FormGroup;
|
|
||||||
states = states;
|
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {
|
|
||||||
this.createForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: ['', Validators.required ],
|
|
||||||
street: '',
|
|
||||||
city: '',
|
|
||||||
state: '',
|
|
||||||
zip: '',
|
|
||||||
power: '',
|
|
||||||
sidekick: ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #enddocregion v4
|
|
@ -1,56 +0,0 @@
|
|||||||
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<!-- #docregion add-group-->
|
|
||||||
<div formGroupName="address" class="well well-lg">
|
|
||||||
<h4>Secret Lair</h4>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Street:
|
|
||||||
<input class="form-control" formControlName="street">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">City:
|
|
||||||
<input class="form-control" formControlName="city">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">State:
|
|
||||||
<select class="form-control" formControlName="state">
|
|
||||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Zip Code:
|
|
||||||
<input class="form-control" formControlName="zip">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- #enddocregion add-group-->
|
|
||||||
<div class="form-group radio">
|
|
||||||
<h4>Super power:</h4>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="center-block">
|
|
||||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>heroForm value: {{ heroForm.value | json}}</p>
|
|
||||||
<h4>Extra info for the curious:</h4>
|
|
||||||
<!-- #docregion inspect-value -->
|
|
||||||
<p>Name value: {{ heroForm.get('name').value }}</p>
|
|
||||||
<!-- #enddocregion inspect-value -->
|
|
||||||
|
|
||||||
<!-- #docregion inspect-child-control -->
|
|
||||||
<p>Street value: {{ heroForm.get('address.street').value}}</p>
|
|
||||||
<!-- #enddocregion inspect-child-control -->
|
|
@ -1,35 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
import { Component } from '@angular/core';
|
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
||||||
|
|
||||||
import { states } from '../data-model';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-5',
|
|
||||||
templateUrl: './hero-detail-5.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v5
|
|
||||||
export class HeroDetailComponent5 {
|
|
||||||
heroForm: FormGroup;
|
|
||||||
states = states;
|
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {
|
|
||||||
this.createForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
this.heroForm = this.fb.group({ // <-- the parent FormGroup
|
|
||||||
name: ['', Validators.required ],
|
|
||||||
address: this.fb.group({ // <-- the child FormGroup
|
|
||||||
street: '',
|
|
||||||
city: '',
|
|
||||||
state: '',
|
|
||||||
zip: ''
|
|
||||||
}),
|
|
||||||
power: '',
|
|
||||||
sidekick: ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// #enddocregion v5
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
|||||||
<!-- #docregion -->
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3><i>PatchValue to initialize a value</i></h3>
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Street:
|
|
||||||
<input class="form-control" formControlName="street">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">City:
|
|
||||||
<input class="form-control" formControlName="city">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">State:
|
|
||||||
<select class="form-control" formControlName="state">
|
|
||||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Zip Code:
|
|
||||||
<input class="form-control" formControlName="zip">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group radio">
|
|
||||||
<h4>Super power:</h4>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="center-block">
|
|
||||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<p>Form value: {{ heroForm.value | json }}</p>
|
|
@ -1,66 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docregion import-input
|
|
||||||
import { Component, Input, OnChanges } from '@angular/core';
|
|
||||||
// #enddocregion import-input
|
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
||||||
|
|
||||||
// #docregion import-hero
|
|
||||||
import { Hero, states } from '../data-model';
|
|
||||||
// #enddocregion import-hero
|
|
||||||
|
|
||||||
////////// 6 ////////////////////
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-6',
|
|
||||||
templateUrl: './hero-detail-5.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v6
|
|
||||||
export class HeroDetailComponent6 implements OnChanges {
|
|
||||||
// #docregion hero
|
|
||||||
@Input() hero: Hero;
|
|
||||||
// #enddocregion hero
|
|
||||||
|
|
||||||
heroForm: FormGroup;
|
|
||||||
states = states;
|
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {
|
|
||||||
this.createForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
// #docregion hero-form-model
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: ['', Validators.required ],
|
|
||||||
address: this.fb.group({
|
|
||||||
street: '',
|
|
||||||
city: '',
|
|
||||||
state: '',
|
|
||||||
zip: ''
|
|
||||||
}),
|
|
||||||
power: '',
|
|
||||||
sidekick: ''
|
|
||||||
});
|
|
||||||
// #enddocregion hero-form-model
|
|
||||||
}
|
|
||||||
|
|
||||||
// #docregion patch-value-on-changes
|
|
||||||
ngOnChanges() { // <-- call rebuildForm in ngOnChanges
|
|
||||||
this.rebuildForm();
|
|
||||||
}
|
|
||||||
// #enddocregion patch-value-on-changes
|
|
||||||
|
|
||||||
// #docregion patch-value-rebuildform
|
|
||||||
rebuildForm() { // <-- wrap patchValue in rebuildForm
|
|
||||||
this.heroForm.reset();
|
|
||||||
// #docregion patch-value
|
|
||||||
this.heroForm.patchValue({
|
|
||||||
name: this.hero.name
|
|
||||||
});
|
|
||||||
// #enddocregion patch-value
|
|
||||||
}
|
|
||||||
// #enddocregion patch-value-rebuildform
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// #enddocregion v6
|
|
@ -1,46 +0,0 @@
|
|||||||
<!-- #docregion -->
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3><i>A FormGroup with multiple FormControls</i></h3>
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Street:
|
|
||||||
<input class="form-control" formControlName="street">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">City:
|
|
||||||
<input class="form-control" formControlName="city">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">State:
|
|
||||||
<select class="form-control" formControlName="state">
|
|
||||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Zip Code:
|
|
||||||
<input class="form-control" formControlName="zip">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group radio">
|
|
||||||
<h4>Super power:</h4>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="center-block">
|
|
||||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
<p>Form value: {{ heroForm.value | json }}</p>
|
|
@ -1,68 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docplaster
|
|
||||||
// #docregion imports
|
|
||||||
import { Component, Input, OnChanges } from '@angular/core';
|
|
||||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
||||||
|
|
||||||
// #docregion import-address
|
|
||||||
import { Address, Hero, states } from '../data-model';
|
|
||||||
// #enddocregion import-address
|
|
||||||
|
|
||||||
// #enddocregion imports
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-7',
|
|
||||||
templateUrl: './hero-detail-5.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v7
|
|
||||||
export class HeroDetailComponent7 implements OnChanges {
|
|
||||||
@Input() hero: Hero;
|
|
||||||
|
|
||||||
heroForm: FormGroup;
|
|
||||||
states = states;
|
|
||||||
|
|
||||||
constructor(private fb: FormBuilder) {
|
|
||||||
this.createForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
// #docregion address-form-group
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: ['', Validators.required ],
|
|
||||||
address: this.fb.group(new Address()), // <-- a FormGroup with a new address
|
|
||||||
power: '',
|
|
||||||
sidekick: ''
|
|
||||||
});
|
|
||||||
// #enddocregion address-form-group
|
|
||||||
}
|
|
||||||
|
|
||||||
// #docregion ngOnChanges
|
|
||||||
ngOnChanges() {
|
|
||||||
this.rebuildForm();
|
|
||||||
}
|
|
||||||
// #enddocregion ngOnChanges
|
|
||||||
|
|
||||||
// #docregion rebuildForm
|
|
||||||
rebuildForm() {
|
|
||||||
this.heroForm.reset({
|
|
||||||
name: this.hero.name,
|
|
||||||
// #docregion set-value-address
|
|
||||||
address: this.hero.addresses[0] || new Address()
|
|
||||||
// #enddocregion set-value-address
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// #enddocregion rebuildForm
|
|
||||||
|
|
||||||
/* First version of rebuildForm */
|
|
||||||
rebuildForm1() {
|
|
||||||
// #docregion reset
|
|
||||||
this.heroForm.reset();
|
|
||||||
// #enddocregion reset
|
|
||||||
// #docregion set-value
|
|
||||||
this.heroForm.setValue({
|
|
||||||
name: this.hero.name,
|
|
||||||
address: this.hero.addresses[0] || new Address()
|
|
||||||
});
|
|
||||||
// #enddocregion set-value
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
<!-- #docplaster-->
|
|
||||||
<h3><i>Using FormArray to add groups</i></h3>
|
|
||||||
|
|
||||||
<form [formGroup]="heroForm">
|
|
||||||
<p>Form Changed: {{ heroForm.dirty }}</p>
|
|
||||||
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<!-- #docregion form-array-->
|
|
||||||
<!-- #docregion form-array-skeleton -->
|
|
||||||
<!-- #docregion form-array-name -->
|
|
||||||
<div formArrayName="secretLairs" class="well well-lg">
|
|
||||||
<!-- #enddocregion form-array-name -->
|
|
||||||
<div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >
|
|
||||||
<!-- The repeated address template -->
|
|
||||||
<!-- #enddocregion form-array-skeleton -->
|
|
||||||
<h4>Address #{{i + 1}}</h4>
|
|
||||||
<div style="margin-left: 1em;">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Street:
|
|
||||||
<input class="form-control" formControlName="street">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">City:
|
|
||||||
<input class="form-control" formControlName="city">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">State:
|
|
||||||
<select class="form-control" formControlName="state">
|
|
||||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Zip Code:
|
|
||||||
<input class="form-control" formControlName="zip">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<!-- End of the repeated address template -->
|
|
||||||
<!-- #docregion form-array-skeleton -->
|
|
||||||
</div>
|
|
||||||
<!-- #enddocregion form-array-skeleton -->
|
|
||||||
<!-- #enddocregion form-array-->
|
|
||||||
<!-- #docregion add-lair -->
|
|
||||||
<button (click)="addLair()" type="button">Add a Secret Lair</button>
|
|
||||||
<!-- #enddocregion add-lair -->
|
|
||||||
<!-- #docregion form-array-->
|
|
||||||
<!-- #docregion form-array-skeleton -->
|
|
||||||
</div>
|
|
||||||
<!-- #enddocregion form-array-skeleton -->
|
|
||||||
<!-- #enddocregion form-array-->
|
|
||||||
<div class="form-group radio">
|
|
||||||
<h4>Super power:</h4>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="center-block">
|
|
||||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<p>heroForm value: {{ heroForm.value | json}}</p>
|
|
@ -1,74 +0,0 @@
|
|||||||
/* tslint:disable:component-class-suffix */
|
|
||||||
// #docregion imports
|
|
||||||
import { Component, Input, OnChanges } from '@angular/core';
|
|
||||||
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
||||||
|
|
||||||
import { Address, Hero, states } from '../data-model';
|
|
||||||
// #enddocregion imports
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail-8',
|
|
||||||
templateUrl: './hero-detail-8.component.html'
|
|
||||||
})
|
|
||||||
// #docregion v8
|
|
||||||
export class HeroDetailComponent8 implements OnChanges {
|
|
||||||
@Input() hero: Hero;
|
|
||||||
|
|
||||||
heroForm: FormGroup;
|
|
||||||
states = states;
|
|
||||||
|
|
||||||
// #docregion ctor
|
|
||||||
constructor(private fb: FormBuilder) {
|
|
||||||
this.createForm();
|
|
||||||
this.logNameChange();
|
|
||||||
}
|
|
||||||
// #enddocregion ctor
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
// #docregion secretLairs-form-array
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: ['', Validators.required ],
|
|
||||||
secretLairs: this.fb.array([]), // <-- secretLairs as an empty FormArray
|
|
||||||
power: '',
|
|
||||||
sidekick: ''
|
|
||||||
});
|
|
||||||
// #enddocregion secretLairs-form-array
|
|
||||||
}
|
|
||||||
|
|
||||||
logNameChange() {/* Coming soon */}
|
|
||||||
|
|
||||||
// #docregion onchanges
|
|
||||||
ngOnChanges() {
|
|
||||||
this.rebuildForm();
|
|
||||||
}
|
|
||||||
// #enddocregion onchanges
|
|
||||||
|
|
||||||
// #docregion rebuildform
|
|
||||||
rebuildForm() {
|
|
||||||
this.heroForm.reset({
|
|
||||||
name: this.hero.name
|
|
||||||
});
|
|
||||||
this.setAddresses(this.hero.addresses);
|
|
||||||
}
|
|
||||||
// #enddocregion rebuildform
|
|
||||||
|
|
||||||
// #docregion get-secret-lairs
|
|
||||||
get secretLairs(): FormArray {
|
|
||||||
return this.heroForm.get('secretLairs') as FormArray;
|
|
||||||
};
|
|
||||||
// #enddocregion get-secret-lairs
|
|
||||||
|
|
||||||
// #docregion set-addresses
|
|
||||||
setAddresses(addresses: Address[]) {
|
|
||||||
const addressFGs = addresses.map(address => this.fb.group(address));
|
|
||||||
const addressFormArray = this.fb.array(addressFGs);
|
|
||||||
this.heroForm.setControl('secretLairs', addressFormArray);
|
|
||||||
}
|
|
||||||
// #enddocregion set-addresses
|
|
||||||
|
|
||||||
// #docregion add-lair
|
|
||||||
addLair() {
|
|
||||||
this.secretLairs.push(this.fb.group(new Address()));
|
|
||||||
}
|
|
||||||
// #enddocregion add-lair
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
<!-- #docplaster -->
|
|
||||||
<!-- #docregion -->
|
|
||||||
<!-- #docregion buttons -->
|
|
||||||
<form [formGroup]="heroForm" (ngSubmit)="onSubmit()">
|
|
||||||
<div style="margin-bottom: 1em">
|
|
||||||
<button type="submit"
|
|
||||||
[disabled]="heroForm.pristine" class="btn btn-success">Save</button>
|
|
||||||
<button type="button" (click)="revert()"
|
|
||||||
[disabled]="heroForm.pristine" class="btn btn-danger">Revert</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Hero Detail Controls -->
|
|
||||||
<!-- #enddocregion buttons -->
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Name:
|
|
||||||
<input class="form-control" formControlName="name">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div formArrayName="secretLairs" class="well well-lg">
|
|
||||||
<div *ngFor="let address of secretLairs.controls; let i=index" [formGroupName]="i" >
|
|
||||||
<!-- The repeated address template -->
|
|
||||||
<h4>Address #{{i + 1}}</h4>
|
|
||||||
<div style="margin-left: 1em;">
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Street:
|
|
||||||
<input class="form-control" formControlName="street">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">City:
|
|
||||||
<input class="form-control" formControlName="city">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">State:
|
|
||||||
<select class="form-control" formControlName="state">
|
|
||||||
<option *ngFor="let state of states" [value]="state">{{state}}</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label class="center-block">Zip Code:
|
|
||||||
<input class="form-control" formControlName="zip">
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<br>
|
|
||||||
<!-- End of the repeated address template -->
|
|
||||||
</div>
|
|
||||||
<button (click)="addLair()" type="button">Add a Secret Lair</button>
|
|
||||||
</div>
|
|
||||||
<!-- #docregion buttons -->
|
|
||||||
<div class="form-group radio">
|
|
||||||
<h4>Super power:</h4>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="flight">Flight</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="x-ray vision">X-ray vision</label>
|
|
||||||
<label class="center-block"><input type="radio" formControlName="power" value="strength">Strength</label>
|
|
||||||
</div>
|
|
||||||
<div class="checkbox">
|
|
||||||
<label class="center-block">
|
|
||||||
<input type="checkbox" formControlName="sidekick">I have a sidekick.
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
<!-- #enddocregion buttons -->
|
|
||||||
|
|
||||||
<p>heroForm value: {{ heroForm.value | json}}</p>
|
|
||||||
|
|
||||||
<!-- #docregion name-change-log -->
|
|
||||||
<h4>Name change log</h4>
|
|
||||||
<div *ngFor="let name of nameChangeLog">{{name}}</div>
|
|
||||||
<!-- #enddocregion name-change-log -->
|
|
@ -1,113 +0,0 @@
|
|||||||
// #docplaster
|
|
||||||
// #docregion
|
|
||||||
import { Component, Input, OnChanges } from '@angular/core';
|
|
||||||
import { FormArray, FormBuilder, FormGroup } from '@angular/forms';
|
|
||||||
|
|
||||||
import { Address, Hero, states } from '../data-model';
|
|
||||||
// #docregion import-service
|
|
||||||
import { HeroService } from '../hero.service';
|
|
||||||
// #enddocregion import-service
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-detail',
|
|
||||||
templateUrl: './hero-detail.component.html',
|
|
||||||
styleUrls: ['./hero-detail.component.css']
|
|
||||||
})
|
|
||||||
|
|
||||||
// #docregion onchanges-implementation
|
|
||||||
export class HeroDetailComponent implements OnChanges {
|
|
||||||
// #enddocregion onchanges-implementation
|
|
||||||
@Input() hero: Hero;
|
|
||||||
|
|
||||||
heroForm: FormGroup;
|
|
||||||
// #docregion log-name-change
|
|
||||||
nameChangeLog: string[] = [];
|
|
||||||
// #enddocregion log-name-change
|
|
||||||
states = states;
|
|
||||||
|
|
||||||
// #docregion ctor
|
|
||||||
constructor(
|
|
||||||
private fb: FormBuilder,
|
|
||||||
private heroService: HeroService) {
|
|
||||||
|
|
||||||
this.createForm();
|
|
||||||
this.logNameChange();
|
|
||||||
}
|
|
||||||
// #enddocregion ctor
|
|
||||||
|
|
||||||
createForm() {
|
|
||||||
this.heroForm = this.fb.group({
|
|
||||||
name: '',
|
|
||||||
secretLairs: this.fb.array([]),
|
|
||||||
power: '',
|
|
||||||
sidekick: ''
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
this.rebuildForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
rebuildForm() {
|
|
||||||
this.heroForm.reset({
|
|
||||||
name: this.hero.name
|
|
||||||
});
|
|
||||||
this.setAddresses(this.hero.addresses);
|
|
||||||
}
|
|
||||||
|
|
||||||
get secretLairs(): FormArray {
|
|
||||||
return this.heroForm.get('secretLairs') as FormArray;
|
|
||||||
};
|
|
||||||
|
|
||||||
setAddresses(addresses: Address[]) {
|
|
||||||
const addressFGs = addresses.map(address => this.fb.group(address));
|
|
||||||
const addressFormArray = this.fb.array(addressFGs);
|
|
||||||
this.heroForm.setControl('secretLairs', addressFormArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
addLair() {
|
|
||||||
this.secretLairs.push(this.fb.group(new Address()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// #docregion on-submit
|
|
||||||
onSubmit() {
|
|
||||||
this.hero = this.prepareSaveHero();
|
|
||||||
this.heroService.updateHero(this.hero).subscribe(/* error handling */);
|
|
||||||
this.rebuildForm();
|
|
||||||
}
|
|
||||||
// #enddocregion on-submit
|
|
||||||
|
|
||||||
// #docregion prepare-save-hero
|
|
||||||
prepareSaveHero(): Hero {
|
|
||||||
const formModel = this.heroForm.value;
|
|
||||||
|
|
||||||
// deep copy of form model lairs
|
|
||||||
const secretLairsDeepCopy: Address[] = formModel.secretLairs.map(
|
|
||||||
(address: Address) => Object.assign({}, address)
|
|
||||||
);
|
|
||||||
|
|
||||||
// return new `Hero` object containing a combination of original hero value(s)
|
|
||||||
// and deep copies of changed form model values
|
|
||||||
const saveHero: Hero = {
|
|
||||||
id: this.hero.id,
|
|
||||||
name: formModel.name as string,
|
|
||||||
// addresses: formModel.secretLairs // <-- bad!
|
|
||||||
addresses: secretLairsDeepCopy
|
|
||||||
};
|
|
||||||
return saveHero;
|
|
||||||
}
|
|
||||||
// #enddocregion prepare-save-hero
|
|
||||||
|
|
||||||
// #docregion revert
|
|
||||||
revert() { this.rebuildForm(); }
|
|
||||||
// #enddocregion revert
|
|
||||||
|
|
||||||
// #docregion log-name-change
|
|
||||||
logNameChange() {
|
|
||||||
const nameControl = this.heroForm.get('name');
|
|
||||||
nameControl.valueChanges.forEach(
|
|
||||||
(value: string) => this.nameChangeLog.push(value)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// #enddocregion log-name-change
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
<!-- #docregion -->
|
|
||||||
<nav>
|
|
||||||
<a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div *ngIf="selectedHero">
|
|
||||||
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
|
|
||||||
</div>
|
|
@ -1,17 +0,0 @@
|
|||||||
<!-- #docregion -->
|
|
||||||
<h3 *ngIf="isLoading"><i>Loading heroes ... </i></h3>
|
|
||||||
<h3 *ngIf="!isLoading">Select a hero:</h3>
|
|
||||||
|
|
||||||
<nav>
|
|
||||||
<button (click)="getHeroes()" class="btn btn-primary">Refresh</button>
|
|
||||||
<a *ngFor="let hero of heroes | async" (click)="select(hero)">{{hero.name}}</a>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div *ngIf="selectedHero">
|
|
||||||
<hr>
|
|
||||||
<h2>Hero Detail</h2>
|
|
||||||
<h3>Editing: {{selectedHero.name}}</h3>
|
|
||||||
<!-- #docregion hero-binding -->
|
|
||||||
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
|
|
||||||
<!-- #enddocregion hero-binding -->
|
|
||||||
</div>
|
|
@ -1,32 +0,0 @@
|
|||||||
// #docregion
|
|
||||||
import { Component, OnInit } from '@angular/core';
|
|
||||||
import { Observable } from 'rxjs';
|
|
||||||
import { finalize } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { Hero } from '../data-model';
|
|
||||||
import { HeroService } from '../hero.service';
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'app-hero-list',
|
|
||||||
templateUrl: './hero-list.component.html',
|
|
||||||
styleUrls: ['./hero-list.component.css']
|
|
||||||
})
|
|
||||||
export class HeroListComponent implements OnInit {
|
|
||||||
heroes: Observable<Hero[]>;
|
|
||||||
isLoading = false;
|
|
||||||
selectedHero: Hero;
|
|
||||||
|
|
||||||
constructor(private heroService: HeroService) { }
|
|
||||||
|
|
||||||
ngOnInit() { this.getHeroes(); }
|
|
||||||
|
|
||||||
getHeroes() {
|
|
||||||
this.isLoading = true;
|
|
||||||
this.heroes = this.heroService.getHeroes()
|
|
||||||
// TODO: error handling
|
|
||||||
.pipe(finalize(() => this.isLoading = false));
|
|
||||||
this.selectedHero = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
select(hero: Hero) { this.selectedHero = hero; }
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
// #docregion
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
|
|
||||||
import { Observable, of } from 'rxjs';
|
|
||||||
import { delay } from 'rxjs/operators';
|
|
||||||
|
|
||||||
import { Hero, heroes } from './data-model';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class HeroService {
|
|
||||||
|
|
||||||
delayMs = 500;
|
|
||||||
|
|
||||||
// Fake server get; assume nothing can go wrong
|
|
||||||
getHeroes(): Observable<Hero[]> {
|
|
||||||
return of(heroes).pipe(delay(this.delayMs)); // simulate latency with delay
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fake server update; assume nothing can go wrong
|
|
||||||
updateHero(hero: Hero): Observable<Hero> {
|
|
||||||
const oldHero = heroes.find(h => h.id === hero.id);
|
|
||||||
const newHero = Object.assign(oldHero, hero); // Demo: mutate cached hero
|
|
||||||
return of(newHero).pipe(delay(this.delayMs)); // simulate latency with delay
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,19 @@
|
|||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
width: 6em;
|
||||||
|
margin: .5em 0;
|
||||||
|
color: #607D8B;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 2em;
|
||||||
|
font-size: 1em;
|
||||||
|
padding-left: .4em;
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<!-- #docregion control-binding -->
|
||||||
|
<label>
|
||||||
|
Name:
|
||||||
|
<input type="text" [formControl]="name">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!-- #enddocregion control-binding -->
|
||||||
|
|
||||||
|
<!-- #docregion display-value -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Value: {{ name.value }}
|
||||||
|
</p>
|
||||||
|
<!-- #enddocregion display-value -->
|
||||||
|
|
||||||
|
<!-- #docregion update-value -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
<button (click)="updateName()">Update Name</button>
|
||||||
|
</p>
|
||||||
|
<!-- #enddocregion update-value -->
|
@ -0,0 +1,22 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion create-control
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
import { FormControl } from '@angular/forms';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-name-editor',
|
||||||
|
templateUrl: './name-editor.component.html',
|
||||||
|
styleUrls: ['./name-editor.component.css']
|
||||||
|
})
|
||||||
|
export class NameEditorComponent {
|
||||||
|
name = new FormControl('');
|
||||||
|
// #enddocregion create-control
|
||||||
|
|
||||||
|
// #docregion update-value
|
||||||
|
updateName() {
|
||||||
|
this.name.setValue('Nancy');
|
||||||
|
}
|
||||||
|
// #enddocregion update-value
|
||||||
|
// #docregion create-control
|
||||||
|
}
|
||||||
|
// #enddocregion create-control
|
@ -0,0 +1,67 @@
|
|||||||
|
<!-- #docplaster -->
|
||||||
|
<!-- #docregion formgroup -->
|
||||||
|
<form [formGroup]="profileForm">
|
||||||
|
|
||||||
|
<label>
|
||||||
|
First Name:
|
||||||
|
<input type="text" formControlName="firstName">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Last Name:
|
||||||
|
<input type="text" formControlName="lastName">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<!-- #enddocregion formgroup -->
|
||||||
|
<!-- #docregion formgroupname -->
|
||||||
|
<div formGroupName="address">
|
||||||
|
<h3>Address</h3>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Street:
|
||||||
|
<input type="text" formControlName="street">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
City:
|
||||||
|
<input type="text" formControlName="city">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
State:
|
||||||
|
<input type="text" formControlName="state">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Zip Code:
|
||||||
|
<input type="text" formControlName="zip">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion formgroupname -->
|
||||||
|
|
||||||
|
<!-- #docregion formarrayname -->
|
||||||
|
<div formArrayName="aliases">
|
||||||
|
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
|
||||||
|
|
||||||
|
<div *ngFor="let address of aliases.controls; let i=index">
|
||||||
|
<!-- The repeated alias template -->
|
||||||
|
<label>
|
||||||
|
Alias:
|
||||||
|
<input type="text" [formControlName]="i">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion formarrayname -->
|
||||||
|
<!-- #docregion formgroup -->
|
||||||
|
</form>
|
||||||
|
<!-- #enddocregion formgroup -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Form Value: {{ profileForm.value | json }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- #docregion patch-value -->
|
||||||
|
<p>
|
||||||
|
<button (click)="updateProfile()">Update Profile</button>
|
||||||
|
</p>
|
||||||
|
<!-- #enddocregion patch-value -->
|
@ -0,0 +1,40 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion formgroup, nested-formgroup
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
// #docregion imports
|
||||||
|
import { FormGroup, FormControl } from '@angular/forms';
|
||||||
|
// #enddocregion imports
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-profile-editor',
|
||||||
|
templateUrl: './profile-editor.component.html',
|
||||||
|
styleUrls: ['./profile-editor.component.css']
|
||||||
|
})
|
||||||
|
export class ProfileEditorComponent {
|
||||||
|
// #docregion formgroup-compare
|
||||||
|
profileForm = new FormGroup({
|
||||||
|
firstName: new FormControl(''),
|
||||||
|
lastName: new FormControl(''),
|
||||||
|
// #enddocregion formgroup
|
||||||
|
address: new FormGroup({
|
||||||
|
street: new FormControl(''),
|
||||||
|
city: new FormControl(''),
|
||||||
|
state: new FormControl(''),
|
||||||
|
zip: new FormControl('')
|
||||||
|
})
|
||||||
|
// #docregion formgroup
|
||||||
|
});
|
||||||
|
// #enddocregion formgroup, nested-formgroup, formgroup-compare
|
||||||
|
// #docregion patch-value
|
||||||
|
updateProfile() {
|
||||||
|
this.profileForm.patchValue({
|
||||||
|
firstName: 'Nancy',
|
||||||
|
address: {
|
||||||
|
street: '123 Drew Street'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// #enddocregion patch-value
|
||||||
|
// #docregion formgroup, nested-formgroup
|
||||||
|
}
|
||||||
|
// #enddocregion formgroup
|
@ -0,0 +1,58 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion form-builder
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
// #docregion form-builder-imports
|
||||||
|
import { FormBuilder } from '@angular/forms';
|
||||||
|
// #enddocregion form-builder-imports, form-builder
|
||||||
|
// #docregion form-array-imports
|
||||||
|
import { FormArray } from '@angular/forms';
|
||||||
|
// #docregion form-builder-imports, form-builder
|
||||||
|
// #enddocregion form-builder-imports, form-array-imports
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-profile-editor',
|
||||||
|
templateUrl: './profile-editor.component.html',
|
||||||
|
styleUrls: ['./profile-editor.component.css']
|
||||||
|
})
|
||||||
|
export class ProfileEditorComponent {
|
||||||
|
// #docregion formgroup-compare
|
||||||
|
profileForm = this.fb.group({
|
||||||
|
firstName: [''],
|
||||||
|
lastName: [''],
|
||||||
|
address: this.fb.group({
|
||||||
|
street: [''],
|
||||||
|
city: [''],
|
||||||
|
state: [''],
|
||||||
|
zip: ['']
|
||||||
|
}),
|
||||||
|
// #enddocregion form-builder, formgroup-compare
|
||||||
|
aliases: this.fb.array([
|
||||||
|
this.fb.control('')
|
||||||
|
])
|
||||||
|
// #docregion form-builder, formgroup-compare
|
||||||
|
});
|
||||||
|
// #enddocregion form-builder, formgroup-compare
|
||||||
|
get aliases() {
|
||||||
|
return this.profileForm.get('aliases') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #docregion inject-form-builder, form-builder
|
||||||
|
|
||||||
|
constructor(private fb: FormBuilder) { }
|
||||||
|
// #enddocregion inject-form-builder, form-builder
|
||||||
|
|
||||||
|
updateProfile() {
|
||||||
|
this.profileForm.patchValue({
|
||||||
|
firstName: 'Nancy',
|
||||||
|
address: {
|
||||||
|
street: '123 Drew Street'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
addAlias() {
|
||||||
|
this.aliases.push(this.fb.control(''));
|
||||||
|
}
|
||||||
|
// #docregion form-builder
|
||||||
|
}
|
||||||
|
// #enddocregion form-builder
|
@ -0,0 +1,39 @@
|
|||||||
|
/* ProfileEditorComponent's private CSS styles */
|
||||||
|
:host {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
width: 6em;
|
||||||
|
margin: .5em 0;
|
||||||
|
color: #607D8B;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 2em;
|
||||||
|
font-size: 1em;
|
||||||
|
padding-left: .4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
font-family: Arial;
|
||||||
|
background-color: #eee;
|
||||||
|
border: none;
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #cfd8dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:disabled {
|
||||||
|
background-color: #eee;
|
||||||
|
color: #ccc;
|
||||||
|
cursor: auto;
|
||||||
|
}
|
@ -0,0 +1,80 @@
|
|||||||
|
<!-- #docplaster -->
|
||||||
|
<!-- #docregion ng-submit -->
|
||||||
|
<form [formGroup]="profileForm" (ngSubmit)="onSubmit()">
|
||||||
|
<!-- #enddocregion ng-submit -->
|
||||||
|
<label>
|
||||||
|
First Name:
|
||||||
|
<!-- #docregion required-attribute -->
|
||||||
|
<input type="text" formControlName="firstName" required>
|
||||||
|
<!-- #enddocregion required-attribute -->
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Last Name:
|
||||||
|
<input type="text" formControlName="lastName">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div formGroupName="address">
|
||||||
|
<h3>Address</h3>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Street:
|
||||||
|
<input type="text" formControlName="street">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
City:
|
||||||
|
<input type="text" formControlName="city">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
State:
|
||||||
|
<input type="text" formControlName="state">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
Zip Code:
|
||||||
|
<input type="text" formControlName="zip">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- #docregion formarrayname -->
|
||||||
|
<div formArrayName="aliases">
|
||||||
|
<h3>Aliases</h3> <button (click)="addAlias()">Add Alias</button>
|
||||||
|
|
||||||
|
<div *ngFor="let address of aliases.controls; let i=index">
|
||||||
|
<!-- The repeated alias template -->
|
||||||
|
<label>
|
||||||
|
Alias:
|
||||||
|
<input type="text" [formControlName]="i">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- #enddocregion formarrayname -->
|
||||||
|
|
||||||
|
<!-- #docregion submit-button -->
|
||||||
|
<button type="submit" [disabled]="!profileForm.valid">Submit</button>
|
||||||
|
<!-- #enddocregion submit-button -->
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<!-- #docregion display-value -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Form Value: {{ profileForm.value | json }}
|
||||||
|
</p>
|
||||||
|
<!-- #enddocregion display-value -->
|
||||||
|
|
||||||
|
<!-- #docregion display-status -->
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Form Status: {{ profileForm.status }}
|
||||||
|
</p>
|
||||||
|
<!-- #enddocregion display-status -->
|
||||||
|
|
||||||
|
<!-- #docregion patch-value -->
|
||||||
|
<p>
|
||||||
|
<button (click)="updateProfile()">Update Profile</button>
|
||||||
|
</p>
|
||||||
|
<!-- #enddocregion patch-value -->
|
@ -0,0 +1,73 @@
|
|||||||
|
// #docplaster
|
||||||
|
// #docregion form-builder
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
// #docregion form-builder-imports
|
||||||
|
import { FormBuilder } from '@angular/forms';
|
||||||
|
// #enddocregion form-builder-imports
|
||||||
|
// #docregion validator-imports
|
||||||
|
import { Validators } from '@angular/forms';
|
||||||
|
// #enddocregion validator-imports
|
||||||
|
// #docregion form-array-imports
|
||||||
|
import { FormArray } from '@angular/forms';
|
||||||
|
// #enddocregion form-array-imports
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-profile-editor',
|
||||||
|
templateUrl: './profile-editor.component.html',
|
||||||
|
styleUrls: ['./profile-editor.component.css']
|
||||||
|
})
|
||||||
|
export class ProfileEditorComponent {
|
||||||
|
// #docregion required-validator, aliases
|
||||||
|
profileForm = this.fb.group({
|
||||||
|
firstName: ['', Validators.required],
|
||||||
|
lastName: [''],
|
||||||
|
address: this.fb.group({
|
||||||
|
street: [''],
|
||||||
|
city: [''],
|
||||||
|
state: [''],
|
||||||
|
zip: ['']
|
||||||
|
}),
|
||||||
|
// #enddocregion form-builder, required-validator
|
||||||
|
aliases: this.fb.array([
|
||||||
|
this.fb.control('')
|
||||||
|
])
|
||||||
|
// #docregion form-builder, required-validator
|
||||||
|
});
|
||||||
|
// #enddocregion form-builder, required-validator, aliases
|
||||||
|
// #docregion aliases-getter
|
||||||
|
|
||||||
|
get aliases() {
|
||||||
|
return this.profileForm.get('aliases') as FormArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #enddocregion aliases-getter
|
||||||
|
// #docregion inject-form-builder, form-builder
|
||||||
|
constructor(private fb: FormBuilder) { }
|
||||||
|
|
||||||
|
// #enddocregion inject-form-builder
|
||||||
|
|
||||||
|
updateProfile() {
|
||||||
|
this.profileForm.patchValue({
|
||||||
|
firstName: 'Nancy',
|
||||||
|
address: {
|
||||||
|
street: '123 Drew Street'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// #enddocregion form-builder
|
||||||
|
// #docregion add-alias
|
||||||
|
|
||||||
|
addAlias() {
|
||||||
|
this.aliases.push(this.fb.control(''));
|
||||||
|
}
|
||||||
|
// #enddocregion add-alias
|
||||||
|
// #docregion on-submit
|
||||||
|
|
||||||
|
onSubmit() {
|
||||||
|
// TODO: Use EventEmitter with form value
|
||||||
|
console.warn(this.profileForm.value);
|
||||||
|
}
|
||||||
|
// #enddocregion on-submit
|
||||||
|
// #docregion form-builder
|
||||||
|
}
|
||||||
|
// #enddocregion form-builder
|
@ -1,17 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!-- #docregion -->
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title>Hero Form</title>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<!-- #docregion bootstrap -->
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<app-root></app-root>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
@ -2,10 +2,9 @@
|
|||||||
<!-- #docregion -->
|
<!-- #docregion -->
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Hero Form</title>
|
<title>Angular Reactive Forms</title>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
<link rel="stylesheet" href="https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css">
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
import { enableProdMode } from '@angular/core';
|
import { enableProdMode } from '@angular/core';
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
import { AppModule } from './app/app.module'; // just the final version
|
import { AppModule } from './app/app.module';
|
||||||
import { DemoModule } from './app/demo.module'; // demo picker
|
|
||||||
import { environment } from './environments/environment';
|
import { environment } from './environments/environment';
|
||||||
|
|
||||||
if (environment.production) {
|
if (environment.production) {
|
||||||
enableProdMode();
|
enableProdMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(DemoModule);
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
@ -1 +0,0 @@
|
|||||||
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
|
|
@ -3,6 +3,7 @@
|
|||||||
"files":[
|
"files":[
|
||||||
"!**/*.d.ts",
|
"!**/*.d.ts",
|
||||||
"!**/*.js",
|
"!**/*.js",
|
||||||
|
"!**/*.[0-9].*",
|
||||||
|
|
||||||
"!src/app/app.component.1.ts",
|
"!src/app/app.component.1.ts",
|
||||||
"!src/app/hero-list.component.1.html",
|
"!src/app/hero-list.component.1.html",
|
||||||
|
@ -8,7 +8,7 @@ const nums = of(1, 2, 3, 4, 5);
|
|||||||
|
|
||||||
// Create a function that accepts an Observable.
|
// Create a function that accepts an Observable.
|
||||||
const squareOddVals = pipe(
|
const squareOddVals = pipe(
|
||||||
filter(n => n % 2),
|
filter((n: number) => n % 2 !== 0),
|
||||||
map(n => n * n)
|
map(n => n * n)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ import { HeroService } from './heroes';
|
|||||||
<toh-heroes></toh-heroes>
|
<toh-heroes></toh-heroes>
|
||||||
`,
|
`,
|
||||||
styleUrls: ['./app.component.css'],
|
styleUrls: ['./app.component.css'],
|
||||||
providers: [ HeroService ]
|
providers: [HeroService]
|
||||||
})
|
})
|
||||||
export class AppComponent { }
|
export class AppComponent {}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
/* avoid */
|
/* avoid */
|
||||||
|
import { Component, NgModule, OnInit } from '@angular/core';
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
|
||||||
import { BrowserModule } from '@angular/platform-browser';
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
import { NgModule, Component, OnInit } from '@angular/core';
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
class Hero {
|
class Hero {
|
||||||
id: number;
|
id: number;
|
||||||
@ -24,24 +23,24 @@ class AppComponent implements OnInit {
|
|||||||
heroes: Hero[] = [];
|
heroes: Hero[] = [];
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
getHeroes().then(heroes => this.heroes = heroes);
|
getHeroes().then(heroes => (this.heroes = heroes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [ BrowserModule ],
|
imports: [BrowserModule],
|
||||||
declarations: [ AppComponent ],
|
declarations: [AppComponent],
|
||||||
exports: [ AppComponent ],
|
exports: [AppComponent],
|
||||||
bootstrap: [ AppComponent ]
|
bootstrap: [AppComponent]
|
||||||
})
|
})
|
||||||
export class AppModule { }
|
export class AppModule {}
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
|
||||||
const HEROES: Hero[] = [
|
const HEROES: Hero[] = [
|
||||||
{id: 1, name: 'Bombasto'},
|
{ id: 1, name: 'Bombasto' },
|
||||||
{id: 2, name: 'Tornado'},
|
{ id: 2, name: 'Tornado' },
|
||||||
{id: 3, name: 'Magneta'},
|
{ id: 3, name: 'Magneta' }
|
||||||
];
|
];
|
||||||
|
|
||||||
function getHeroes(): Promise<Hero[]> {
|
function getHeroes(): Promise<Hero[]> {
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
import { Hero } from './hero.model';
|
import { Hero } from './hero.model';
|
||||||
|
|
||||||
export const HEROES: Hero[] = [
|
export const HEROES: Hero[] = [
|
||||||
{id: 1, name: 'Bombasto'},
|
{ id: 1, name: 'Bombasto' },
|
||||||
{id: 2, name: 'Tornado'},
|
{ id: 2, name: 'Tornado' },
|
||||||
{id: 3, name: 'Magneta'},
|
{ id: 3, name: 'Magneta' }
|
||||||
];
|
];
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// #docregion
|
// #docregion
|
||||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
import { AppModule } from './app/app.module';
|
import { AppModule } from './app/app.module';
|
||||||
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user