Compare commits
381 Commits
Author | SHA1 | Date | |
---|---|---|---|
024aba075c | |||
a86d038875 | |||
bc8a93e842 | |||
4c18f637ae | |||
fc189b2577 | |||
778ddb257a | |||
2d4f4b5d12 | |||
912742f1ca | |||
0746485136 | |||
265489518e | |||
00dd566b47 | |||
59fd91d785 | |||
56712aa771 | |||
529e4b1565 | |||
9ff4617956 | |||
8887d75723 | |||
7717ff187a | |||
98b18cd49f | |||
fe23a6e77e | |||
d7c4898081 | |||
98c509fecb | |||
a92f111b66 | |||
de1c44f6e3 | |||
183b079175 | |||
8f8caa13b7 | |||
195dc0748b | |||
2c6f84b25d | |||
8e726f7d7b | |||
2d1102f5bf | |||
0437598609 | |||
bc2bf184a2 | |||
b51ce62c58 | |||
25b532e819 | |||
69c8226a9f | |||
5326537985 | |||
ada486a1dd | |||
563e8e3e56 | |||
dd931c73ec | |||
b8975a90ca | |||
f44161503a | |||
6c55a130b1 | |||
144a624088 | |||
f561f5a460 | |||
916914be13 | |||
9a98de941d | |||
0f1de35604 | |||
d89f57f9d5 | |||
ac5b69f783 | |||
5ddd6dcedd | |||
ad68332fa0 | |||
2880cf9ef1 | |||
64a8584a92 | |||
97897ab738 | |||
9378f44d6d | |||
afa46af4c6 | |||
dbffdcc442 | |||
06d68a1b9f | |||
75dd3c5ca5 | |||
23f56198a5 | |||
69167e4519 | |||
10feafcf27 | |||
3f3fed95be | |||
94433f3b9e | |||
aa753878e6 | |||
3513ae40cc | |||
979e7b6086 | |||
41f008d109 | |||
c950410ee1 | |||
b0fa504d39 | |||
426f9710a0 | |||
ae01c70bba | |||
3341a97154 | |||
2056e1f05c | |||
3938a8be75 | |||
393db94b8d | |||
65744e4ae1 | |||
56bc86992c | |||
9426c02648 | |||
0b356d4163 | |||
a886659444 | |||
3649958595 | |||
8919577c54 | |||
84eff4219e | |||
c11e84ee18 | |||
1ce5a495d3 | |||
1f532aaa5a | |||
e81982ef90 | |||
776bc38999 | |||
7e7ef8ee25 | |||
dd90d92573 | |||
1b862820e9 | |||
1a642231cd | |||
138e0d79cd | |||
f0a43716e2 | |||
2be27fb888 | |||
74f07f40e0 | |||
aa66e84e44 | |||
e1bc3f5c1a | |||
f5b366147b | |||
3147a92ee9 | |||
ffedbde0b3 | |||
e543c734ab | |||
fbe6871a94 | |||
f43cb9398c | |||
1e04df9ac7 | |||
840ca05fe8 | |||
facc9d498d | |||
9d0999027f | |||
c00dd5a3f3 | |||
3908a63381 | |||
20f9e51b8f | |||
232203242a | |||
0ca634e697 | |||
7fd9918024 | |||
b0aacb81d4 | |||
6c850eb031 | |||
0df61ad107 | |||
fa985ac067 | |||
06f9197361 | |||
c2bad1249e | |||
9d69ff8ddd | |||
3fd33f8eb0 | |||
501a243b3f | |||
b91b9efc91 | |||
03718c95ce | |||
0834710c18 | |||
9a9a7de9a4 | |||
ff51691221 | |||
2ad90ab0d3 | |||
c5872e6782 | |||
d20877bf3f | |||
b3089b4963 | |||
880b03101e | |||
451d996414 | |||
ea3669e334 | |||
ea2987c7af | |||
5b823dedcc | |||
80f7c83bdd | |||
53072b10da | |||
30c2f560b1 | |||
18c4976f3b | |||
0139173c7c | |||
f3c9954a68 | |||
e8765352e8 | |||
9e61ad897e | |||
0d81151cbc | |||
70319722a1 | |||
cfa5b6567d | |||
41698bf5fd | |||
23c50e27fc | |||
0ae8ea254a | |||
19deca159b | |||
dc3e8aa0fb | |||
a9222c0ade | |||
ea3127e3f9 | |||
8e30f7b1aa | |||
46d81b48ac | |||
3e51a2cb26 | |||
faf60d9310 | |||
0639cb9de1 | |||
810d025488 | |||
efe49c141a | |||
a3d9878c9d | |||
10213d0ca0 | |||
0e1919c2db | |||
d579b8ce05 | |||
d69ba735ee | |||
2991b1b217 | |||
b18cf21e99 | |||
c17098dae6 | |||
43e3073687 | |||
f28878f92f | |||
a10bdefa8b | |||
2f377dbcdd | |||
e2e7b4943e | |||
b8a1363bb2 | |||
e9e6a58dd0 | |||
5932a79713 | |||
7c6a082afd | |||
8a2fe5b7c9 | |||
c9eb4910eb | |||
a634a5abbc | |||
41225026e4 | |||
e9f2203347 | |||
906b3ec8e7 | |||
1cb0c4644a | |||
9d28a27215 | |||
f04aef48f2 | |||
d249852f4a | |||
707dd59767 | |||
4d2570576a | |||
76e58e6cca | |||
a27066b9d2 | |||
29dfc8f3ac | |||
b5533e0ee5 | |||
b275d378df | |||
96655f7aac | |||
253f509493 | |||
5a02ae2f84 | |||
f860752902 | |||
efa126f157 | |||
715135b117 | |||
c30fc52d99 | |||
434eb971e4 | |||
6e31e22d41 | |||
8fef926cd2 | |||
7698afedb1 | |||
a583d12660 | |||
a867d71ece | |||
84b43daf65 | |||
9b3423b50d | |||
d6041d83ec | |||
a638f504eb | |||
a29c756251 | |||
f206eb94bb | |||
3d6e82eccd | |||
154289305e | |||
469b1e4a9a | |||
7fee1fd442 | |||
0ee5b7efa5 | |||
afff84c03f | |||
23f2a7069f | |||
42260702f7 | |||
eec231d21e | |||
95344d6039 | |||
1c9839e91d | |||
bc1a66e907 | |||
2a83dbb0d8 | |||
4007d00403 | |||
61e32ac3c4 | |||
43ee10fbbd | |||
eb90039ea1 | |||
5d318ff234 | |||
f3361abdd7 | |||
52cbe894e9 | |||
41c2030534 | |||
daee41a40a | |||
68bd45ba87 | |||
0f6407750b | |||
95fca24fd8 | |||
a8f6542115 | |||
c6b618d020 | |||
ad6052e397 | |||
e9d1709156 | |||
b5d3de50cc | |||
734d37b231 | |||
bc2063807c | |||
2a528fcb15 | |||
752b83ac81 | |||
56be3375ec | |||
1b83b3fb15 | |||
14d4625ede | |||
fd880a8de4 | |||
bd3dddce4b | |||
1336a9451f | |||
d280077412 | |||
2b578f5c61 | |||
12dcb313af | |||
f576851ecc | |||
ca6cb66c32 | |||
484233f6c1 | |||
3d8799b3a2 | |||
8733843c11 | |||
f1097914c5 | |||
06776d1d10 | |||
2b31b6dc3f | |||
2254ac23e4 | |||
38c678fdcd | |||
1a655836cb | |||
2e466f4bea | |||
7b06fa88ab | |||
3d712894ae | |||
75b8edae03 | |||
fe7f48c1d5 | |||
29600cbb19 | |||
5581e97d2a | |||
19262d9f90 | |||
1eb1c6315d | |||
3807599a4d | |||
2ed41d9e38 | |||
5eb9c01bb6 | |||
09d9662386 | |||
844cbd9774 | |||
373a47dda9 | |||
83f12f3047 | |||
bbc416cdcd | |||
07f2098655 | |||
9b53a6e779 | |||
65f8505fb6 | |||
5a5ea45c40 | |||
52a3657b48 | |||
3824e3f858 | |||
05aa5e0179 | |||
4ddeb030e7 | |||
afe6380429 | |||
947ea17a09 | |||
a190c45a64 | |||
902781803f | |||
0d480ac0dc | |||
6934bf4863 | |||
7e7ea33fb0 | |||
5bd8c6887e | |||
d28ab372c8 | |||
d0ccf5f169 | |||
ecde15298a | |||
983e5f2d7e | |||
5fc4299e0a | |||
1823d5dd1c | |||
91d4da0d2f | |||
22eb8e26fc | |||
f6002c1702 | |||
14138f6382 | |||
f11daa2031 | |||
31a435ef5b | |||
3e92b22258 | |||
2e5457c824 | |||
1ab5fba92e | |||
e1e57ddaa7 | |||
ee7cb48877 | |||
a30c57090a | |||
8a49ec4f27 | |||
697b6c040c | |||
4008e36e80 | |||
e47bb52084 | |||
ac2b530f4b | |||
64bf6edf00 | |||
adf6235479 | |||
04c18ac1aa | |||
1b26dd8cdb | |||
f721b06bde | |||
0bc8443e12 | |||
db17231597 | |||
540626a3a6 | |||
391bfcede5 | |||
d8de6488dd | |||
151fb66848 | |||
02424ff0d0 | |||
f0925d9705 | |||
212b806eda | |||
b9431e88fb | |||
7790cfa0d0 | |||
41b5149509 | |||
06f865640d | |||
824f74f27b | |||
5301c43eed | |||
f280d1aef1 | |||
5e741d42a6 | |||
0035d41030 | |||
08f447ceec | |||
8953f123e3 | |||
6274007e3b | |||
ee76be7783 | |||
a8c720bc3a | |||
ac47a3cd93 | |||
041458a3d2 | |||
2cb74b748f | |||
cb0bfe7a43 | |||
8ee11aeaa6 | |||
bf89fcb361 | |||
4f70c5b6f7 | |||
ad3ebec2a5 | |||
3f5d61f2dd | |||
11ada7f78b | |||
0b96bc7456 | |||
fefadadff3 | |||
946057ae29 | |||
d4293aaaaa | |||
e3dcc227f6 | |||
c9230dd90e | |||
234af9ba59 | |||
7204028d3e | |||
f7c55952bf | |||
e38e3bd135 | |||
cd20c01ba1 | |||
12a191ef3f | |||
65e67b3c3a | |||
32e57f6197 | |||
7de69ba29b | |||
44193c0b94 | |||
6db8241ffa | |||
33c594516c |
@ -12,8 +12,8 @@
|
||||
## IMPORTANT
|
||||
# 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.
|
||||
var_1: &docker_image angular/ngcontainer:0.3.3
|
||||
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.3.3
|
||||
var_1: &docker_image angular/ngcontainer:0.3.2
|
||||
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.3.2
|
||||
|
||||
# Define common ENV vars
|
||||
var_3: &define_env_vars
|
||||
@ -85,7 +85,7 @@ jobs:
|
||||
# This avoids waiting for the slowest build target to finish before running the first test
|
||||
# See https://github.com/bazelbuild/bazel/issues/4257
|
||||
# NOTE: Angular developers should typically just bazel build //packages/... or bazel test //packages/...
|
||||
- run: bazel query --output=label //... | xargs bazel test --build_tag_filters=-ivy-only --test_tag_filters=-manual,-ivy-only
|
||||
- run: bazel query --output=label //... | xargs bazel test
|
||||
|
||||
# CircleCI will allow us to go back and view/download these artifacts from past builds.
|
||||
# Also we can use a service like https://buildsize.org/ to automatically track binary size of these artifacts.
|
||||
@ -111,42 +111,6 @@ jobs:
|
||||
paths:
|
||||
- "node_modules"
|
||||
- "~/bazel_repository_cache"
|
||||
# Temporary job to test what will happen when we flip the Ivy flag to true
|
||||
test_ivy_jit:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *define_env_vars
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
- run: .circleci/setup_cache.sh
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- *setup-bazel-remote-cache
|
||||
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel query --output=label //... | xargs bazel test --define=compile=jit --build_tag_filters=ivy-jit --test_tag_filters=-manual,ivy-jit
|
||||
|
||||
test_ivy_aot:
|
||||
<<: *job_defaults
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *define_env_vars
|
||||
- checkout:
|
||||
<<: *post_checkout
|
||||
# See remote cache documentation in /docs/BAZEL.md
|
||||
- run: .circleci/setup_cache.sh
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- *setup-bazel-remote-cache
|
||||
|
||||
- restore_cache:
|
||||
key: *cache_key
|
||||
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel query --output=label //... | xargs bazel test --define=compile=local --build_tag_filters=ivy-local --test_tag_filters=-manual,ivy-local
|
||||
|
||||
# This job exists only for backwards-compatibility with old scripts and tests
|
||||
# that rely on the pre-Bazel dist/packages-dist layout.
|
||||
@ -176,8 +140,6 @@ jobs:
|
||||
root: dist
|
||||
paths:
|
||||
- packages-dist
|
||||
- packages-dist-ivy-jit
|
||||
- packages-dist-ivy-local
|
||||
|
||||
# We run the integration tests outside of Bazel for now.
|
||||
# They are a separate workflow job so that they can be easily re-run.
|
||||
@ -242,8 +204,6 @@ workflows:
|
||||
jobs:
|
||||
- lint
|
||||
- test
|
||||
- test_ivy_jit
|
||||
- test_ivy_aot
|
||||
- build-packages-dist
|
||||
- integration_test:
|
||||
requires:
|
||||
@ -256,8 +216,6 @@ workflows:
|
||||
requires:
|
||||
# Only publish if tests and integration tests pass
|
||||
- test
|
||||
- test_ivy_jit
|
||||
- test_ivy_aot
|
||||
- integration_test
|
||||
# Get the artifacts to publish from the build-packages-dist job
|
||||
# since the publishing script expects the legacy outputs layout.
|
||||
|
@ -23,6 +23,7 @@
|
||||
# petebacondarwin - Pete Bacon Darwin
|
||||
# pkozlowski-opensource - Pawel Kozlowski
|
||||
# robwormald - Rob Wormald
|
||||
# tinayuangao - Tina Gao
|
||||
# vicb - Victor Berchet
|
||||
# vikerman - Vikram Subramanian
|
||||
|
||||
@ -162,7 +163,7 @@ groups:
|
||||
files:
|
||||
- "packages/compiler/*"
|
||||
users:
|
||||
- alxhub #primary
|
||||
- chuckjaz #primary
|
||||
- vicb
|
||||
- mhevery
|
||||
- IgorMinar #fallback
|
||||
@ -217,6 +218,7 @@ groups:
|
||||
- "aio/content/examples/reactive-forms/*"
|
||||
users:
|
||||
- kara #primary
|
||||
- tinayuangao #secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
|
185
BUILD.bazel
185
BUILD.bazel
@ -5,7 +5,6 @@ load("@build_bazel_rules_nodejs//:defs.bzl", "node_modules_filegroup")
|
||||
exports_files([
|
||||
"tsconfig.json",
|
||||
"LICENSE",
|
||||
"protractor-perf.conf.js",
|
||||
])
|
||||
|
||||
# Developers should always run `bazel run :install`
|
||||
@ -18,207 +17,23 @@ alias(
|
||||
node_modules_filegroup(
|
||||
name = "node_modules",
|
||||
packages = [
|
||||
"adm-zip",
|
||||
"ajv",
|
||||
"angular",
|
||||
"angular-1.5",
|
||||
"angular-mocks",
|
||||
"angular-mocks-1.5",
|
||||
"anymatch",
|
||||
"arr-diff",
|
||||
"arr-flatten",
|
||||
"arr-union",
|
||||
"array-unique",
|
||||
"asn1",
|
||||
"assert-plus",
|
||||
"assign-symbols",
|
||||
"async-each",
|
||||
"asynckit",
|
||||
"atob",
|
||||
"aws-sign2",
|
||||
"aws4",
|
||||
"balanced-match",
|
||||
"base",
|
||||
"base64-js",
|
||||
"binary-extensions",
|
||||
"blocking-proxy",
|
||||
"brace-expansion",
|
||||
"braces",
|
||||
"bytebuffer",
|
||||
"cache-base",
|
||||
"caseless",
|
||||
"chokidar",
|
||||
"class-utils",
|
||||
"co",
|
||||
"collection-visit",
|
||||
"combined-stream",
|
||||
"component-emitter",
|
||||
"concat-map",
|
||||
"copy-descriptor",
|
||||
"core-util-is",
|
||||
"debug",
|
||||
"decode-uri-component",
|
||||
"define-property",
|
||||
"delayed-stream",
|
||||
"domino",
|
||||
"expand-brackets",
|
||||
"expand-range",
|
||||
"extend",
|
||||
"extend-shallow",
|
||||
"extglob",
|
||||
"extsprintf",
|
||||
"fast-deep-equal",
|
||||
"fast-json-stable-stringify",
|
||||
"filename-regex",
|
||||
"fill-range",
|
||||
"for-in",
|
||||
"for-own",
|
||||
"forever-agent",
|
||||
"form-data",
|
||||
"fragment-cache",
|
||||
"fs.realpath",
|
||||
"get-value",
|
||||
"glob",
|
||||
"glob-base",
|
||||
"glob-parent",
|
||||
"graceful-fs",
|
||||
"hammerjs",
|
||||
"har-schema",
|
||||
"har-validator",
|
||||
"has-value",
|
||||
"has-values",
|
||||
"http-signature",
|
||||
"https-proxy-agent",
|
||||
"inflight",
|
||||
"inherits",
|
||||
"is-accessor-descriptor",
|
||||
"is-binary-path",
|
||||
"is-buffer",
|
||||
"is-data-descriptor",
|
||||
"is-descriptor",
|
||||
"is-dotfile",
|
||||
"is-equal-shallow",
|
||||
"is-extendable",
|
||||
"is-extglob",
|
||||
"is-glob",
|
||||
"is-number",
|
||||
"is-plain-object",
|
||||
"is-posix-bracket",
|
||||
"is-primitive",
|
||||
"is-typedarray",
|
||||
"is-windows",
|
||||
"isarray",
|
||||
"isobject",
|
||||
"isstream",
|
||||
"jasmine",
|
||||
"jasmine-core",
|
||||
"jasminewd2",
|
||||
"json-schema",
|
||||
"json-schema-traverse",
|
||||
"json-stable-stringify",
|
||||
"json-stringify-safe",
|
||||
"jsprim",
|
||||
"kind-of",
|
||||
"long",
|
||||
"lru-cache",
|
||||
"map-cache",
|
||||
"map-visit",
|
||||
"math-random",
|
||||
"micromatch",
|
||||
"mime-db",
|
||||
"mime-types",
|
||||
"minimatch",
|
||||
"minimist",
|
||||
"mixin-deep",
|
||||
"nanomatch",
|
||||
"normalize-path",
|
||||
"oauth-sign",
|
||||
"object.omit",
|
||||
"object.pick",
|
||||
"object-copy",
|
||||
"object-visit",
|
||||
"once",
|
||||
"optimist",
|
||||
"options",
|
||||
"os-tmpdir",
|
||||
"parse-glob",
|
||||
"pascalcase",
|
||||
"path-dirname",
|
||||
"path-is-absolute",
|
||||
"performance-now",
|
||||
"posix-character-classes",
|
||||
"preserve",
|
||||
"process-nextick-args",
|
||||
"protobufjs",
|
||||
"protractor",
|
||||
"qs",
|
||||
"randomatic",
|
||||
"readable-stream",
|
||||
"readdirp",
|
||||
"reflect-metadata",
|
||||
"regex-cache",
|
||||
"regex-not",
|
||||
"remove-trailing-separator",
|
||||
"repeat-element",
|
||||
"repeat-string",
|
||||
"request",
|
||||
"ret",
|
||||
"rimraf",
|
||||
"safe-buffer",
|
||||
"safe-regex",
|
||||
"sax",
|
||||
"semver",
|
||||
"set-immediate-shim",
|
||||
"set-value",
|
||||
"shelljs",
|
||||
"sigmund",
|
||||
"snapdragon",
|
||||
"snapdragon-node",
|
||||
"snapdragon-util",
|
||||
"source-map",
|
||||
"source-map-resolve",
|
||||
"source-map-support",
|
||||
"source-map-url",
|
||||
"split-string",
|
||||
"sshpk",
|
||||
"static-extend",
|
||||
"stringstream",
|
||||
"tmp",
|
||||
"to-object-path",
|
||||
"to-regex",
|
||||
"to-regex-range",
|
||||
"tough-cookie",
|
||||
"tsickle",
|
||||
"tslib",
|
||||
"tsutils",
|
||||
"tunnel-agent",
|
||||
"typescript",
|
||||
"union-value",
|
||||
"unset-value",
|
||||
"upath",
|
||||
"uri-js",
|
||||
"urix",
|
||||
"use",
|
||||
"util-deprecate",
|
||||
"uuid",
|
||||
"verror",
|
||||
"webdriver-js-extender",
|
||||
"webdriver-manager",
|
||||
"wordwrap",
|
||||
"wrappy",
|
||||
"xhr2",
|
||||
"xml2js",
|
||||
"xmlbuilder",
|
||||
"zone.js",
|
||||
"@angular-devkit/core",
|
||||
"@angular-devkit/schematics",
|
||||
"@types",
|
||||
"@webcomponents/custom-elements",
|
||||
],
|
||||
patterns = [
|
||||
"node_modules/protractor/**",
|
||||
"node_modules/@schematics/angular/**",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
|
134
CHANGELOG.md
134
CHANGELOG.md
@ -1,127 +1,3 @@
|
||||
<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
|
||||
|
||||
* **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))
|
||||
* **benchpress:** Fix promise chain in chrome_driver_extension. ([#23458](https://github.com/angular/angular/issues/23458)) ([d4b6c41](https://github.com/angular/angular/commit/d4b6c41))
|
||||
* **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)
|
||||
* **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)
|
||||
* **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)
|
||||
* **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:** 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)
|
||||
* **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)
|
||||
* **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:** 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:** 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:** 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:** 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)
|
||||
* **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-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:** 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:** 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:** 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:** 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))
|
||||
* **core:** add support for using async/await with Jasmine ([#24637](https://github.com/angular/angular/issues/24637)) ([71100e6](https://github.com/angular/angular/commit/71100e6))
|
||||
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([3553977](https://github.com/angular/angular/commit/3553977))
|
||||
* **core:** add support for using async/await with Jasmine ([#24637](https://github.com/angular/angular/issues/24637)) ([71100e6](https://github.com/angular/angular/commit/71100e6))
|
||||
(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)
|
||||
|
||||
@ -142,6 +18,8 @@ For example:
|
||||
|
||||
* **core:** add support for ShadowDOM v1 ([#24718](https://github.com/angular/angular/issues/24718)) ([6c55a13](https://github.com/angular/angular/commit/6c55a13))
|
||||
|
||||
|
||||
|
||||
<a name="6.0.7"></a>
|
||||
## [6.0.7](https://github.com/angular/angular/compare/6.0.6...6.0.7) (2018-06-27)
|
||||
|
||||
@ -151,6 +29,7 @@ For example:
|
||||
* **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)
|
||||
|
||||
@ -160,15 +39,22 @@ For example:
|
||||
* **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>
|
||||
## [6.0.5](https://github.com/angular/angular/compare/6.0.4...6.0.5) (2018-06-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always render end-state styles for orphaned DOM nodes ([#24236](https://github.com/angular/angular/issues/24236)) ([0139173](https://github.com/angular/angular/commit/0139173))
|
||||
* **bazel:** Allow ng_module to depend on targets w no deps ([#24446](https://github.com/angular/angular/issues/24446)) ([ea3669e](https://github.com/angular/angular/commit/ea3669e))
|
||||
* **docs-infra:** use script nomodule to load IE polyfills, skip other polyfills ([#24317](https://github.com/angular/angular/issues/24317)) ([e876535](https://github.com/angular/angular/commit/e876535)), closes [#23647](https://github.com/angular/angular/issues/23647)
|
||||
* **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)
|
||||
|
||||
|
||||
|
||||
<a name="6.0.4"></a>
|
||||
## [6.0.4](https://github.com/angular/angular/compare/6.0.3...6.0.4) (2018-06-06)
|
||||
|
||||
|
31
WORKSPACE
31
WORKSPACE
@ -13,16 +13,16 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_webtesting",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/7ffe970bbf380891754487f66c3d680c087d67f2.zip",
|
||||
strip_prefix = "rules_webtesting-7ffe970bbf380891754487f66c3d680c087d67f2",
|
||||
sha256 = "4fb0dca8c9a90547891b7ef486592775a523330fc4555c88cd8f09270055c2ce",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/v0.2.0.zip",
|
||||
strip_prefix = "rules_webtesting-0.2.0",
|
||||
sha256 = "cecc12f07e95740750a40d38e8b14b76fefa1551bef9332cb432d564d693723c",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_typescript",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.3.zip",
|
||||
strip_prefix = "rules_typescript-0.15.3",
|
||||
sha256 = "a2b26ac3fc13036011196063db1bf7f1eae81334449201dc28087ebfa3708c99",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.0.zip",
|
||||
strip_prefix = "rules_typescript-0.15.0",
|
||||
sha256 = "1aa75917330b820cb239b3c10a936a10f0a46fe215063d4492dd76dc6e1616f4",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@ -58,10 +58,10 @@ http_archive(
|
||||
# Even better, things like aspects will visit the entire graph including
|
||||
# ts_library rules in the devkit repository.
|
||||
http_archive(
|
||||
name = "angular_cli",
|
||||
url = "https://github.com/angular/angular-cli/archive/v6.1.0-rc.0.zip",
|
||||
strip_prefix = "angular-cli-6.1.0-rc.0",
|
||||
sha256 = "8cf320ea58c321e103f39087376feea502f20eaf79c61a4fdb05c7286c8684fd",
|
||||
name = "angular_devkit",
|
||||
url = "https://github.com/angular/devkit/archive/v0.3.1.zip",
|
||||
strip_prefix = "devkit-0.3.1",
|
||||
sha256 = "31d4b597fe9336650acf13df053c1c84dcbe9c29c6a833bcac3819cd3fd8cad3",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@ -77,11 +77,8 @@ http_archive(
|
||||
|
||||
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,
|
||||
)
|
||||
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")
|
||||
|
||||
@ -100,10 +97,6 @@ load("@build_bazel_rules_typescript//:defs.bzl", "ts_setup_workspace")
|
||||
|
||||
ts_setup_workspace()
|
||||
|
||||
load("//packages/bazel/src:ng_setup_workspace.bzl", "ng_setup_workspace")
|
||||
|
||||
ng_setup_workspace()
|
||||
|
||||
#
|
||||
# Point Bazel to WORKSPACEs that live in subdirectories
|
||||
#
|
||||
|
@ -5,7 +5,7 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
// #docregion directive-import
|
||||
@ -24,7 +24,7 @@ import { ItemDirective } from './item.directive';
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule
|
||||
HttpModule
|
||||
],
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
|
@ -1,7 +1,7 @@
|
||||
// #docregion
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
// import { AppRoutingModule } from './app-routing.module';
|
||||
import { LocationStrategy,
|
||||
@ -54,7 +54,7 @@ const c_components = [
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpClientModule,
|
||||
HttpModule,
|
||||
InMemoryWebApiModule.forRoot(HeroData)
|
||||
// AppRoutingModule TODO: add routes
|
||||
],
|
||||
|
@ -1,69 +0,0 @@
|
||||
'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);
|
||||
});
|
||||
});
|
||||
});
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"projectType": "elements"
|
||||
}
|
@ -1,5 +1,6 @@
|
||||
// #docregion
|
||||
import { Component, Injector } from '@angular/core';
|
||||
import { createCustomElement } from '@angular/elements';
|
||||
import { createNgElementConstructor } from '../elements-dist';
|
||||
import { PopupService } from './popup.service';
|
||||
import { PopupComponent } from './popup.component';
|
||||
|
||||
@ -7,15 +8,19 @@ import { PopupComponent } from './popup.component';
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<input #input value="Message">
|
||||
<button (click)="popup.showAsComponent(input.value)">Show as component</button>
|
||||
<button (click)="popup.showAsElement(input.value)">Show as element</button>
|
||||
`,
|
||||
<button (click)="popup.showAsComponent(input.value)">
|
||||
Show as component </button>
|
||||
<button (click)="popup.showAsElement(input.value)">
|
||||
Show as element </button>
|
||||
`
|
||||
})
|
||||
|
||||
export class AppComponent {
|
||||
constructor(injector: Injector, public popup: PopupService) {
|
||||
// Convert `PopupComponent` to a custom element.
|
||||
const PopupElement = createCustomElement(PopupComponent, {injector});
|
||||
// Register the custom element with the browser.
|
||||
customElements.define('popup-element', PopupElement);
|
||||
constructor(private injector: Injector, public popup: PopupService) {
|
||||
// on init, convert PopupComponent to a custom element
|
||||
const PopupElement =
|
||||
createNgElementConstructor(PopupComponent, {injector: this.injector});
|
||||
// register the custom element with the browser.
|
||||
customElements.define('popup-element', PopupElement);
|
||||
}
|
||||
}
|
||||
|
@ -1,21 +1,22 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
// #docregion
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { NgModule } from '@angular/core';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { PopupComponent } from './popup.component';
|
||||
import { PopupService } from './popup.service';
|
||||
import { PopupComponent } from './popup.component';
|
||||
|
||||
// Include the `PopupService` provider,
|
||||
// but exclude `PopupComponent` from compilation,
|
||||
// because it will be added dynamically.
|
||||
// include the PopupService provider,
|
||||
// but exclude PopupComponent from compilation,
|
||||
// because it will be added dynamically
|
||||
|
||||
@NgModule({
|
||||
declarations: [AppComponent, PopupComponent],
|
||||
imports: [BrowserModule, BrowserAnimationsModule],
|
||||
providers: [PopupService],
|
||||
declarations: [AppComponent, PopupComponent],
|
||||
bootstrap: [AppComponent],
|
||||
entryComponents: [PopupComponent],
|
||||
})
|
||||
export class AppModule {
|
||||
}
|
||||
|
||||
export class AppModule {}
|
||||
|
@ -1,14 +1,14 @@
|
||||
// #docregion
|
||||
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AnimationEvent } from '@angular/animations';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
|
||||
@Component({
|
||||
selector: 'my-popup',
|
||||
template: `
|
||||
<span>Popup: {{message}}</span>
|
||||
<button (click)="closed.next()">✖</button>
|
||||
`,
|
||||
template: 'Popup: {{message}}',
|
||||
host: {
|
||||
'[@state]': 'state',
|
||||
'(@state.done)': 'onAnimationDone($event)',
|
||||
},
|
||||
animations: [
|
||||
trigger('state', [
|
||||
@ -27,17 +27,13 @@ import { animate, state, style, transition, trigger } from '@angular/animations'
|
||||
height: 48px;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 1px solid black;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 50%;
|
||||
}
|
||||
`]
|
||||
})
|
||||
|
||||
export class PopupComponent {
|
||||
private state: 'opened' | 'closed' = 'closed';
|
||||
|
||||
@ -45,10 +41,18 @@ export class PopupComponent {
|
||||
set message(message: string) {
|
||||
this._message = message;
|
||||
this.state = 'opened';
|
||||
|
||||
setTimeout(() => this.state = 'closed', 2000);
|
||||
}
|
||||
get message(): string { return this._message; }
|
||||
_message: string;
|
||||
|
||||
@Output()
|
||||
closed = new EventEmitter();
|
||||
|
||||
onAnimationDone(e: AnimationEvent) {
|
||||
if (e.toState === 'closed') {
|
||||
this.closed.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,9 @@
|
||||
|
||||
// #docregion
|
||||
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()
|
||||
export class PopupService {
|
||||
@ -39,7 +40,7 @@ export class PopupService {
|
||||
// This uses the new custom-element method to add the popup to the DOM.
|
||||
showAsElement(message: string) {
|
||||
// Create element
|
||||
const popupEl: NgElement & WithProperties<PopupComponent> = document.createElement('popup-element') as any;
|
||||
const popupEl = document.createElement('popup-element');
|
||||
|
||||
// Listen to the close event
|
||||
popupEl.addEventListener('closed', () => document.body.removeChild(popupEl));
|
||||
|
@ -1,12 +0,0 @@
|
||||
<!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,5 +1,7 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
|
||||
/* App Root */
|
||||
import { AppComponent } from './app.component';
|
||||
|
@ -15,7 +15,7 @@ function sequenceSubscriber(observer) {
|
||||
if (idx === arr.length - 1) {
|
||||
observer.complete();
|
||||
} else {
|
||||
doSequence(arr, ++idx);
|
||||
doSequence(arr, idx++);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
@ -95,7 +95,7 @@ function multicastSequenceSubscriber() {
|
||||
},
|
||||
complete() {
|
||||
// Notify all complete callbacks
|
||||
observers.slice(0).forEach(obs => obs.complete());
|
||||
observers.forEach(obs => obs.complete());
|
||||
}
|
||||
}, seq, 0);
|
||||
}
|
||||
@ -121,13 +121,13 @@ function doSequence(observer, arr, idx) {
|
||||
if (idx === arr.length - 1) {
|
||||
observer.complete();
|
||||
} else {
|
||||
doSequence(observer, arr, ++idx);
|
||||
doSequence(observer, arr, idx++);
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
// 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
|
||||
multicastSequence.subscribe({
|
||||
|
3
aio/content/examples/reactive-forms/debug.log
Normal file
3
aio/content/examples/reactive-forms/debug.log
Normal file
@ -0,0 +1,3 @@
|
||||
[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
24
aio/content/examples/reactive-forms/final.stackblitz.json
Normal file
24
aio/content/examples/reactive-forms/final.stackblitz.json
Normal file
@ -0,0 +1,24 @@
|
||||
{
|
||||
"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,10 +1,4 @@
|
||||
<!-- #docplaster -->
|
||||
<h1>Reactive Forms</h1>
|
||||
|
||||
<!-- #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 -->
|
||||
<div class="container">
|
||||
<h1>Reactive Forms</h1>
|
||||
<app-hero-detail></app-hero-detail>
|
||||
</div>
|
||||
|
@ -1,17 +1,4 @@
|
||||
<!-- #docplaster -->
|
||||
<!-- #docregion app-name-editor -->
|
||||
<h1>Reactive Forms</h1>
|
||||
|
||||
<!-- #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 -->
|
||||
<div class="container">
|
||||
<h1>Reactive Forms</h1>
|
||||
<app-hero-list></app-hero-list>
|
||||
</div>
|
||||
|
@ -1,27 +0,0 @@
|
||||
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,24 +1,9 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
export type EditorType = 'name' | 'profile';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
editor: EditorType = 'name';
|
||||
|
||||
get showNameEditor() {
|
||||
return this.editor === 'name';
|
||||
}
|
||||
|
||||
get showProfileEditor() {
|
||||
return this.editor === 'profile';
|
||||
}
|
||||
|
||||
toggleEditor(type: EditorType) {
|
||||
this.editor = type;
|
||||
}
|
||||
}
|
||||
export class AppComponent { }
|
||||
|
@ -1,34 +1,45 @@
|
||||
// #docplaster
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
// #docregion imports
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
// #docregion
|
||||
// #docregion v1
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ReactiveFormsModule } from '@angular/forms'; // <-- #1 import module
|
||||
|
||||
// #enddocregion imports
|
||||
import { AppComponent } from './app.component';
|
||||
import { NameEditorComponent } from './name-editor/name-editor.component';
|
||||
import { ProfileEditorComponent } from './profile-editor/profile-editor.component';
|
||||
import { AppComponent } from './app.component';
|
||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||
// #enddocregion v1
|
||||
// #docregion hero-service-list
|
||||
// add JavaScript imports
|
||||
import { HeroListComponent } from './hero-list/hero-list.component';
|
||||
import { HeroService } from './hero.service';
|
||||
// #docregion v1
|
||||
|
||||
// #docregion imports
|
||||
@NgModule({
|
||||
// #enddocregion imports
|
||||
declarations: [
|
||||
AppComponent,
|
||||
NameEditorComponent,
|
||||
ProfileEditorComponent
|
||||
HeroDetailComponent,
|
||||
// #enddocregion v1
|
||||
HeroListComponent // <--declare HeroListComponent
|
||||
// #docregion v1
|
||||
],
|
||||
// #docregion imports
|
||||
// #enddocregion hero-service-list
|
||||
imports: [
|
||||
// #enddocregion imports
|
||||
BrowserModule,
|
||||
// #docregion imports
|
||||
// other imports ...
|
||||
ReactiveFormsModule
|
||||
ReactiveFormsModule // <-- #2 add to @NgModule imports
|
||||
],
|
||||
// #enddocregion imports
|
||||
providers: [],
|
||||
bootstrap: [AppComponent]
|
||||
// #docregion imports
|
||||
// #enddocregion v1
|
||||
// export for the DemoModule
|
||||
// #docregion hero-service-list
|
||||
// ...
|
||||
exports: [
|
||||
AppComponent,
|
||||
HeroDetailComponent,
|
||||
HeroListComponent // <-- export HeroListComponent
|
||||
],
|
||||
providers: [ HeroService ], // <-- provide HeroService
|
||||
// #enddocregion hero-service-list
|
||||
// #docregion v1
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
// #enddocregion imports
|
||||
// #enddocregion v1
|
||||
|
40
aio/content/examples/reactive-forms/src/app/data-model.ts
Normal file
40
aio/content/examples/reactive-forms/src/app/data-model.ts
Normal file
@ -0,0 +1,40 @@
|
||||
// #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'];
|
@ -0,0 +1,40 @@
|
||||
<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>
|
@ -0,0 +1,49 @@
|
||||
/* 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();
|
||||
}
|
||||
}
|
33
aio/content/examples/reactive-forms/src/app/demo.module.ts
Normal file
33
aio/content/examples/reactive-forms/src/app/demo.module.ts
Normal file
@ -0,0 +1,33 @@
|
||||
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 { }
|
@ -0,0 +1,8 @@
|
||||
<!-- #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-->
|
||||
|
@ -0,0 +1,15 @@
|
||||
/* 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();
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
<!-- #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 -->
|
||||
|
||||
|
||||
|
@ -0,0 +1,17 @@
|
||||
/* 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
|
@ -0,0 +1,16 @@
|
||||
<!-- #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 -->
|
@ -0,0 +1,27 @@
|
||||
/* 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
|
@ -0,0 +1,25 @@
|
||||
/* 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
|
@ -0,0 +1,46 @@
|
||||
<!-- #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>
|
@ -0,0 +1,34 @@
|
||||
/* 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
|
@ -0,0 +1,56 @@
|
||||
|
||||
<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 -->
|
@ -0,0 +1,35 @@
|
||||
/* 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
|
||||
|
@ -0,0 +1,46 @@
|
||||
<!-- #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>
|
@ -0,0 +1,66 @@
|
||||
/* 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
|
@ -0,0 +1,46 @@
|
||||
<!-- #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>
|
@ -0,0 +1,68 @@
|
||||
/* 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
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<!-- #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>
|
@ -0,0 +1,74 @@
|
||||
/* 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
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
<!-- #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 -->
|
@ -0,0 +1,113 @@
|
||||
// #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
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<!-- #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>
|
@ -0,0 +1,17 @@
|
||||
<!-- #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>
|
@ -0,0 +1,32 @@
|
||||
// #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; }
|
||||
}
|
25
aio/content/examples/reactive-forms/src/app/hero.service.ts
Normal file
25
aio/content/examples/reactive-forms/src/app/hero.service.ts
Normal file
@ -0,0 +1,25 @@
|
||||
// #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
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
: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;
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
<!-- #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 -->
|
@ -1,22 +0,0 @@
|
||||
// #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
|
@ -1,67 +0,0 @@
|
||||
<!-- #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 -->
|
@ -1,40 +0,0 @@
|
||||
// #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
|
@ -1,58 +0,0 @@
|
||||
// #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
|
@ -1,39 +0,0 @@
|
||||
/* 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;
|
||||
}
|
@ -1,80 +0,0 @@
|
||||
<!-- #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 -->
|
@ -1,73 +0,0 @@
|
||||
// #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
|
17
aio/content/examples/reactive-forms/src/index-final.html
Normal file
17
aio/content/examples/reactive-forms/src/index-final.html
Normal file
@ -0,0 +1,17 @@
|
||||
<!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,9 +2,10 @@
|
||||
<!-- #docregion -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Angular Reactive Forms</title>
|
||||
<title>Hero Form</title>
|
||||
<meta charset="UTF-8">
|
||||
<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>
|
||||
|
||||
<body>
|
||||
|
@ -1,3 +1,4 @@
|
||||
// tslint:disable:no-unused-variable
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
@ -9,3 +10,4 @@ if (environment.production) {
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
@ -2,11 +2,12 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { AppModule } from './app/app.module'; // just the final version
|
||||
import { DemoModule } from './app/demo.module'; // demo picker
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
platformBrowserDynamic().bootstrapModule(DemoModule);
|
||||
|
1
aio/content/examples/reactive-forms/src/styles.1.css
Normal file
1
aio/content/examples/reactive-forms/src/styles.1.css
Normal file
@ -0,0 +1 @@
|
||||
@import url('https://unpkg.com/bootstrap@3.3.7/dist/css/bootstrap.min.css');
|
@ -3,7 +3,6 @@
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[0-9].*",
|
||||
|
||||
"!src/app/app.component.1.ts",
|
||||
"!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.
|
||||
const squareOddVals = pipe(
|
||||
filter((n: number) => n % 2 !== 0),
|
||||
filter(n => n % 2),
|
||||
map(n => n * n)
|
||||
);
|
||||
|
||||
|
@ -9,6 +9,6 @@ import { HeroService } from './heroes';
|
||||
<toh-heroes></toh-heroes>
|
||||
`,
|
||||
styleUrls: ['./app.component.css'],
|
||||
providers: [HeroService]
|
||||
providers: [ HeroService ]
|
||||
})
|
||||
export class AppComponent {}
|
||||
export class AppComponent { }
|
||||
|
@ -1,8 +1,9 @@
|
||||
// #docregion
|
||||
/* avoid */
|
||||
import { Component, NgModule, OnInit } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule, Component, OnInit } from '@angular/core';
|
||||
|
||||
class Hero {
|
||||
id: number;
|
||||
@ -23,24 +24,24 @@ class AppComponent implements OnInit {
|
||||
heroes: Hero[] = [];
|
||||
|
||||
ngOnInit() {
|
||||
getHeroes().then(heroes => (this.heroes = heroes));
|
||||
getHeroes().then(heroes => this.heroes = heroes);
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule],
|
||||
declarations: [AppComponent],
|
||||
exports: [AppComponent],
|
||||
bootstrap: [AppComponent]
|
||||
imports: [ BrowserModule ],
|
||||
declarations: [ AppComponent ],
|
||||
exports: [ AppComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule {}
|
||||
export class AppModule { }
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
||||
const HEROES: Hero[] = [
|
||||
{ id: 1, name: 'Bombasto' },
|
||||
{ id: 2, name: 'Tornado' },
|
||||
{ id: 3, name: 'Magneta' }
|
||||
{id: 1, name: 'Bombasto'},
|
||||
{id: 2, name: 'Tornado'},
|
||||
{id: 3, name: 'Magneta'},
|
||||
];
|
||||
|
||||
function getHeroes(): Promise<Hero[]> {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import { Hero } from './hero.model';
|
||||
|
||||
export const HEROES: Hero[] = [
|
||||
{ id: 1, name: 'Bombasto' },
|
||||
{ id: 2, name: 'Tornado' },
|
||||
{ id: 3, name: 'Magneta' }
|
||||
{id: 1, name: 'Bombasto'},
|
||||
{id: 2, name: 'Tornado'},
|
||||
{id: 3, name: 'Magneta'},
|
||||
];
|
||||
|
@ -1,6 +1,6 @@
|
||||
// #docregion
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
@ -3,8 +3,9 @@
|
||||
/* avoid */
|
||||
|
||||
import { ExceptionService, SpinnerService, ToastService } from '../../core';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Http } from '@angular/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { Hero } from './hero.model';
|
||||
// #enddocregion example
|
||||
|
||||
@ -15,15 +16,18 @@ export class HeroService {
|
||||
private exceptionService: ExceptionService,
|
||||
private spinnerService: SpinnerService,
|
||||
private toastService: ToastService,
|
||||
private http: HttpClient
|
||||
private http: Http
|
||||
) { }
|
||||
|
||||
getHero(id: number) {
|
||||
return this.http.get<Hero>(`api/heroes/${id}`);
|
||||
return this.http.get(`api/heroes/${id}`).pipe(
|
||||
map(response => response.json().data as Hero));
|
||||
}
|
||||
|
||||
getHeroes() {
|
||||
return this.http.get<Hero[]>(`api/heroes`);
|
||||
return this.http.get(`api/heroes`).pipe(
|
||||
map(response => response.json().data as Hero[]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,11 +1,11 @@
|
||||
// #docregion
|
||||
// #docregion example
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
import { ExceptionService, SpinnerService, ToastService } from '../../core';
|
||||
import { Hero } from './hero.model';
|
||||
|
||||
import { ExceptionService, SpinnerService, ToastService } from '../../core';
|
||||
// #enddocregion example
|
||||
|
||||
@Injectable()
|
||||
@ -16,15 +16,18 @@ export class HeroService {
|
||||
private exceptionService: ExceptionService,
|
||||
private spinnerService: SpinnerService,
|
||||
private toastService: ToastService,
|
||||
private http: HttpClient
|
||||
private http: Http
|
||||
) { }
|
||||
|
||||
getHero(id: number) {
|
||||
return this.http.get<Hero>(`api/heroes/${id}`);
|
||||
return this.http.get(`api/heroes/${id}`).pipe(
|
||||
map(response => response.json() as Hero));
|
||||
}
|
||||
|
||||
getHeroes() {
|
||||
return this.http.get<Hero[]>(`api/heroes`);
|
||||
return this.http.get(`api/heroes`).pipe(
|
||||
map(response => response.json() as Hero[]));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ code uses [AnimationBuilder](/api/animations/AnimationBuilder). If your code doe
|
||||
uncomment the `web-animations-js` polyfill from the `polyfills.ts` file generated by Angular CLI.
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The examples in this page are available as a <live-example></live-example>.
|
||||
|
||||
@ -183,7 +183,7 @@ transition definitions, and not in a separate `state(void)` definition. Thus, th
|
||||
are different on enter and leave: the element enters from the left
|
||||
and leaves to the right.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
These two common animations have their own aliases:
|
||||
|
||||
|
@ -4,7 +4,7 @@ The Angular Ahead-of-Time (AOT) compiler converts your Angular HTML and TypeScri
|
||||
|
||||
This guide explains how to build with the AOT compiler using different compiler options and how to write Angular metadata that AOT can compile.
|
||||
|
||||
<div class="alert is-helpful>
|
||||
<div class="l-sub-section">
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=kW9cJsvcsGo">Watch compiler author Tobias Bosch explain the Angular Compiler</a> at AngularConnect 2016.
|
||||
|
||||
@ -39,7 +39,7 @@ For AOT compilation, append the `--aot` flags to the _build-only_ or the _build-
|
||||
ng serve --aot
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `--prod` meta-flag compiles with AOT by default.
|
||||
|
||||
@ -297,7 +297,7 @@ At the same time, the AOT **_collector_** analyzes the metadata recorded in the
|
||||
|
||||
You can think of `.metadata.json` as a diagram of the overall structure of a decorator's metadata, represented as an [abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/compiler-cli/src/metadata/schema.ts)
|
||||
describes the JSON format as a collection of TypeScript interfaces.
|
||||
@ -333,7 +333,7 @@ Parentheses | `(a + b)`
|
||||
If an expression uses unsupported syntax, the _collector_ writes an error node to the `.metadata.json` file. The compiler later reports the error if it needs that
|
||||
piece of metadata to generate the application code.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you want `ngc` to report syntax errors immediately rather than produce a `.metadata.json` file with errors, set the `strictMetadataEmit` option in `tsconfig`.
|
||||
|
||||
|
@ -11,7 +11,7 @@ A _component_ controls a patch of screen called a *view*. For example, individua
|
||||
You define a component's application logic—what it does to support the view—inside a class.
|
||||
The class interacts with the view through an API of properties and methods.
|
||||
|
||||
For example, the `HeroListComponent` has a `heroes` property that holds an array of heroes. It also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. The component acquires the heroes from a service, which is a TypeScript [parameter property](http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system.
|
||||
For example, the `HeroListComponent` has a `heroes` property that holds an array of heroes. It also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list. The component acquires the heroes from a service, which is a TypeScript [parameter property[(http://www.typescriptlang.org/docs/handbook/classes.html#parameter-properties) on the constructor. The service is provided to the component through the dependency injection system.
|
||||
|
||||
<code-example path="architecture/src/app/hero-list.component.ts" linenums="false" title="src/app/hero-list.component.ts (class)" region="class"></code-example>
|
||||
|
||||
@ -40,7 +40,7 @@ Angular inserts an instance of the `HeroListComponent` view between those tags.
|
||||
|
||||
* `templateUrl`: The module-relative address of this component's HTML template. Alternatively, you can provide the HTML template inline, as the value of the `template` property. This template defines the component's _host view_.
|
||||
|
||||
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular how to provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
* `providers`: An array of **dependency injection providers** for services that the component requires. In the example, this tells Angular how provide the `HeroService` instance that the component's constructor uses to get the list of heroes to display.
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -177,4 +177,4 @@ or modify aspects of DOM elements and components
|
||||
|
||||
You can also write your own directives. Components such as `HeroListComponent` are one kind of custom directive. You can also create custom structural and attribute directives.
|
||||
|
||||
<!-- PENDING: link to where to learn more about other kinds! -->
|
||||
<!-- PENDING: link to where to learn more about other kinds! -->
|
@ -26,7 +26,7 @@ Here's a simple root NgModule definition:
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="module" title="src/app/app.module.ts" linenums="false"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `export` of `AppComponent` is just to show how to export; it isn't actually necessary in this example. A root NgModule has no reason to _export_ anything because other modules don't need to _import_ the root NgModule.
|
||||
|
||||
@ -56,7 +56,7 @@ A component and its template together define a _view_. A component can contain a
|
||||
|
||||
When you create a component, it is associated directly with a single view, called the _host view_. The host view can be the root of a view hierarchy, which can contain _embedded views_, which are in turn the host views of other components. Those components can be in the same NgModule, or can be imported from other NgModules. Views in the tree can be nested to any depth.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
The hierarchical structure of views is a key factor in the way Angular detects and responds to changes in the DOM and app data.
|
||||
</div>
|
||||
|
||||
@ -72,7 +72,7 @@ Other JavaScript modules use *import statements* to access public objects from o
|
||||
|
||||
<code-example path="architecture/src/app/app.module.ts" region="export" linenums="false"></code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
<a href="http://exploringjs.com/es6/ch_modules.html">Learn more about the JavaScript module system on the web.</a>
|
||||
</div>
|
||||
|
||||
@ -99,7 +99,7 @@ In the example of the simple root module above, the application module needs mat
|
||||
|
||||
In this way you're using both the Angular and JavaScript module systems _together_. Although it's easy to confuse the two systems, which share the common vocabulary of "imports" and "exports", you will become familiar with the different contexts in which they are used.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Learn more from the [NgModules](guide/ngmodules) page.
|
||||
|
||||
|
@ -27,7 +27,7 @@ Like JavaScript modules, NgModules can import functionality from other NgModules
|
||||
|
||||
Organizing your code into distinct functional modules helps in managing development of complex applications, and in designing for reusability. In addition, this technique lets you take advantage of _lazy-loading_—that is, loading modules on demand—in order to minimize the amount of code that needs to be loaded at startup.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
For a more detailed discussion, see [Introduction to modules](guide/architecture-modules).
|
||||
|
||||
@ -39,7 +39,7 @@ Every Angular application has at least one component, the *root component* that
|
||||
|
||||
The `@Component` decorator identifies the class immediately below it as a component, and provides the template and related component-specific metadata.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Decorators are functions that modify JavaScript classes. Angular defines a number of such decorators that attach specific kinds of metadata to classes, so that it knows what those classes mean and how they should work.
|
||||
|
||||
@ -59,7 +59,7 @@ Before a view is displayed, Angular evaluates the directives and resolves the bi
|
||||
|
||||
Your templates can also use *pipes* to improve the user experience by transforming values for display. Use pipes to display, for example, dates and currency values in a way appropriate to the user's locale. Angular provides predefined pipes for common transformations, and you can also define your own.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
For a more detailed discussion of these concepts, see [Introduction to components](guide/architecture-components).
|
||||
|
||||
@ -74,7 +74,7 @@ For data or logic that is not associated with a specific view, and that you want
|
||||
|
||||
*Dependency injection* (or DI) lets you keep your component classes lean and efficient. They don't fetch data from the server, validate user input, or log directly to the console; they delegate such tasks to services.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
For a more detailed discusssion, see [Introduction to services and DI](guide/architecture-services).
|
||||
|
||||
@ -96,7 +96,7 @@ The router interprets a link URL according to your app's view navigation rules a
|
||||
|
||||
To define navigation rules, you associate *navigation paths* with your components. A path uses a URL-like syntax that integrates your program data, in much the same way that template syntax integrates your views with your program data. You can then apply program logic to choose which views to show or to hide, in response to user input and your own access rules.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
For a more detailed discussion, see [Routing and navigation](guide/router).
|
||||
|
||||
@ -128,7 +128,7 @@ Each of these subjects is introduced in more detail in the following pages.
|
||||
* [Pipes](guide/architecture-components#pipes)
|
||||
* [Services and dependency injection](guide/architecture-services)
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Note that the code referenced on these pages is available as a <live-example></live-example>.
|
||||
</div>
|
||||
|
@ -51,7 +51,7 @@ ng generate directive highlight
|
||||
|
||||
The CLI creates `src/app/highlight.directive.ts`, a corresponding test file (`.../spec.ts`, and _declares_ the directive class in the root `AppModule`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
_Directives_ must be declared in [Angular Modules](guide/ngmodules) in the same manner as _components_.
|
||||
|
||||
@ -71,7 +71,7 @@ Angular locates each element in the template that has an attribute named `appHig
|
||||
|
||||
The _attribute selector_ pattern explains the name of this kind of directive.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
#### Why not "highlight"?
|
||||
|
||||
@ -146,7 +146,7 @@ each adorned by the `HostListener` decorator.
|
||||
The `@HostListener` decorator lets you subscribe to events of the DOM
|
||||
element that hosts an attribute directive, the `<p>` in this case.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Of course you could reach into the DOM with standard JavaScript and attach event listeners manually.
|
||||
There are at least three problems with _that_ approach:
|
||||
|
@ -95,7 +95,7 @@ Angular supports most recent browsers. This includes the following specific vers
|
||||
|
||||
</table>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Angular's continuous integration process runs unit tests of the framework on all of these browsers for every pull request,
|
||||
using <a href="https://saucelabs.com/">SauceLabs</a> and
|
||||
@ -154,7 +154,7 @@ add it yourself, following the same pattern:
|
||||
1. install the npm package
|
||||
1. `import` the file in `polyfills.ts`
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Non-CLI users should follow the instructions [below](#non-cli).
|
||||
</div>
|
||||
|
@ -119,7 +119,7 @@ E2E tests of input property setter with empty and non-empty names:
|
||||
|
||||
Detect and act upon changes to input property values with the `ngOnChanges()` method of the `OnChanges` lifecycle hook interface.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -311,7 +311,7 @@ The following example illustrates this technique with the same
|
||||
Neither its appearance nor its behavior will change.
|
||||
The child [CountdownTimerComponent](guide/component-interaction#countdown-timer-example) is the same as well.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -400,7 +400,7 @@ Each `AstronautComponent` is a child of the `MissionControlComponent` and theref
|
||||
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
|
@ -187,7 +187,7 @@ They are _not inherited_ by any components nested within the template nor by any
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can specify more than one styles file or even a combination of `styles` and `styleUrls`.
|
||||
|
||||
@ -216,9 +216,10 @@ You can also write `<link>` tags into the component's HTML template.
|
||||
|
||||
<div class="alert is-critical">
|
||||
|
||||
When building with the CLI, be sure to include the linked style file among the assets to be copied to the server as described in the [CLI documentation](https://github.com/angular/angular-cli/wiki/stories-asset-configuration).
|
||||
The link tag's `href` URL must be relative to the
|
||||
_**application root**_, not relative to the component file.
|
||||
|
||||
Once included, the CLI will include the stylesheet, whether the link tag's href URL is relative to the application root or the component file.
|
||||
When building with the CLI, be sure to include the linked style file among the assets to be copied to the server as described in the [CLI documentation](https://github.com/angular/angular-cli/wiki/stories-asset-configuration).
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -25,7 +25,7 @@ application and don't need to be listed in any module.
|
||||
Service classes can act as their own providers which is why defining them in the `@Injectable` decorator
|
||||
is all the registration you need.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -167,7 +167,7 @@ that is visible only to the component and its children, if any.
|
||||
You could also provide the `HeroService` to a *different* component elsewhere in the application.
|
||||
That would result in a *different* instance of the service, living in a *different* injector.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -433,7 +433,7 @@ It may already have that value in its internal container.
|
||||
If it doesn't, it may be able to make one with the help of a ***provider***.
|
||||
A *provider* is a recipe for delivering a service associated with a *token*.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -577,7 +577,7 @@ The second provider substitutes the `DateLoggerService` for the `LoggerService`.
|
||||
The `LoggerService` is already registered at the `AppComponent` level.
|
||||
When _this component_ requests the `LoggerService`, it receives the `DateLoggerService` instead.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -645,7 +645,7 @@ The `HeroOfTheMonthComponent` constructor's `logger` parameter is typed as `Mini
|
||||
Behind the scenes, Angular actually sets the `logger` parameter to the full service registered under the `LoggingService` token which happens to be the `DateLoggerService` that was [provided above](guide/dependency-injection-in-action#useclass).
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -707,7 +707,7 @@ After some undisclosed work, the function returns the string of names
|
||||
and Angular injects it into the `runnersUp` parameter of the `HeroOfTheMonthComponent`.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -769,7 +769,7 @@ A ***class-interface*** should define *only* the members that its consumers are
|
||||
Such a narrowing interface helps decouple the concrete class from its consumers.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -866,7 +866,7 @@ and displays them in the order they arrive from the database.
|
||||
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -982,7 +982,7 @@ If you're lucky, they all implement the same base class
|
||||
whose API your `NewsComponent` understands.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -1165,7 +1165,7 @@ its class signature doesn't mention `Parent`:
|
||||
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
|
@ -81,7 +81,7 @@ now in the constructor.
|
||||
The `Car` class no longer creates an `engine` or `tires`.
|
||||
It just consumes them.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
This example leverages TypeScript's constructor syntax for declaring
|
||||
parameters and properties simultaneously.
|
||||
@ -101,7 +101,7 @@ conform to the general API requirements of an `engine` or `tires`.
|
||||
|
||||
Now, if someone extends the `Engine` class, that is not `Car`'s problem.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The _consumer_ of `Car` has the problem. The consumer must update the car creation code to
|
||||
something like this:
|
||||
|
@ -98,7 +98,7 @@ Without a provider, the injector would not know
|
||||
that it is responsible for injecting the service
|
||||
nor be able to create the service.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You'll learn much more about _providers_ [below](#providers).
|
||||
For now, it is sufficient to know that they configure where and how services are created.
|
||||
@ -141,7 +141,7 @@ The second registers a value (`HERO_DI_CONFIG`) under the `APP_CONFIG` _injectio
|
||||
With the above registrations, Angular can inject the `UserService` or the `HERO_DI_CONFIG` value
|
||||
into any class that it creates.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You'll learn about _injection tokens_ and _provider_ syntax [below](#providers).
|
||||
</div>
|
||||
@ -174,7 +174,7 @@ You're likely to inject the `UserService` in many places throughout the app
|
||||
and will want to inject the same service instance every time.
|
||||
Providing the `UserService` with an Angular module is a good choice if an `@Injectable` provider is not an option..
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
To be precise, Angular module providers are registered with the root injector
|
||||
_unless the module is_ [lazy loaded](guide/lazy-loading-ngmodules).
|
||||
@ -199,7 +199,7 @@ and is never destroyed so the `HeroService` created for the `HeroComponent` also
|
||||
If you want to restrict `HeroService` access to the `HeroComponent` and its nested `HeroListComponent`,
|
||||
providing the `HeroService` in the `HeroComponent` may be a good choice.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The scope and lifetime of component-provided services is a consequence of [the way Angular creates component instances](#component-child-injectors).
|
||||
|
||||
@ -375,7 +375,7 @@ and let the injector pass them along to the factory function:
|
||||
<code-example path="dependency-injection/src/app/heroes/hero.service.provider.ts" region="provider" title="src/app/heroes/hero.service.provider.ts (excerpt)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `useFactory` field tells Angular that the provider is a factory function
|
||||
whose implementation is the `heroServiceFactory`.
|
||||
@ -440,7 +440,7 @@ The service can be instantiated by configuring a factory function as shown below
|
||||
|
||||
<code-example path="dependency-injection/src/app/tree-shaking/service.0.ts" title="src/app/tree-shaking/service.0.ts" linenums="false"> </code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
To override tree-shakable providers, register the provider using the `providers: []` array syntax of any Angular decorator that supports it.
|
||||
|
||||
@ -532,7 +532,7 @@ under test:
|
||||
<code-example path="dependency-injection/src/app/test.component.ts" region="spec" title="src/app/test.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Learn more in the [Testing](guide/testing) guide.
|
||||
|
||||
@ -636,7 +636,7 @@ But what should you use as the token?
|
||||
You don't have a class to serve as a token.
|
||||
There is no `AppConfig` class.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
### TypeScript interfaces aren't valid tokens
|
||||
|
||||
@ -742,7 +742,7 @@ You can call `get()` with a second parameter, which is the value to return if th
|
||||
is not found. Angular can't find the service if it's not registered with this or any ancestor injector.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -776,7 +776,7 @@ If you define the component before the service,
|
||||
you'll get a runtime null reference error.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You actually can define the component first with the help of the `forwardRef()` method as explained
|
||||
in this [blog post](http://blog.thoughtram.io/angular/2015/09/03/forward-references-in-angular-2.html).
|
||||
|
@ -187,7 +187,7 @@ For example, given the `<base href="/my/app/">`, the browser resolves a URL such
|
||||
into a server request for `my/app/some/place/foo.jpg`.
|
||||
During navigation, the Angular router uses the _base href_ as the base path to component, template, and module files.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
See also the [*APP_BASE_HREF*](api/common/APP_BASE_HREF "API: APP_BASE_HREF") alternative.
|
||||
|
||||
@ -215,7 +215,7 @@ The `ng build` command writes generated build artifacts to the output folder.
|
||||
The `ng serve` command does not.
|
||||
It serves build artifacts from memory instead for a faster development experience.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The output folder is `dist/` by default.
|
||||
To output to a different folder, change the `outputPath` in `angular.json`.
|
||||
|
@ -13,7 +13,7 @@ The final UI looks like this:
|
||||
<img src="generated/images/guide/displaying-data/final.png" alt="Final UI">
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -60,7 +60,7 @@ interpolation:
|
||||
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -79,7 +79,7 @@ inserts those values into the browser. Angular updates the display
|
||||
when these properties change.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -213,7 +213,7 @@ to the item (the hero) in the current iteration. Angular uses that variable as t
|
||||
context for the interpolation in the double curly braces.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
|
@ -47,7 +47,7 @@ The reader requests a page by its Page URL. The doc viewer fetches the correspon
|
||||
Page URLs mirror the `content` file structure. The URL for the page of a guide is in the form `guide/{page-name}`. The page for _this_ "Authors Style Guide" is located at `content/guide/docs-style-guide.md` and its URL is `guide/docs-style-guide`.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
_Tutorial_ pages are exactly like guide pages. The only difference is that they reside in `content/tutorial` instead of `content/guide` and have URLs like `tutorial/{page-name}`.
|
||||
|
||||
@ -84,7 +84,7 @@ Standard markdown processors don't allow you to put markdown _within_ HTML tags.
|
||||
</div>
|
||||
```
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
It is customary but not required to _precede_ the _closing HTML_ tag with a blank line as well.
|
||||
|
||||
@ -162,6 +162,34 @@ Try to minimize the heading depth, preferably only two. But more headings, such
|
||||
Try to minimize ...
|
||||
```
|
||||
|
||||
## Subsections
|
||||
|
||||
Subsections typically present extra detail and references to other pages.
|
||||
|
||||
Use subsections for commentary that _enriches_ the reader's understanding of the text that precedes it.
|
||||
|
||||
A subsection _must not_ contain anything _essential_ to that understanding. Don't put a critical instruction or a tutorial step in a subsection.
|
||||
|
||||
A subsection is content within a `<div>` that has the `l-sub-section` CSS class. You should write the subsection content in markdown.
|
||||
|
||||
Here is an example of a subsection `<div>` surrounding the subsection content written in markdown.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
You'll learn about styles for live examples in the [section below](guide/docs-style-guide#live-examples "Live examples").
|
||||
|
||||
</div>
|
||||
|
||||
```html
|
||||
<div class="l-sub-section">
|
||||
|
||||
You'll learn about styles for live examples in the [section below](guide/docs-style-guide#live-examples "Live examples").
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
Note that at least one blank line must follow the opening `<div>`. A blank line before the closing `</div>` is customary but not required.
|
||||
|
||||
## Table of contents
|
||||
|
||||
Most pages display a table of contents (TOC). The TOC appears in the right panel when the viewport is wide. When narrow, the TOC appears in an expandable/collapsible region near the top of the page.
|
||||
@ -294,7 +322,7 @@ The author must also write end-to-end tests for the sample.
|
||||
|
||||
Code samples are located in sub-folders of the `content/examples` directory of the `angular/angular` repository. An example folder name should be the same as the guide page it supports.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
A guide page might not have its own sample code. It might refer instead to a sample belonging to another page.
|
||||
|
||||
@ -327,7 +355,7 @@ In this example, that path is `docs-style-guide/src/app/app.module.ts`.
|
||||
You added a header to tell the reader where to find the file by setting the `title` attribute.
|
||||
Following convention, you set the `title` attribute to the file's location within the sample's root folder.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Unless otherwise noted, all code snippets in this page are derived from sample source code
|
||||
located in the `content/examples/docs-style-guide` directory.
|
||||
@ -495,7 +523,7 @@ The `linenums` attribute in the second pane restores line numbering for _itself
|
||||
|
||||
You must add special code snippet markup to sample source code files before they can be displayed by `<code-example>` and `<code-tabs>` components.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The sample source code for this page, located in `context/examples/docs-style-guide`, contains examples of every code snippet markup described in this section.
|
||||
|
||||
@ -542,7 +570,7 @@ The `<code-example>` and `<code-tabs>` components won't display a source code fi
|
||||
The _#docregion_ comment begins a code snippet region.
|
||||
Every line of code _after_ that comment belongs in the region _until_ the code fragment processor encounters the end of the file or a closing _#enddocregion_.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `src/main.ts` is a simple example of a file with a single _#docregion_ at the top of the file.
|
||||
|
||||
@ -585,7 +613,7 @@ You can nest _#docregions_ within _#docregions_
|
||||
... yet more code ...
|
||||
/// #enddocregion
|
||||
```
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `src/app/app.module.ts` file has a good example of a nested region.
|
||||
|
||||
@ -698,7 +726,7 @@ By adding `<live-example>` to the page you generate links that run sample code i
|
||||
|
||||
Live examples (AKA "stackblitz") are defined by one or more `stackblitz.json` files in the root of a code sample folder. Each sample folder usually has a single unnamed definition file, the default `stackblitz.json`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can create additional, named definition files in the form `name.stackblitz.json`. See `content/examples/testing` for examples.
|
||||
|
||||
@ -798,14 +826,14 @@ Here's an embedded live example for this guide.
|
||||
|
||||
Every section header tag is also an anchor point. Another guide page could add a link to this section by writing:
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
See the ["Anchors"](guide/docs-style-guide#anchors "Style Guide - Anchors") section for details.
|
||||
|
||||
</div>
|
||||
|
||||
```html
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
See the ["Anchors"](guide/docs-style-guide#anchors "Style Guide - Anchors") section for details.
|
||||
|
||||
@ -847,7 +875,7 @@ Now [link to that custom anchor name](#ugly-anchors) as you did before.
|
||||
Now [link to that custom anchor name](#ugly-anchors) as you did before.
|
||||
```
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Alternatively, you can use the HTML `<a>` tag.
|
||||
|
||||
@ -861,81 +889,44 @@ If you do, be sure to set the `id` attribute - not the `name` attribute! The doc
|
||||
|
||||
</div>
|
||||
|
||||
## Alerts and Calllouts
|
||||
## Alerts
|
||||
|
||||
Alerts and callouts present warnings, extra detail or references to other pages. They can also be used to provide commentary that _enriches_ the reader's understanding of the content being presented.
|
||||
|
||||
An alert or callout _must not_ contain anything _essential_ to that understanding. Don't put a critical instruction or a tutorial step in a subsection.
|
||||
|
||||
### Alerts
|
||||
|
||||
Alerts draw attention to short important points. Alerts should not be used for multi-line content (use [callouts](#callouts "callouts") instead).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You'll learn about styles for live examples in the [section below](guide/docs-style-guide#live-examples "Live examples").
|
||||
|
||||
</div>
|
||||
|
||||
Note that at least one blank line must follow both the opening and closing `<div>` tags. A blank line before the closing `</div>` is customary but not required.
|
||||
|
||||
```html
|
||||
<div class="alert is-helpful">
|
||||
|
||||
You'll learn about styles for live examples in the [section below](guide/docs-style-guide#live-examples "Live examples").
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
There are three different _urgency levels_ used to style the alerts based on the severity or importance of the content.
|
||||
Alerts draw attention to important points. Alerts should not be used for multi-line content (use callouts insteads) or stacked on top of each other. Note that the content of an alert is indented to the right by two spaces.
|
||||
|
||||
<div class="alert is-critical">
|
||||
|
||||
A critical alert.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
An important alert.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
A helpful, informational alert.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
Here is the markup for these alerts.
|
||||
```html
|
||||
<div class="alert is-critical">
|
||||
|
||||
A critical alert.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
An important alert.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
A helpful, informational alert.
|
||||
|
||||
</div>
|
||||
```
|
||||
|
||||
### Callouts
|
||||
Alerts are meant to grab the user's attention and should be used sparingly.
|
||||
They are not for casual asides or commentary. Use [subsections](#subsections "subsections") for commentary.
|
||||
|
||||
Callouts, like alerts, are meant to draw attention to important points. Use a callout when you want a riveting header and multi-line content.
|
||||
## Callouts
|
||||
|
||||
If you have more than two paragraphs, consider creating a new page or making it part of the main content.
|
||||
|
||||
Callouts use the same _urgency levels_ that alerts do.
|
||||
Callouts (like alerts) are meant to draw attention to important points. Use a callout when you want a riveting header and multi-line content.
|
||||
|
||||
<div class="callout is-critical">
|
||||
<header>A critical point</header>
|
||||
@ -952,7 +943,7 @@ Callouts use the same _urgency levels_ that alerts do.
|
||||
</div>
|
||||
|
||||
<div class="callout is-helpful">
|
||||
<header>A helpful or informational point</header>
|
||||
<header>A helpful point</header>
|
||||
|
||||
**Pitchfork hoodie semiotics**, roof party pop-up _paleo_ messenger bag cred Carles tousled Truffaut yr. Semiotics viral freegan VHS, Shoreditch disrupt McSweeney's. Intelligentsia kale chips Vice four dollar toast, Schlitz crucifix
|
||||
|
||||
@ -968,10 +959,10 @@ Here is the markup for the first of these callouts.
|
||||
</div>
|
||||
```
|
||||
|
||||
Notice that:
|
||||
* the callout header text is forced to all upper case
|
||||
* the callout body can be written in markdown
|
||||
* a blank line separates the `</header>` tag from the markdown content
|
||||
Notice that
|
||||
* the callout header text is forced to all upper case.
|
||||
* the callout body can be written in markdown.
|
||||
* a blank line separates the `</header>` tag from the markdown content.
|
||||
|
||||
Callouts are meant to grab the user's attention. They are not for casual asides. Please use them sparingly.
|
||||
|
||||
@ -1263,7 +1254,7 @@ Note that you generally don't wrap a floating image in a `<figure>` element.
|
||||
|
||||
If you have a floating image inside an alert, callout, or a subsection, it is a good idea to apply the `clear-fix` class to the `div` to ensure that the image doesn't overflow its container. For example:
|
||||
|
||||
<div class="alert is-helpful clear-fix">
|
||||
<div class="l-sub-section clear-fix">
|
||||
|
||||
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
|
||||
alt="flying Angular hero"
|
||||
@ -1275,7 +1266,7 @@ If you have a floating image inside an alert, callout, or a subsection, it is a
|
||||
</div>
|
||||
|
||||
```html
|
||||
<div class="alert is-helpful clear-fix">
|
||||
<div class="l-sub-section clear-fix">
|
||||
|
||||
<img src="generated/images/guide/docs-style-guide/flying-hero.png"
|
||||
alt="flying Angular hero"
|
||||
|
@ -101,7 +101,7 @@ The `loadComponent()` method is doing a lot of the heavy lifting here.
|
||||
Take it step by step. First, it picks an ad.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
|
@ -11,7 +11,7 @@ The `@angular/elements` package exports a `createCustomElement()` API that provi
|
||||
Transforming a component to a custom element makes all of the required Angular infrastructure available to the browser.
|
||||
Creating a custom element is simple and straightforward, and automatically connects your component-defined view with change detection and data binding, mapping Angular functionality to the corresponding native HTML equivalents.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
We are working on custom elements that can be used by web apps built on other frameworks.
|
||||
A minimal, self-contained version of the Angular framework will be injected as a service to support the component's change-detection and data-binding functionality.
|
||||
|
@ -9,7 +9,7 @@ This page shows how to validate user input in the UI and display useful validati
|
||||
using both reactive and template-driven forms. It assumes some basic knowledge of the two
|
||||
forms modules.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you're new to forms, start by reviewing the [Forms](guide/forms) and
|
||||
[Reactive Forms](guide/reactive-forms) guides.
|
||||
@ -51,7 +51,7 @@ but only if the `name` is invalid and the control is either `dirty` or `touched`
|
||||
There are messages for `required`, `minlength`, and `forbiddenName`.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -178,7 +178,7 @@ Once the `ForbiddenValidatorDirective` is ready, you can simply add its selector
|
||||
</code-example>
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You may have noticed that the custom validation directive is instantiated with `useExisting`
|
||||
rather than `useClass`. The registered validator must be _this instance_ of
|
||||
@ -211,7 +211,7 @@ set the color of each form control's border.
|
||||
## Cross field validation
|
||||
This section shows how to perform cross field validation. It assumes some basic knowledge of creating custom validators.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you haven't created custom validators before, start by reviewing the [custom validators section](guide/form-validation#custom-validators).
|
||||
|
||||
|
@ -29,7 +29,7 @@ You can run the <live-example></live-example> in Stackblitz and download the cod
|
||||
You can build forms by writing templates in the Angular [template syntax](guide/template-syntax) with
|
||||
the form-specific directives and techniques described in this page.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can also use a reactive (or model-driven) approach to build forms.
|
||||
However, this page focuses on template-driven forms.
|
||||
@ -62,7 +62,7 @@ If you delete the hero name, the form displays a validation error in an attentio
|
||||
|
||||
Note that the *Submit* button is disabled, and the "required" bar to the left of the input control changes from green to red.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can customize the colors and location of the "required" bar with standard CSS.
|
||||
|
||||
@ -180,7 +180,7 @@ Update it with the following:
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
There are two changes:
|
||||
|
||||
@ -208,7 +208,7 @@ Replace the contents of its template with the following:
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
There are only two changes.
|
||||
The `template` is simply the new element tag identified by the component's `selector` property.
|
||||
@ -235,7 +235,7 @@ You added a *Submit* button at the bottom with some classes on it for styling.
|
||||
|
||||
*You're not using Angular yet*. There are no bindings or extra directives, just layout.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
In template driven forms, if you've imported `FormsModule`, you don't have to do anything
|
||||
to the `<form>` tag in order to make use of `FormsModule`. Continue on to see how this works.
|
||||
@ -311,7 +311,7 @@ Find the `<input>` tag for *Name* and update it like this:
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You added a diagnostic interpolation after the input tag
|
||||
so you can see what you're doing.
|
||||
@ -331,7 +331,7 @@ a template variable for the form. Update the `<form>` tag with
|
||||
|
||||
The variable `heroForm` is now a reference to the `NgForm` directive that governs the form as a whole.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
{@a ngForm}
|
||||
|
||||
@ -362,7 +362,7 @@ At some point it might look like this:
|
||||
The diagnostic is evidence that values really are flowing from the input box to the model and
|
||||
back again.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
That's *two-way data binding*.
|
||||
For more information, see
|
||||
@ -375,7 +375,7 @@ Notice that you also added a `name` attribute to the `<input>` tag and set it to
|
||||
which makes sense for the hero's name. Any unique value will do, but using a descriptive name is helpful.
|
||||
Defining a `name` attribute is a requirement when using `[(ngModel)]` in combination with a form.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Internally, Angular creates `FormControl` instances and
|
||||
registers them with an `NgForm` directive that Angular attached to the `<form>` tag.
|
||||
@ -395,7 +395,7 @@ After revision, the core of the form should look like this:
|
||||
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
* Each input element has an `id` property that is used by the `label` element's `for` attribute
|
||||
to match the label to its input control.
|
||||
@ -571,7 +571,7 @@ Here's an example of an error message added to the _name_ input box:
|
||||
You need a template reference variable to access the input box's Angular control from within the template.
|
||||
Here you created a variable called `name` and gave it the value "ngModel".
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Why "ngModel"?
|
||||
A directive's [exportAs](api/core/Directive) property
|
||||
@ -687,7 +687,7 @@ For you, it was as simple as this:
|
||||
|
||||
Submitting the form isn't terribly dramatic at the moment.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
An unsurprising observation for a demo. To be honest,
|
||||
jazzing it up won't teach you anything new about forms.
|
||||
|
@ -580,7 +580,7 @@ For more information, see https://www.npmjs.com/package/@angular-devkit/schemati
|
||||
## Scoped package
|
||||
|
||||
A way to group related npm packages.
|
||||
NgModules are delivered within *scoped packages* whose names begin with the Angular *scope name* `@angular`. For example, `@angular/core`, `@angular/common`, `@angular/forms`, and `@angular/router`.
|
||||
NgModules are delivered within *scoped packages* whose names begin with the Angular *scope name* `@angular`. For example, `@angular/core`, `@angular/common`, `@angular/http`, and `@angular/router`.
|
||||
|
||||
Import a scoped package in the same way that you import a normal package.
|
||||
|
||||
|
@ -24,7 +24,7 @@ An Angular application is a tree of components. Each component instance has its
|
||||
The tree of components parallels the tree of injectors.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -61,7 +61,7 @@ The requests keep bubbling up until Angular finds an injector that can handle th
|
||||
If it runs out of ancestors, Angular throws an error.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -196,7 +196,7 @@ Providing the service at the component level ensures that _every_ instance of th
|
||||
No tax return overwriting. No mess.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
@ -244,7 +244,7 @@ its injector produces an instance of `Car` resolved by injector (C) with an `Eng
|
||||
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
|
@ -497,7 +497,7 @@ and displays search results as they arrive.
|
||||
|
||||
A search value reaches the service only if it's a new value and the user has stopped typing.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `withRefresh` option is explained [below](#cache-refresh).
|
||||
|
||||
@ -517,7 +517,7 @@ it cancels that request and sends a new one.
|
||||
server returns them out of order.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you think you'll reuse this debouncing logic,
|
||||
consider moving it to a utility function or into the `PackageSearchService` itself.
|
||||
@ -622,7 +622,7 @@ Then import and add it to the `AppModule` _providers array_ like this:
|
||||
As you create new interceptors, add them to the `httpInterceptorProviders` array and
|
||||
you won't have to revisit the `AppModule`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
There are many more interceptors in the complete sample code.
|
||||
|
||||
@ -824,7 +824,7 @@ and emits again later with the updated search results.
|
||||
|
||||
The _cache-then-refresh_ option is triggered by the presence of a **custom `x-refresh` header**.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
A checkbox on the `PackageSearchComponent` toggles a `withRefresh` flag,
|
||||
which is one of the arguments to `PackageSearchService.search()`.
|
||||
|
@ -54,10 +54,10 @@ If you use JIT, you also need to define the `LOCALE_ID` provider in your main mo
|
||||
</code-example>
|
||||
|
||||
|
||||
For more information about Unicode locale identifiers, see the
|
||||
For more information about Unicode locale identifiers, see the
|
||||
[CLDR core spec](http://cldr.unicode.org/core-spec#Unicode_Language_and_Locale_Identifiers).
|
||||
|
||||
For a complete list of locales supported by Angular, see
|
||||
For a complete list of locales supported by Angular, see
|
||||
[the Angular repository](https://github.com/angular/angular/tree/master/packages/common/locales).
|
||||
|
||||
The locale identifiers used by CLDR and Angular are based on [BCP47](http://www.rfc-editor.org/rfc/bcp/bcp47.txt).
|
||||
@ -101,27 +101,27 @@ specify a custom locale id. For example, Angular's locale data defines the local
|
||||
"fr". You can use the second parameter to associate the imported French locale data with the custom
|
||||
locale id "fr-FR" instead of "fr".
|
||||
|
||||
The files in `@angular/common/locales` contain most of the locale data that you
|
||||
The files in `@angular/common/locales` contain most of the locale data that you
|
||||
need, but some advanced formatting options might only be available in the extra dataset that you can
|
||||
import from `@angular/common/locales/extra`. An error message informs you when this is the case.
|
||||
|
||||
|
||||
<code-example path="i18n/doc-files/app.locale_data_extra.ts" region="import-locale-extra" title="src/app/app.module.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
All locale data used by Angular are extracted from the Unicode Consortium's
|
||||
<a href="http://cldr.unicode.org/" title="CLDR">Common Locale Data Repository (CLDR)</a>.
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
## Template translations
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
This document refers to a unit of translatable text as "text," a "message", or a
|
||||
"text message."
|
||||
This document refers to a unit of translatable text as "text," a "message", or a
|
||||
"text message."
|
||||
|
||||
</div>
|
||||
|
||||
@ -168,7 +168,7 @@ To mark the greeting for translation, add the `i18n` attribute to the `<h1>` tag
|
||||
{@a help-translator}
|
||||
### Help the translator with a description and meaning
|
||||
|
||||
To translate a text message accurately, the translator may need additional information or context.
|
||||
To translate a text message accurately, the translator may need additional information or context.
|
||||
|
||||
You can add a description of the text message as the value of the `i18n` attribute, as shown in the
|
||||
example below:
|
||||
@ -176,7 +176,7 @@ example below:
|
||||
<code-example path="i18n/doc-files/app.component.html" region="i18n-attribute-desc" title="src/app/app.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The translator may also need to know the meaning or intent of the text message within this particular
|
||||
The translator may also need to know the meaning or intent of the text message within this particular
|
||||
app context.
|
||||
|
||||
You add context by beginning the `i18n` attribute value with the _meaning_ and
|
||||
@ -192,7 +192,7 @@ The Angular extraction tool preserves both the meaning and the description in th
|
||||
source file to facilitate contextually-specific translations, but only the combination of meaning
|
||||
and text message are used to generate the specific id of a translation. If you have two
|
||||
similar text messages with different meanings, they are extracted separately. If you have two similar
|
||||
text messages with different descriptions (not different meanings), then they are extracted only once.
|
||||
text messages with different descriptions (not different meanings), then they are extracted only once.
|
||||
|
||||
|
||||
{@a custom-id}
|
||||
@ -208,7 +208,7 @@ When you change the translatable text, the extractor tool generates a new id for
|
||||
You must then update the translation file with the new id.
|
||||
|
||||
Alternatively, you can specify a custom id in the `i18n` attribute by using the prefix `@@`.
|
||||
The example below defines the custom id `introductionHeader`:
|
||||
The example below defines the custom id `introductionHeader`:
|
||||
|
||||
<code-example path='i18n/doc-files/app.component.html' region='i18n-attribute-solo-id' title='app/app.component.html' linenums="false">
|
||||
</code-example>
|
||||
@ -220,7 +220,7 @@ custom id.
|
||||
</code-example>
|
||||
|
||||
The custom id is persistent. The extractor tool does not change it when the translatable text changes.
|
||||
Therefore, you do not need to update the translation. This approach makes maintenance easier.
|
||||
Therefore, you do not need to update the translation. This approach makes maintenance easier.
|
||||
|
||||
#### Use a custom id with a description
|
||||
|
||||
@ -266,7 +266,7 @@ the same text, `Bonjour`:
|
||||
<!-- ... -->
|
||||
<p>Bonjour</p>
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
{@a no-element}
|
||||
@ -325,7 +325,7 @@ the number of minutes.
|
||||
* The third parameter defines a pluralization pattern consisting of pluralization categories and their matching values.
|
||||
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
This syntax conforms to the
|
||||
<a href="http://userguide.icu-project.org/formatparse/messages" title="ICU Message Format">ICU Message Format</a>
|
||||
@ -352,7 +352,7 @@ Any unmatched cardinality uses `other {{{minutes}} minutes ago}`. You could choo
|
||||
for two, three, or any other number if the pluralization rules were different. For the example of
|
||||
"minute", only these three patterns are necessary in English.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can use interpolations and html markup inside of your translations.
|
||||
|
||||
@ -399,7 +399,7 @@ By default, the tool generates a translation file named `messages.xlf` in the
|
||||
<a href="https://en.wikipedia.org/wiki/XLIFF">XML Localization Interchange File Format
|
||||
(XLIFF, version 1.2)</a>.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
If you don't use the CLI, you have two options:
|
||||
* You can use the `ng-xi18n` tool directly from the `@angular/compiler-cli` package.
|
||||
@ -430,7 +430,7 @@ ng xi18n --i18n-format=xmb
|
||||
|
||||
The sample in this guide uses the default XLIFF 1.2 format.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
XLIFF files have the extension .xlf. The XMB format generates .xmb source files but uses
|
||||
.xtb (XML Translation Bundle: XTB) translation files.
|
||||
@ -474,7 +474,7 @@ file. This information is not used by Angular, but external translation tools ma
|
||||
## Translate text messages
|
||||
|
||||
The `ng xi18n` command generates a translation source file named `messages.xlf` in the project `src`
|
||||
folder.
|
||||
folder.
|
||||
|
||||
The next step is to translate this source file into the specific language
|
||||
translation files. The example in this guide creates a French translation file.
|
||||
@ -488,7 +488,7 @@ for the project structure to reflect the entire internationalization effort.
|
||||
One approach is to dedicate a folder to localization and store related assets, such as
|
||||
internationalization files, there.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Localization and internationalization are
|
||||
<a href="https://en.wikipedia.org/wiki/Internationalization_and_localization">different but
|
||||
@ -526,7 +526,7 @@ This sample file is easy to translate without a special editor or knowledge of F
|
||||
</code-example>
|
||||
|
||||
> This XML element represents the translation of the `<h1>` greeting tag that you marked with the
|
||||
`i18n` attribute earlier in this guide.
|
||||
`i18n` attribute earlier in this guide.
|
||||
|
||||
> Note that the translation unit `id=introductionHeader` is derived from the
|
||||
[custom `id`](#custom-id "Set a custom id") that you set earlier, but
|
||||
@ -573,7 +573,7 @@ To translate a `plural`, translate its ICU format match values:
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translated-plural" title="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
You can add or remove plural cases, with each language having its own cardinality. (See
|
||||
You can add or remove plural cases, with each language having its own cardinality. (See
|
||||
[CLDR plural rules](http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html).)
|
||||
|
||||
{@a translate-select}
|
||||
@ -609,7 +609,7 @@ Here they are together, after translation:
|
||||
{@a translate-nested}
|
||||
### Translate a nested expression
|
||||
|
||||
A nested expression is similar to the previous examples. As in the previous example, there are
|
||||
A nested expression is similar to the previous examples. As in the previous example, there are
|
||||
two translation units. The first one contains the text outside of the nested expression:
|
||||
|
||||
<code-example path="i18n/doc-files/messages.fr.xlf.html" region="translate-nested-1" title="src/locale/messages.fr.xlf (<trans-unit>)" linenums="false">
|
||||
@ -683,40 +683,28 @@ You also need to instruct the AOT compiler to use your translation configuration
|
||||
* `i18nLocale`: the locale id.
|
||||
|
||||
```
|
||||
"build": {
|
||||
"configurations": {
|
||||
...
|
||||
"configurations": {
|
||||
"fr": {
|
||||
"aot": true,
|
||||
"outputPath": "dist/my-project-fr/",
|
||||
"i18nFile": "src/locale/messages.fr.xlf",
|
||||
"i18nFormat": "xlf",
|
||||
"i18nLocale": "fr",
|
||||
...
|
||||
"fr": {
|
||||
"aot": true,
|
||||
"outputPath": "dist/my-project-fr/",
|
||||
"i18nFile": "src/locale/messages.fr.xlf",
|
||||
"i18nFormat": "xlf",
|
||||
"i18nLocale": "fr",
|
||||
...
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
...
|
||||
"configurations": {
|
||||
...
|
||||
"fr": {
|
||||
"browserTarget": "*project-name*:build:fr"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You then pass the configuration with the `ng serve` or `ng build` commands.
|
||||
The example below shows how to serve the French language file created in previous
|
||||
You then pass the configuration with the `ng serve` or `ng build` commands.
|
||||
The example below shows how to serve the French language file created in previous
|
||||
sections of this guide:
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng serve --configuration=fr
|
||||
</code-example>
|
||||
|
||||
For production builds, you define a separate `production-fr` build configuration in
|
||||
For production builds, you define a separate `production-fr` build configuration in
|
||||
your `angular.json`.
|
||||
|
||||
```
|
||||
|
@ -300,7 +300,7 @@ The sequence of log messages follows the prescribed hook calling order:
|
||||
`OnChanges`, `OnInit`, `DoCheck` (3x), `AfterContentInit`, `AfterContentChecked` (3x),
|
||||
`AfterViewInit`, `AfterViewChecked` (3x), and `OnDestroy`.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The constructor isn't an Angular hook *per se*.
|
||||
The log confirms that input properties (the `name` property in this case) have no assigned values at construction.
|
||||
@ -323,7 +323,7 @@ Go undercover with these two spy hooks to discover when an element is initialize
|
||||
This is the perfect infiltration job for a directive.
|
||||
The heroes will never know they're being watched.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Kidding aside, pay attention to two key points:
|
||||
|
||||
@ -373,7 +373,7 @@ Use `ngOnInit()` for two main reasons:
|
||||
|
||||
Experienced developers agree that components should be cheap and safe to construct.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Misko Hevery, Angular team lead,
|
||||
[explains why](http://misko.hevery.com/code-reviewers-guide/flaw-constructor-does-real-work/)
|
||||
@ -394,7 +394,7 @@ Remember also that a directive's data-bound input properties are not set until _
|
||||
That's a problem if you need to initialize the directive based on those properties.
|
||||
They'll have been set when `ngOnInit()` runs.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The `ngOnChanges()` method is your first opportunity to access those properties.
|
||||
Angular calls `ngOnChanges()` before `ngOnInit()` and many times after that.
|
||||
@ -460,7 +460,7 @@ The hero object *reference* didn't change so, from Angular's perspective, there
|
||||
|
||||
Use the `DoCheck` hook to detect and act upon changes that Angular doesn't catch on its own.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
Use this method to detect a change that Angular overlooked.
|
||||
|
||||
@ -549,7 +549,7 @@ The *AfterContent* sample explores the `AfterContentInit()` and `AfterContentChe
|
||||
*Content projection* is a way to import HTML content from outside the component and insert that content
|
||||
into the component's template in a designated spot.
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
AngularJS developers know this technique as *transclusion*.
|
||||
|
||||
@ -577,7 +577,7 @@ In this case, the projected content is the `<app-child>` from the parent.
|
||||
<img src='generated/images/guide/lifecycle-hooks/projected-child-view.png' alt="Projected Content">
|
||||
</figure>
|
||||
|
||||
<div class="alert is-helpful">
|
||||
<div class="l-sub-section">
|
||||
|
||||
The telltale signs of *content projection* are twofold:
|
||||
|
||||
|
@ -12,8 +12,8 @@ A basic understanding of the following concepts:
|
||||
|
||||
At a high level, NgModules are a way to organize Angular apps
|
||||
and they accomplish this through the metadata in the `@NgModule`
|
||||
decorator.
|
||||
The metadata falls into three categories:
|
||||
decorator. The metadata falls
|
||||
into three categories:
|
||||
|
||||
* **Static:** Compiler configuration which tells the compiler about directive selectors and where in templates the directives should be applied through selector matching. This is configured via the `declarations` array.
|
||||
* **Runtime:** Injector configuration via the `providers` array.
|
||||
@ -75,8 +75,9 @@ The following table summarizes the `@NgModule` metadata properties.
|
||||
</ol>
|
||||
|
||||
Components, directives, and pipes must belong to _exactly_ one module.
|
||||
The compiler emits an error if you try to declare the same class in more than one module. Be careful not to re-declare a class that is imported
|
||||
directly or indirectly from another module.
|
||||
The compiler emits an error if you try to declare the same class in more than one module.
|
||||
|
||||
Don't re-declare a class imported from another module.
|
||||
|
||||
</td>
|
||||
|
||||
@ -107,7 +108,7 @@ The following table summarizes the `@NgModule` metadata properties.
|
||||
|
||||
Components in external modules continue to receive the instance provided by their injectors.
|
||||
|
||||
For more information on injector hierarchy and scoping, see [Providers](guide/providers) and the [DI Guide](guide/dependency-injection).
|
||||
For more information on injector hierarchy and scoping, see [Providers](guide/providers).
|
||||
|
||||
</td>
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user