Compare commits
218 Commits
Author | SHA1 | Date | |
---|---|---|---|
9e45589085 | |||
3c93d07124 | |||
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 |
@ -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.0
|
||||
var_2: &cache_key v2-angular-{{ .Branch }}-{{ checksum "yarn.lock" }}-0.3.0
|
||||
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
|
||||
@ -80,7 +80,7 @@ jobs:
|
||||
|
||||
- run: ls /home/circleci/bazel_repository_cache || true
|
||||
- run: bazel info release
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel run @nodejs//:yarn
|
||||
# Use bazel query so that we explicitly ask for all buildable targets to be built as well
|
||||
# This avoids waiting for the slowest build target to finish before running the first test
|
||||
# See https://github.com/bazelbuild/bazel/issues/4257
|
||||
@ -131,7 +131,7 @@ jobs:
|
||||
- run: sudo cp .circleci/bazel.rc /etc/bazel.bazelrc
|
||||
- *setup-bazel-remote-cache
|
||||
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel run @nodejs//:yarn
|
||||
- run: scripts/build-packages-dist.sh
|
||||
|
||||
# Save the npm packages from //packages/... for other workflow jobs to read
|
||||
@ -149,6 +149,10 @@ jobs:
|
||||
# See comments inside the integration/run_tests.sh script.
|
||||
integration_test:
|
||||
<<: *job_defaults
|
||||
# Note: we run Bazel in one of the integration tests, and it can consume >2G
|
||||
# of memory. Together with the system under test, this can exhaust the RAM
|
||||
# on a 4G worker so we use a larger machine here too.
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- *define_env_vars
|
||||
- checkout:
|
||||
|
2
.github/angular-robot.yml
vendored
2
.github/angular-robot.yml
vendored
@ -45,10 +45,12 @@ merge:
|
||||
- "packages/language-service/**"
|
||||
- "**/.gitignore"
|
||||
- "**/.gitkeep"
|
||||
- "**/package.json"
|
||||
- "**/tsconfig-build.json"
|
||||
- "**/tsconfig.json"
|
||||
- "**/rollup.config.js"
|
||||
- "**/BUILD.bazel"
|
||||
- "packages/**/integrationtest/**"
|
||||
- "packages/**/test/**"
|
||||
|
||||
# comment that will be added to a PR when there is a conflict, leave empty or set to false to disable
|
||||
|
10
BUILD.bazel
10
BUILD.bazel
@ -11,7 +11,7 @@ exports_files([
|
||||
# This ensures that package.json in subdirectories get installed as well.
|
||||
alias(
|
||||
name = "install",
|
||||
actual = "@yarn//:yarn",
|
||||
actual = "@nodejs//:yarn",
|
||||
)
|
||||
|
||||
node_modules_filegroup(
|
||||
@ -44,14 +44,16 @@ filegroup(
|
||||
"//:node_modules/zone.js/dist/zone.js",
|
||||
"//:node_modules/zone.js/dist/zone-testing.js",
|
||||
"//:node_modules/zone.js/dist/task-tracking.js",
|
||||
"//:test-events.js",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "angularjs",
|
||||
# do not sort
|
||||
name = "angularjs_scripts",
|
||||
srcs = [
|
||||
"//:node_modules/angular/angular.js",
|
||||
"//:node_modules/angular-1.5/angular.js",
|
||||
"//:node_modules/angular-mocks-1.5/angular-mocks.js",
|
||||
"//:node_modules/angular-mocks/angular-mocks.js",
|
||||
"//:node_modules/angular/angular.js",
|
||||
],
|
||||
)
|
||||
|
110
CHANGELOG.md
110
CHANGELOG.md
@ -1,3 +1,85 @@
|
||||
<a name="6.0.9"></a>
|
||||
## [6.0.9](https://github.com/angular/angular/compare/6.0.8...6.0.9) (2018-07-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** format fractional seconds ([#24844](https://github.com/angular/angular/issues/24844)) ([3c93d07](https://github.com/angular/angular/commit/3c93d07)), closes [#24831](https://github.com/angular/angular/issues/24831)
|
||||
|
||||
|
||||
|
||||
<a name="6.0.8"></a>
|
||||
## [6.0.8](https://github.com/angular/angular/compare/6.0.7...6.0.8) (2018-07-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **common:** do not round factional seconds ([#24831](https://github.com/angular/angular/issues/24831)) ([0746485](https://github.com/angular/angular/commit/0746485)), closes [#24384](https://github.com/angular/angular/issues/24384)
|
||||
* **common:** properly update collection reference in NgForOf ([#24684](https://github.com/angular/angular/issues/24684)) ([9a98de9](https://github.com/angular/angular/commit/9a98de9)), closes [#24155](https://github.com/angular/angular/issues/24155)
|
||||
* **common:** use correct currency format for locale de-AT ([#24658](https://github.com/angular/angular/issues/24658)) ([a92f111](https://github.com/angular/angular/commit/a92f111)), closes [#24609](https://github.com/angular/angular/issues/24609)
|
||||
* **compiler-cli:** Use typescript to resolve modules for metadata ([#22856](https://github.com/angular/angular/issues/22856)) ([7717ff1](https://github.com/angular/angular/commit/7717ff1))
|
||||
* **core:** use addCustomEqualityTester instead of overriding toEqual ([#22983](https://github.com/angular/angular/issues/22983)) ([b8975a9](https://github.com/angular/angular/commit/b8975a9)), closes [#22939](https://github.com/angular/angular/issues/22939)
|
||||
* **language-service:** do not overwrite native `Reflect` ([#24299](https://github.com/angular/angular/issues/24299)) ([de1c44f](https://github.com/angular/angular/commit/de1c44f)), closes [#21420](https://github.com/angular/angular/issues/21420)
|
||||
* **router:** add ability to recover from malformed url ([#23283](https://github.com/angular/angular/issues/23283)) ([2d4f4b5](https://github.com/angular/angular/commit/2d4f4b5)), closes [#21468](https://github.com/angular/angular/issues/21468)
|
||||
* **service-worker:** avoid network requests when looking up hashed resources in cache ([#24127](https://github.com/angular/angular/issues/24127)) ([183b079](https://github.com/angular/angular/commit/183b079))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** set animations styles properly on platform-server ([#24624](https://github.com/angular/angular/issues/24624)) ([0b356d4](https://github.com/angular/angular/commit/0b356d4))
|
||||
* **common:** use correct ICU plural for locale mk ([#24659](https://github.com/angular/angular/issues/24659)) ([64a8584](https://github.com/angular/angular/commit/64a8584))
|
||||
|
||||
|
||||
<a name="6.0.6"></a>
|
||||
## [6.0.6](https://github.com/angular/angular/compare/6.0.5...6.0.6) (2018-06-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** support `.` in import statements. ([#20634](https://github.com/angular/angular/issues/20634)) ([e543c73](https://github.com/angular/angular/commit/e543c73)), closes [#20363](https://github.com/angular/angular/issues/20363)
|
||||
* **core:** Injector correctly honors the @Self flag ([#24520](https://github.com/angular/angular/issues/24520)) ([f5b3661](https://github.com/angular/angular/commit/f5b3661))
|
||||
|
||||
|
||||
|
||||
<a name="6.0.5"></a>
|
||||
## [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)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** Fix browser detection logic ([#24188](https://github.com/angular/angular/issues/24188)) ([c9eb491](https://github.com/angular/angular/commit/c9eb491))
|
||||
* **animations:** retain trigger-state for nodes that are moved around ([#24238](https://github.com/angular/angular/issues/24238)) ([19deca1](https://github.com/angular/angular/commit/19deca1))
|
||||
* **forms:** properly handle special properties in FormGroup.get ([#22249](https://github.com/angular/angular/issues/22249)) ([dc3e8aa](https://github.com/angular/angular/commit/dc3e8aa)), closes [#17195](https://github.com/angular/angular/issues/17195)
|
||||
* **platform-server:** avoid clash between server and client style encapsulation attributes ([#24158](https://github.com/angular/angular/issues/24158)) ([e9f2203](https://github.com/angular/angular/commit/e9f2203))
|
||||
* **platform-server:** avoid dependency cycle when using http interceptor ([#24229](https://github.com/angular/angular/issues/24229)) ([2991b1b](https://github.com/angular/angular/commit/2991b1b)), closes [#23023](https://github.com/angular/angular/issues/23023)
|
||||
* **platform-server:** don't reflect innerHTML property to attibute ([#24213](https://github.com/angular/angular/issues/24213)) ([c17098d](https://github.com/angular/angular/commit/c17098d)), closes [#19278](https://github.com/angular/angular/issues/19278)
|
||||
* **platform-server:** provide Domino DOM types globally ([#24116](https://github.com/angular/angular/issues/24116)) ([906b3ec](https://github.com/angular/angular/commit/906b3ec)), closes [#23280](https://github.com/angular/angular/issues/23280) [#23133](https://github.com/angular/angular/issues/23133)
|
||||
|
||||
|
||||
<a name="6.0.3"></a>
|
||||
## [6.0.3](https://github.com/angular/angular/compare/6.0.2...6.0.3) (2018-05-22)
|
||||
|
||||
@ -42,7 +124,7 @@
|
||||
|
||||
Angular v6 is the first release of Angular that unifies the Framework, Material and CLI.
|
||||
|
||||
To learn about the release highlights and our new CLI-powered update workflow for your projects please check out the [v6 release announcement](https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4).
|
||||
To learn about the release highlights and our new CLI-powered update workflow for your projects please check out the [v6 release announcement](https://blog.angular.io/version-6-0-0-of-angular-now-available-cc56b0efa7a4).
|
||||
|
||||
|
||||
|
||||
@ -193,10 +275,10 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
This change removes support for `<template>`. `<ng-template>` should be used instead.
|
||||
|
||||
BEFORE:
|
||||
|
||||
|
||||
<!-- html template -->
|
||||
<template>some template content</template>
|
||||
|
||||
|
||||
# tsconfig.json
|
||||
{
|
||||
# ...
|
||||
@ -206,12 +288,12 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
"enableLegacyTemplate": [true|false]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
AFTER:
|
||||
|
||||
|
||||
<!-- html template -->
|
||||
<ng-template>some template content</ng-template>
|
||||
|
||||
|
||||
* **core:** it is no longer possible to import animation-related functions from @angular/core. All animation symbols must now be imported from @angular/animations.
|
||||
|
||||
|
||||
@ -224,35 +306,35 @@ To learn about the release highlights and our new CLI-powered update workflow fo
|
||||
|
||||
Previously, ngModelChange was emitted before its underlying control was updated.
|
||||
This was fine if you passed through the value directly through the $event keyword, e.g.
|
||||
|
||||
|
||||
```
|
||||
<input [(ngModel)]="name" (ngModelChange)="onChange($event)">
|
||||
|
||||
|
||||
onChange(value) {
|
||||
console.log(value); // would log updated value
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
However, if you had a handler for the ngModelChange event that checked the value through the control,
|
||||
you would get the old value rather than the updated value. e.g:
|
||||
|
||||
|
||||
```
|
||||
<input #modelDir="ngModel" [(ngModel)]="name" (ngModelChange)="onChange(modelDir)">
|
||||
|
||||
|
||||
onChange(ngModel: NgModel) {
|
||||
console.log(ngModel.value); // would log old value, not updated value
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Now the value and validity will be updated before the ngModelChange event is emitted,
|
||||
so the same setup will log the updated value.
|
||||
|
||||
|
||||
```
|
||||
onChange(ngModel: NgModel) {
|
||||
console.log(ngModel.value); // will log updated value
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
We think this order will be less confusing when the control is checked directly.
|
||||
You will only need to update your app if it has relied on this bug to keep track of the old control value.
|
||||
If that is the case, you should be able to track the old value directly by saving it on your component.
|
||||
|
@ -227,10 +227,15 @@ The following is the list of supported scopes:
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
||||
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g. public path changes, package.json changes done to all packages, d.ts file/format changes, changes to bundles, etc.
|
||||
* **packaging**: used for changes that change the npm package layout in all of our packages, e.g.
|
||||
public path changes, package.json changes done to all packages, d.ts file/format changes, changes
|
||||
to bundles, etc.
|
||||
* **changelog**: used for updating the release notes in CHANGELOG.md
|
||||
* **aio**: used for docs-app (angular.io) related changes within the /aio directory of the repo
|
||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all packages (e.g. `style: add missing semicolons`)
|
||||
* **docs-infra**: used for docs-app (angular.io) related changes within the /aio directory of the
|
||||
repo
|
||||
* none/empty string: useful for `style`, `test` and `refactor` changes that are done across all
|
||||
packages (e.g. `style: add missing semicolons`) and for docs changes that are not related to a
|
||||
specific package (e.g. `docs: fix typo in tutorial`).
|
||||
|
||||
### Subject
|
||||
The subject contains a succinct description of the change:
|
||||
@ -269,7 +274,7 @@ changes to be accepted, the CLA must be signed. It's a quick process, we promise
|
||||
* https://help.github.com/articles/about-commit-email-addresses/
|
||||
* https://help.github.com/articles/blocking-command-line-pushes-that-expose-your-personal-email-address/
|
||||
|
||||
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
|
||||
Note that if you have more than one Git identity, it is important to verify that you are logged in with the same ID with which you signed the CLA, before you commit changes. If not, your PR will fail the CLA check.
|
||||
|
||||
<hr>
|
||||
|
||||
|
30
WORKSPACE
30
WORKSPACE
@ -6,23 +6,23 @@ workspace(name = "angular")
|
||||
|
||||
http_archive(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.8.0.zip",
|
||||
strip_prefix = "rules_nodejs-0.8.0",
|
||||
sha256 = "4e40dd49ae7668d245c3107645f2a138660fcfd975b9310b91eda13f0c973953",
|
||||
url = "https://github.com/bazelbuild/rules_nodejs/archive/0.10.1.zip",
|
||||
strip_prefix = "rules_nodejs-0.10.1",
|
||||
sha256 = "634206524d90dc03c52392fa3f19a16637d2bcf154910436fe1d669a0d9d7b9c",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
name = "io_bazel_rules_webtesting",
|
||||
url = "https://github.com/bazelbuild/rules_webtesting/archive/cfcaaf98553fee8e7063b5f5c11fd1b77e43d683.zip",
|
||||
strip_prefix = "rules_webtesting-cfcaaf98553fee8e7063b5f5c11fd1b77e43d683",
|
||||
sha256 = "636c7a9ac2ca13a04d982c2f9c874876ecc90a7b9ccfe4188156122b26ada7b3",
|
||||
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/v0.13.0.zip",
|
||||
strip_prefix = "rules_typescript-0.13.0",
|
||||
sha256 = "8f2767ff56ad68c80c62e9a1cdc2ba2c2ba0b19d350f713365e5333045df02e3",
|
||||
url = "https://github.com/bazelbuild/rules_typescript/archive/0.15.0.zip",
|
||||
strip_prefix = "rules_typescript-0.15.0",
|
||||
sha256 = "1aa75917330b820cb239b3c10a936a10f0a46fe215063d4492dd76dc6e1616f4",
|
||||
)
|
||||
|
||||
http_archive(
|
||||
@ -34,13 +34,13 @@ http_archive(
|
||||
# This commit matches the version of buildifier in angular/ngcontainer
|
||||
# If you change this, also check if it matches the version in the angular/ngcontainer
|
||||
# version in /.circleci/config.yml
|
||||
BAZEL_BUILDTOOLS_VERSION = "fd9878fd5de921e0bbab3dcdcb932c2627812ee1"
|
||||
BAZEL_BUILDTOOLS_VERSION = "82b21607e00913b16fe1c51bec80232d9d6de31c"
|
||||
|
||||
http_archive(
|
||||
name = "com_github_bazelbuild_buildtools",
|
||||
url = "https://github.com/bazelbuild/buildtools/archive/%s.zip" % BAZEL_BUILDTOOLS_VERSION,
|
||||
strip_prefix = "buildtools-%s" % BAZEL_BUILDTOOLS_VERSION,
|
||||
sha256 = "27bb461ade23fd44ba98723ad98f84ee9c83cd3540b773b186a1bc5037f3d862",
|
||||
sha256 = "edb24c2f9c55b10a820ec74db0564415c0cf553fa55e9fc709a6332fb6685eff",
|
||||
)
|
||||
|
||||
# Fetching the Bazel source code allows us to compile the Skylark linter
|
||||
@ -66,9 +66,9 @@ http_archive(
|
||||
|
||||
http_archive(
|
||||
name = "org_brotli",
|
||||
url = "https://github.com/google/brotli/archive/c6333e1e79fb62ea088443f192293f964409b04e.zip",
|
||||
strip_prefix = "brotli-c6333e1e79fb62ea088443f192293f964409b04e",
|
||||
sha256 = "3f781988dee7dd3bcce2bf238294663cfaaf3b6433505bdb762e24d0a284d1dc",
|
||||
url = "https://github.com/google/brotli/archive/f9b8c02673c576a3e807edbf3a9328e9e7af6d7c.zip",
|
||||
strip_prefix = "brotli-f9b8c02673c576a3e807edbf3a9328e9e7af6d7c",
|
||||
sha256 = "8a517806d2b7c8505ba5c53934e7d7c70d341b68ffd268e9044d35b564a48828",
|
||||
)
|
||||
|
||||
#
|
||||
@ -77,7 +77,7 @@ http_archive(
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "check_bazel_version", "node_repositories", "yarn_install")
|
||||
|
||||
check_bazel_version("0.13.0")
|
||||
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")
|
||||
|
@ -52,8 +52,7 @@ export class BuildCleaner {
|
||||
protected removeDir(dir: string) {
|
||||
try {
|
||||
if (shell.test('-d', dir)) {
|
||||
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||
(shell as any).chmod('-R', 'a+w', dir);
|
||||
shell.chmod('-R', 'a+w', dir);
|
||||
shell.rm('-rf', dir);
|
||||
}
|
||||
} catch (err) {
|
||||
|
@ -106,8 +106,7 @@ export class BuildCreator extends EventEmitter {
|
||||
}
|
||||
|
||||
try {
|
||||
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||
(shell as any).chmod('-R', 'a-w', outputDir);
|
||||
shell.chmod('-R', 'a-w', outputDir);
|
||||
shell.rm('-f', inputFile);
|
||||
resolve();
|
||||
} catch (err) {
|
||||
|
@ -98,8 +98,7 @@ class Helper {
|
||||
const prDir = this.getPrDir(pr, isPublic);
|
||||
|
||||
if (fs.existsSync(prDir)) {
|
||||
// Undocumented signature (see https://github.com/shelljs/shelljs/pull/663).
|
||||
(shell as any).chmod('-R', 'a+w', prDir);
|
||||
shell.chmod('-R', 'a+w', prDir);
|
||||
shell.rm('-rf', prDir);
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@
|
||||
"scripts": {
|
||||
"prebuild": "yarn clean-dist",
|
||||
"build": "tsc",
|
||||
"build-watch": "yarn tsc --watch",
|
||||
"build-watch": "yarn build --watch",
|
||||
"clean-dist": "node --eval \"require('shelljs').rm('-rf', 'dist')\"",
|
||||
"dev": "concurrently --kill-others --raw --success first \"yarn build-watch\" \"yarn test-watch\"",
|
||||
"lint": "tslint --project tsconfig.json",
|
||||
@ -33,7 +33,7 @@
|
||||
"@types/jasmine": "^2.6.0",
|
||||
"@types/jsonwebtoken": "^7.2.3",
|
||||
"@types/node": "^8.0.30",
|
||||
"@types/shelljs": "^0.7.4",
|
||||
"@types/shelljs": "^0.8.0",
|
||||
"@types/supertest": "^2.0.3",
|
||||
"concurrently": "^3.5.0",
|
||||
"nodemon": "^1.12.1",
|
||||
|
@ -69,9 +69,9 @@
|
||||
"@types/express-serve-static-core" "*"
|
||||
"@types/mime" "*"
|
||||
|
||||
"@types/shelljs@^0.7.4":
|
||||
version "0.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.7.4.tgz#137b5f31306eaff4de120ffe5b9d74b297809cfc"
|
||||
"@types/shelljs@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.0.tgz#0caa56b68baae4f68f44e0dd666ab30b098e3632"
|
||||
dependencies:
|
||||
"@types/glob" "*"
|
||||
"@types/node" "*"
|
||||
|
@ -3,7 +3,7 @@
|
||||
set -eux -o pipefail
|
||||
exec 3>&1
|
||||
|
||||
echo "\n\n[`date`] - Updating the preview server..."
|
||||
echo -e "\n\n[`date`] - Updating the preview server..."
|
||||
|
||||
# Input
|
||||
readonly HOST_REPO_DIR=$1
|
||||
|
2
aio/content/examples/.gitignore
vendored
2
aio/content/examples/.gitignore
vendored
@ -60,6 +60,8 @@ dist/
|
||||
!rollup-config.js
|
||||
aot-compiler/**/*.d.ts
|
||||
aot-compiler/**/*.factory.d.ts
|
||||
upgrade-phonecat-2-hybrid/aot/**/*
|
||||
!upgrade-phonecat-2-hybrid/aot/index.html
|
||||
|
||||
# i18n
|
||||
!i18n/src/systemjs-text-plugin.js
|
||||
|
@ -40,5 +40,7 @@ export class HighlightDirective {
|
||||
// #docregion color-2
|
||||
@Input() appHighlight: string;
|
||||
// #enddocregion color-2
|
||||
}
|
||||
|
||||
// #docregion
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -16,6 +16,7 @@ describe('Form Validation Tests', function () {
|
||||
|
||||
tests('Template-Driven Form');
|
||||
bobTests();
|
||||
crossValidationTests();
|
||||
});
|
||||
|
||||
describe('Reactive form', () => {
|
||||
@ -25,6 +26,7 @@ describe('Form Validation Tests', function () {
|
||||
|
||||
tests('Reactive Form');
|
||||
bobTests();
|
||||
crossValidationTests();
|
||||
});
|
||||
});
|
||||
|
||||
@ -42,7 +44,8 @@ let page: {
|
||||
powerOption: ElementFinder,
|
||||
errorMessages: ElementArrayFinder,
|
||||
heroFormButtons: ElementArrayFinder,
|
||||
heroSubmitted: ElementFinder
|
||||
heroSubmitted: ElementFinder,
|
||||
crossValidationErrorMessage: ElementFinder,
|
||||
};
|
||||
|
||||
function getPage(sectionTag: string) {
|
||||
@ -59,7 +62,8 @@ function getPage(sectionTag: string) {
|
||||
powerOption: section.element(by.css('#power option')),
|
||||
errorMessages: section.all(by.css('div.alert')),
|
||||
heroFormButtons: buttons,
|
||||
heroSubmitted: section.element(by.css('.submitted-message'))
|
||||
heroSubmitted: section.element(by.css('.submitted-message')),
|
||||
crossValidationErrorMessage: section.element(by.css('.cross-validation-error-message')),
|
||||
};
|
||||
}
|
||||
|
||||
@ -172,3 +176,29 @@ function bobTests() {
|
||||
expectFormIsValid();
|
||||
});
|
||||
}
|
||||
|
||||
function crossValidationTests() {
|
||||
const emsg = 'Name cannot match alter ego.';
|
||||
|
||||
it(`should produce "${emsg}" error after setting name and alter ego to the same value`, function () {
|
||||
page.nameInput.clear();
|
||||
page.nameInput.sendKeys('Batman');
|
||||
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Batman');
|
||||
|
||||
expectFormIsInvalid();
|
||||
expect(page.crossValidationErrorMessage.getText()).toBe(emsg);
|
||||
});
|
||||
|
||||
it('should be ok again with different values', function () {
|
||||
page.nameInput.clear();
|
||||
page.nameInput.sendKeys('Batman');
|
||||
|
||||
page.alterEgoInput.clear();
|
||||
page.alterEgoInput.sendKeys('Superman');
|
||||
|
||||
expectFormIsValid();
|
||||
expect(page.crossValidationErrorMessage.isPresent()).toBe(false);
|
||||
});
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import { AppComponent } from './app.component';
|
||||
import { HeroFormTemplateComponent } from './template/hero-form-template.component';
|
||||
import { HeroFormReactiveComponent } from './reactive/hero-form-reactive.component';
|
||||
import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||
|
||||
import { IdentityRevealedValidatorDirective } from './shared/identity-revealed.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -19,7 +19,8 @@ import { ForbiddenValidatorDirective } from './shared/forbidden-name.directive';
|
||||
AppComponent,
|
||||
HeroFormTemplateComponent,
|
||||
HeroFormReactiveComponent,
|
||||
ForbiddenValidatorDirective
|
||||
ForbiddenValidatorDirective,
|
||||
IdentityRevealedValidatorDirective
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
|
@ -0,0 +1,42 @@
|
||||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
templateUrl: './hero-form-reactive.component.html',
|
||||
styleUrls: ['./hero-form-reactive.component.css'],
|
||||
})
|
||||
export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||
|
||||
heroForm: FormGroup;
|
||||
|
||||
// #docregion form-group
|
||||
ngOnInit(): void {
|
||||
// #docregion custom-validator
|
||||
this.heroForm = new FormGroup({
|
||||
'name': new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
// #enddocregion custom-validator
|
||||
|
||||
}
|
||||
|
||||
get name() { return this.heroForm.get('name'); }
|
||||
|
||||
get power() { return this.heroForm.get('power'); }
|
||||
// #enddocregion form-group
|
||||
}
|
||||
// #enddocregion
|
@ -0,0 +1,5 @@
|
||||
/* #docregion cross-validation-error-css */
|
||||
.cross-validation-error input {
|
||||
border-left: 5px solid red;
|
||||
}
|
||||
/* #enddocregion cross-validation-error-css */
|
@ -7,33 +7,41 @@
|
||||
|
||||
<div [hidden]="formDir.submitted">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||
<div class="form-group">
|
||||
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<input id="name" class="form-control"
|
||||
formControlName="name" required >
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<input id="name" class="form-control"
|
||||
formControlName="name" required >
|
||||
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
|
||||
<div *ngIf="name.errors.required">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="name.errors.minlength">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div *ngIf="name.errors.forbiddenName">
|
||||
Name cannot be Bob.
|
||||
<div *ngIf="name.errors.required">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="name.errors.minlength">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div *ngIf="name.errors.forbiddenName">
|
||||
Name cannot be Bob.
|
||||
</div>
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
formControlName="alterEgo" >
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
formControlName="alterEgo" >
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||
Name cannot match alter ego.
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation-error-message -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -1,40 +1,36 @@
|
||||
/* tslint:disable: member-ordering forin */
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { FormControl, FormGroup, Validators } from '@angular/forms';
|
||||
import { forbiddenNameValidator } from '../shared/forbidden-name.directive';
|
||||
import { identityRevealedValidator } from '../shared/identity-revealed.directive';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-form-reactive',
|
||||
templateUrl: './hero-form-reactive.component.html'
|
||||
templateUrl: './hero-form-reactive.component.html',
|
||||
styleUrls: ['./hero-form-reactive.component.css'],
|
||||
})
|
||||
export class HeroFormReactiveComponent implements OnInit {
|
||||
|
||||
powers = ['Really Smart', 'Super Flexible', 'Weather Changer'];
|
||||
|
||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||
hero = { name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0] };
|
||||
|
||||
heroForm: FormGroup;
|
||||
|
||||
// #docregion form-group
|
||||
ngOnInit(): void {
|
||||
// #docregion custom-validator
|
||||
this.heroForm = new FormGroup({
|
||||
'name': new FormControl(this.hero.name, [
|
||||
Validators.required,
|
||||
Validators.minLength(4),
|
||||
forbiddenNameValidator(/bob/i) // <-- Here's how you pass in the custom validator.
|
||||
forbiddenNameValidator(/bob/i)
|
||||
]),
|
||||
'alterEgo': new FormControl(this.hero.alterEgo),
|
||||
'power': new FormControl(this.hero.power, Validators.required)
|
||||
});
|
||||
// #enddocregion custom-validator
|
||||
}, { validators: identityRevealedValidator }); // <-- add custom validator at the FormGroup level
|
||||
}
|
||||
|
||||
get name() { return this.heroForm.get('name'); }
|
||||
|
||||
get power() { return this.heroForm.get('power'); }
|
||||
// #enddocregion form-group
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -5,7 +5,7 @@ import { AbstractControl, NG_VALIDATORS, Validator, ValidatorFn, Validators } fr
|
||||
// #docregion custom-validator
|
||||
/** A hero's name can't match the given regular expression */
|
||||
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
||||
return (control: AbstractControl): {[key: string]: any} => {
|
||||
return (control: AbstractControl): {[key: string]: any} | null => {
|
||||
const forbidden = nameRe.test(control.value);
|
||||
return forbidden ? {'forbiddenName': {value: control.value}} : null;
|
||||
};
|
||||
@ -22,7 +22,7 @@ export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
|
||||
export class ForbiddenValidatorDirective implements Validator {
|
||||
@Input('appForbiddenName') forbiddenName: string;
|
||||
|
||||
validate(control: AbstractControl): {[key: string]: any} {
|
||||
validate(control: AbstractControl): {[key: string]: any} | null {
|
||||
return this.forbiddenName ? forbiddenNameValidator(new RegExp(this.forbiddenName, 'i'))(control)
|
||||
: null;
|
||||
}
|
||||
|
@ -0,0 +1,25 @@
|
||||
// #docregion
|
||||
import { Directive } from '@angular/core';
|
||||
import { AbstractControl, FormGroup, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn } from '@angular/forms';
|
||||
|
||||
// #docregion cross-validation-validator
|
||||
/** A hero's name can't match the hero's alter ego */
|
||||
export const identityRevealedValidator: ValidatorFn = (control: FormGroup): ValidationErrors | null => {
|
||||
const name = control.get('name');
|
||||
const alterEgo = control.get('alterEgo');
|
||||
|
||||
return name && alterEgo && name.value === alterEgo.value ? { 'identityRevealed': true } : null;
|
||||
};
|
||||
// #enddocregion cross-validation-validator
|
||||
|
||||
// #docregion cross-validation-directive
|
||||
@Directive({
|
||||
selector: '[appIdentityRevealed]',
|
||||
providers: [{ provide: NG_VALIDATORS, useExisting: IdentityRevealedValidatorDirective, multi: true }]
|
||||
})
|
||||
export class IdentityRevealedValidatorDirective implements Validator {
|
||||
validate(control: AbstractControl): ValidationErrors {
|
||||
return identityRevealedValidator(control)
|
||||
}
|
||||
}
|
||||
// #enddocregion cross-validation-directive
|
@ -0,0 +1,4 @@
|
||||
/* #docregion */
|
||||
.cross-validation-error input {
|
||||
border-left: 5px solid red;
|
||||
}
|
@ -2,41 +2,48 @@
|
||||
<div class="container">
|
||||
|
||||
<h1>Template-Driven Form</h1>
|
||||
<!-- #docregion form-tag-->
|
||||
<form #heroForm="ngForm">
|
||||
<!-- #enddocregion form-tag-->
|
||||
<!-- #docregion cross-validation-register-validator -->
|
||||
<form #heroForm="ngForm" appIdentityRevealed>
|
||||
<!-- #enddocregion cross-validation-register-validator -->
|
||||
<div [hidden]="heroForm.submitted">
|
||||
<div class="cross-validation" [class.cross-validation-error]="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)">
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion name-input -->
|
||||
|
||||
<div class="form-group">
|
||||
<label for="name">Name</label>
|
||||
<!-- #docregion name-with-error-msg -->
|
||||
<!-- #docregion name-input -->
|
||||
<input id="name" name="name" class="form-control"
|
||||
required minlength="4" appForbiddenName="bob"
|
||||
[(ngModel)]="hero.name" #name="ngModel" >
|
||||
<!-- #enddocregion name-input -->
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
|
||||
<div *ngIf="name.invalid && (name.dirty || name.touched)"
|
||||
class="alert alert-danger">
|
||||
<div *ngIf="name.errors.required">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="name.errors.minlength">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div *ngIf="name.errors.forbiddenName">
|
||||
Name cannot be Bob.
|
||||
</div>
|
||||
|
||||
<div *ngIf="name.errors.required">
|
||||
Name is required.
|
||||
</div>
|
||||
<div *ngIf="name.errors.minlength">
|
||||
Name must be at least 4 characters long.
|
||||
</div>
|
||||
<div *ngIf="name.errors.forbiddenName">
|
||||
Name cannot be Bob.
|
||||
</div>
|
||||
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
<!-- #enddocregion name-with-error-msg -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||
<div class="form-group">
|
||||
<label for="alterEgo">Alter Ego</label>
|
||||
<input id="alterEgo" class="form-control"
|
||||
name="alterEgo" [(ngModel)]="hero.alterEgo" >
|
||||
</div>
|
||||
|
||||
<!-- #docregion cross-validation-error-message -->
|
||||
<div *ngIf="heroForm.errors?.identityRevealed && (heroForm.touched || heroForm.dirty)" class="cross-validation-error-message alert alert-danger">
|
||||
Name cannot match alter ego.
|
||||
</div>
|
||||
<!-- #enddocregion cross-validation-error-message -->
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
@ -62,5 +69,4 @@
|
||||
<button (click)="heroForm.resetForm({})">Add new hero</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -3,9 +3,11 @@
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #docregion component
|
||||
@Component({
|
||||
selector: 'app-hero-form-template',
|
||||
templateUrl: './hero-form-template.component.html'
|
||||
templateUrl: './hero-form-template.component.html',
|
||||
styleUrls: ['./hero-form-template.component.css'],
|
||||
})
|
||||
export class HeroFormTemplateComponent {
|
||||
|
||||
@ -14,3 +16,4 @@ export class HeroFormTemplateComponent {
|
||||
hero = {name: 'Dr.', alterEgo: 'Dr. What', power: this.powers[0]};
|
||||
|
||||
}
|
||||
// #enddocregion
|
||||
|
@ -2,6 +2,7 @@
|
||||
"description": "Validation",
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js"
|
||||
"!**/*.js",
|
||||
"!**/*.[1].*"
|
||||
]
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ button {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
code, .code {
|
||||
code,
|
||||
.code {
|
||||
background-color: #eee;
|
||||
color: black;
|
||||
font-family: Courier, sans-serif;
|
||||
@ -21,14 +22,18 @@ div.code {
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 40px 0
|
||||
margin: 40px 0;
|
||||
}
|
||||
|
||||
td, th {
|
||||
td,
|
||||
th {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* #docregion p-span */
|
||||
p span { color: red; font-size: 70%; }
|
||||
p span {
|
||||
color: red;
|
||||
font-size: 70%;
|
||||
}
|
||||
/* #enddocregion p-span */
|
||||
|
@ -132,7 +132,7 @@
|
||||
<!-- #docregion select-span -->
|
||||
<select [(ngModel)]="hero">
|
||||
<span *ngFor="let h of heroes">
|
||||
<span *ngIf="showSad || h?.emotion != 'sad'">
|
||||
<span *ngIf="showSad || h?.emotion !== 'sad'">
|
||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||
</span>
|
||||
</span>
|
||||
@ -147,7 +147,7 @@
|
||||
<!-- #docregion select-ngcontainer -->
|
||||
<select [(ngModel)]="hero">
|
||||
<ng-container *ngFor="let h of heroes">
|
||||
<ng-container *ngIf="showSad || h?.emotion != 'sad'">
|
||||
<ng-container *ngIf="showSad || h?.emotion !== 'sad'">
|
||||
<option [ngValue]="h">{{h.name}} ({{h?.emotion}})</option>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
@ -6,14 +6,15 @@ import { heroes } from './hero';
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: [ './app.component.css' ]
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
heroes = heroes;
|
||||
hero = this.heroes[0];
|
||||
heroTraits = [ 'honest', 'brave', 'considerate' ];
|
||||
heroTraits = ['honest', 'brave', 'considerate'];
|
||||
|
||||
// flags for the table
|
||||
|
||||
attrDirs = true;
|
||||
strucDirs = true;
|
||||
divNgIf = false;
|
||||
|
@ -1,9 +1,9 @@
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { AppComponent } from './app.component';
|
||||
import { ContentComponent } from './content.component';
|
||||
import { heroComponents } from './hero.components';
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
// #docregion
|
||||
import { Component, Input } from '@angular/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
@Component({
|
||||
@ -33,11 +34,15 @@ export class ConfusedHeroComponent {
|
||||
export class UnknownHeroComponent {
|
||||
@Input() hero: Hero;
|
||||
get message() {
|
||||
return this.hero && this.hero.name ?
|
||||
`${this.hero.name} is strange and mysterious.` :
|
||||
'Are you feeling indecisive?';
|
||||
return this.hero && this.hero.name
|
||||
? `${this.hero.name} is strange and mysterious.`
|
||||
: 'Are you feeling indecisive?';
|
||||
}
|
||||
}
|
||||
|
||||
export const heroComponents =
|
||||
[ HappyHeroComponent, SadHeroComponent, ConfusedHeroComponent, UnknownHeroComponent ];
|
||||
export const heroComponents = [
|
||||
HappyHeroComponent,
|
||||
SadHeroComponent,
|
||||
ConfusedHeroComponent,
|
||||
UnknownHeroComponent
|
||||
];
|
||||
|
@ -6,8 +6,8 @@ export class Hero {
|
||||
}
|
||||
|
||||
export const heroes: Hero[] = [
|
||||
{ id: 1, name: 'Mr. Nice', emotion: 'happy'},
|
||||
{ id: 2, name: 'Narco', emotion: 'sad' },
|
||||
{ id: 1, name: 'Mr. Nice', emotion: 'happy' },
|
||||
{ id: 2, name: 'Narco', emotion: 'sad' },
|
||||
{ id: 3, name: 'Windstorm', emotion: 'confused' },
|
||||
{ id: 4, name: 'Magneta'}
|
||||
{ id: 4, name: 'Magneta' }
|
||||
];
|
||||
|
@ -15,6 +15,7 @@ export class ComposeMessageComponent {
|
||||
@HostBinding('style.position') position = 'absolute';
|
||||
|
||||
details: string;
|
||||
message: string;
|
||||
sending = false;
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
@ -3,15 +3,15 @@
|
||||
<!-- #enddocregion show-hero-1 -->
|
||||
|
||||
<!-- #docregion show-hero-2 -->
|
||||
<h2>{{ hero.name }} Details</h2>
|
||||
<h2>{{hero.name}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div><span>name: </span>{{hero.name}}</div>
|
||||
<!-- #enddocregion show-hero-2 -->
|
||||
|
||||
<!-- #docregion name-input -->
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
</div>
|
||||
<!-- #enddocregion name-input -->
|
||||
|
@ -1,10 +1,10 @@
|
||||
<!-- #docregion -->
|
||||
<!-- #docregion pipe -->
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<!-- #enddocregion pipe -->
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name">
|
||||
</label>
|
||||
</div>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<div *ngIf="selectedHero">
|
||||
|
||||
<!-- #docregion selectedHero-details -->
|
||||
<h2>{{ selectedHero.name | uppercase }} Details</h2>
|
||||
<h2>{{selectedHero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{selectedHero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div *ngIf="hero">
|
||||
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -18,11 +18,11 @@ nav a {
|
||||
border-radius: 4px;
|
||||
}
|
||||
nav a:visited, a:link {
|
||||
color: #607D8B;
|
||||
color: #607d8b;
|
||||
}
|
||||
nav a:hover {
|
||||
color: #039be5;
|
||||
background-color: #CFD8DC;
|
||||
background-color: #cfd8dc;
|
||||
}
|
||||
nav a.active {
|
||||
color: #039be5;
|
||||
|
@ -33,11 +33,11 @@ h4 {
|
||||
color: #eee;
|
||||
max-height: 120px;
|
||||
min-width: 120px;
|
||||
background-color: #607D8B;
|
||||
background-color: #607d8b;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.module:hover {
|
||||
background-color: #EEE;
|
||||
background-color: #eee;
|
||||
cursor: pointer;
|
||||
color: #607d8b;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,16 +1,36 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { HeroSearchComponent } from '../hero-search/hero-search.component';
|
||||
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { HEROES } from '../mock-heroes';
|
||||
import { HeroService } from '../hero.service';
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent;
|
||||
let fixture: ComponentFixture<DashboardComponent>;
|
||||
let heroService;
|
||||
let getHeroesSpy;
|
||||
|
||||
beforeEach(async(() => {
|
||||
heroService = jasmine.createSpyObj('HeroService', ['getHeroes']);
|
||||
getHeroesSpy = heroService.getHeroes.and.returnValue( of(HEROES) );
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardComponent ]
|
||||
declarations: [
|
||||
DashboardComponent,
|
||||
HeroSearchComponent
|
||||
],
|
||||
imports: [
|
||||
RouterTestingModule.withRoutes([])
|
||||
],
|
||||
providers: [
|
||||
{ provide: HeroService, useValue: heroService }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
@ -22,4 +42,17 @@ describe('DashboardComponent', () => {
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should display "Top Heroes" as headline', () => {
|
||||
expect(fixture.nativeElement.querySelector('h3').textContent).toEqual('Top Heroes');
|
||||
});
|
||||
|
||||
it('should call heroService', async(() => {
|
||||
expect(getHeroesSpy.calls.any()).toBe(true);
|
||||
}));
|
||||
|
||||
it('should display 4 links', async(() => {
|
||||
expect(fixture.nativeElement.querySelectorAll('a').length).toEqual(4);
|
||||
}));
|
||||
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<h2>{{hero.name | uppercase}} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
|
@ -1,14 +1,18 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
|
||||
import { HeroSearchComponent } from './hero-search.component';
|
||||
|
||||
|
||||
describe('HeroSearchComponent', () => {
|
||||
let component: HeroSearchComponent;
|
||||
let fixture: ComponentFixture<HeroSearchComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HeroSearchComponent ]
|
||||
declarations: [ HeroSearchComponent ],
|
||||
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -40,7 +40,7 @@ export class HeroService {
|
||||
// #enddocregion getHeroes-1
|
||||
.pipe(
|
||||
// #enddocregion getHeroes-2
|
||||
tap(heroes => this.log(`fetched heroes`)),
|
||||
tap(heroes => this.log('fetched heroes')),
|
||||
// #docregion getHeroes-2
|
||||
catchError(this.handleError('getHeroes', []))
|
||||
);
|
||||
@ -151,7 +151,7 @@ export class HeroService {
|
||||
// #docregion log
|
||||
/** Log a HeroService message with the MessageService */
|
||||
private log(message: string) {
|
||||
this.messageService.add('HeroService: ' + message);
|
||||
this.messageService.add(`HeroService: ${message}`);
|
||||
}
|
||||
// #enddocregion log
|
||||
}
|
||||
|
@ -20,7 +20,7 @@
|
||||
</a>
|
||||
<!-- #docregion delete -->
|
||||
<button class="delete" title="delete hero"
|
||||
(click)="delete(hero)">x</button>
|
||||
(click)="delete(hero)">x</button>
|
||||
<!-- #enddocregion delete -->
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -1,6 +1,7 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
|
||||
describe('HeroesComponent', () => {
|
||||
let component: HeroesComponent;
|
||||
@ -8,7 +9,8 @@ describe('HeroesComponent', () => {
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HeroesComponent ]
|
||||
declarations: [ HeroesComponent ],
|
||||
imports: [RouterTestingModule.withRoutes([]), HttpClientTestingModule],
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
@ -30,7 +30,7 @@ export class HeroService {
|
||||
getHeroes (): Observable<Hero[]> {
|
||||
return this.http.get<Hero[]>(this.heroesUrl)
|
||||
.pipe(
|
||||
tap(heroes => this.log(`fetched heroes`)),
|
||||
tap(heroes => this.log('fetched heroes')),
|
||||
catchError(this.handleError('getHeroes', []))
|
||||
);
|
||||
}
|
||||
@ -123,6 +123,6 @@ export class HeroService {
|
||||
|
||||
/** Log a HeroService message with the MessageService */
|
||||
private log(message: string) {
|
||||
this.messageService.add('HeroService: ' + message);
|
||||
this.messageService.add(`HeroService: ${message}`);
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@
|
||||
"experimentalDecorators": true,
|
||||
"removeComments": false,
|
||||
"noImplicitAny": false,
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
// #docregion
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
|
||||
import { AppModuleNgFactory } from '../aot/app/app.module.ngfactory';
|
||||
import { AppModuleNgFactory } from './app.module.ngfactory';
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// #enddocregion activatedroute
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
|
||||
@ -21,7 +21,7 @@ function xyzPhoneData(): PhoneData {
|
||||
|
||||
class MockPhone {
|
||||
get(id: string): Observable<PhoneData> {
|
||||
return Observable.of(xyzPhoneData());
|
||||
return of(xyzPhoneData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// #docregion
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
|
||||
@ -15,7 +15,7 @@ class ActivatedRouteMock {
|
||||
|
||||
class MockPhone {
|
||||
query(): Observable<PhoneData[]> {
|
||||
return Observable.of([
|
||||
return of([
|
||||
{name: 'Nexus S', snippet: '', images: []},
|
||||
{name: 'Motorola DROID', snippet: '', images: []}
|
||||
]);
|
||||
|
@ -9,10 +9,8 @@
|
||||
"lib": ["es2015", "dom"],
|
||||
"removeComments": false,
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
|
||||
"files": [
|
||||
@ -21,7 +19,6 @@
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "aot",
|
||||
"skipMetadataEmit" : true
|
||||
"skipMetadataEmit" : true
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -3,7 +3,7 @@
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
|
||||
// #enddocregion activatedroute
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
|
||||
import { async, TestBed } from '@angular/core/testing';
|
||||
|
||||
@ -21,7 +21,7 @@ function xyzPhoneData(): PhoneData {
|
||||
|
||||
class MockPhone {
|
||||
get(id: string): Observable<PhoneData> {
|
||||
return Observable.of(xyzPhoneData());
|
||||
return of(xyzPhoneData());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
// #docregion routestuff
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Observable } from 'rxjs';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SpyLocation } from '@angular/common/testing';
|
||||
|
||||
@ -17,7 +17,7 @@ class ActivatedRouteMock {
|
||||
|
||||
class MockPhone {
|
||||
query(): Observable<PhoneData[]> {
|
||||
return Observable.of([
|
||||
return of([
|
||||
{name: 'Nexus S', snippet: '', images: []},
|
||||
{name: 'Motorola DROID', snippet: '', images: []}
|
||||
]);
|
||||
|
@ -8,10 +8,8 @@
|
||||
"experimentalDecorators": true,
|
||||
"lib": [ "es2015", "dom" ],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
"skipLibCheck": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
},
|
||||
"compileOnSave": true,
|
||||
"exclude": [
|
||||
|
@ -1,12 +0,0 @@
|
||||
// #docregion
|
||||
var path = require('path');
|
||||
|
||||
var _root = path.resolve(__dirname, '..');
|
||||
|
||||
function root(args) {
|
||||
args = Array.prototype.slice.call(arguments, 0);
|
||||
return path.join.apply(path, [_root].concat(args));
|
||||
}
|
||||
|
||||
exports.root = root;
|
||||
// #enddocregion
|
@ -1,17 +0,0 @@
|
||||
// #docregion
|
||||
Error.stackTraceLimit = Infinity;
|
||||
|
||||
require('core-js/es6');
|
||||
require('core-js/es7/reflect');
|
||||
|
||||
require('zone.js/dist/zone');
|
||||
require('zone.js/dist/zone-testing');
|
||||
|
||||
var appContext = require.context('../src', true, /\.spec\.ts/);
|
||||
|
||||
appContext.keys().forEach(appContext);
|
||||
|
||||
var testing = require('@angular/core/testing');
|
||||
var browser = require('@angular/platform-browser-dynamic/testing');
|
||||
|
||||
testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule, browser.platformBrowserDynamicTesting());
|
@ -1,39 +0,0 @@
|
||||
// #docregion
|
||||
var webpackConfig = require('./webpack.test');
|
||||
|
||||
module.exports = function (config) {
|
||||
var _config = {
|
||||
basePath: '',
|
||||
|
||||
frameworks: ['jasmine'],
|
||||
|
||||
files: [
|
||||
{pattern: './config/karma-test-shim.js', watched: false}
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'./config/karma-test-shim.js': ['webpack', 'sourcemap']
|
||||
},
|
||||
|
||||
webpack: webpackConfig,
|
||||
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only'
|
||||
},
|
||||
|
||||
webpackServer: {
|
||||
noInfo: true
|
||||
},
|
||||
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: false,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: true
|
||||
};
|
||||
|
||||
config.set(_config);
|
||||
};
|
||||
// #enddocregion
|
@ -1,81 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
var webpack = require('webpack');
|
||||
var HtmlWebpackPlugin = require('html-webpack-plugin');
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = {
|
||||
// #docregion entries, one-entry, two-entries
|
||||
entry: {
|
||||
// #enddocregion one-entry, two-entries
|
||||
'polyfills': './src/polyfills.ts',
|
||||
// #docregion two-entries
|
||||
'vendor': './src/vendor.ts',
|
||||
// #docregion one-entry
|
||||
'app': './src/main.ts'
|
||||
},
|
||||
// #enddocregion entries, one-entry, two-entries
|
||||
|
||||
// #docregion resolve
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
// #enddocregion resolve
|
||||
|
||||
// #docregion loaders
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loaders: [
|
||||
{
|
||||
loader: 'awesome-typescript-loader',
|
||||
options: { configFileName: helpers.root('src', 'tsconfig.json') }
|
||||
} , 'angular2-template-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader'
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||
loader: 'file-loader?name=assets/[name].[hash].[ext]'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: helpers.root('src', 'app'),
|
||||
loader: ExtractTextPlugin.extract({ fallbackLoader: 'style-loader', loader: 'css-loader?sourceMap' })
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: helpers.root('src', 'app'),
|
||||
loader: 'raw-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
// #enddocregion loaders
|
||||
|
||||
// #docregion plugins
|
||||
plugins: [
|
||||
// Workaround for angular/angular#11580
|
||||
new webpack.ContextReplacementPlugin(
|
||||
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||
/angular(\\|\/)core(\\|\/)@angular/,
|
||||
helpers.root('./src'), // location of your src
|
||||
{} // a map of your routes
|
||||
),
|
||||
|
||||
new webpack.optimize.CommonsChunkPlugin({
|
||||
name: ['app', 'vendor', 'polyfills']
|
||||
}),
|
||||
|
||||
new HtmlWebpackPlugin({
|
||||
template: 'src/index.html'
|
||||
})
|
||||
]
|
||||
// #enddocregion plugins
|
||||
};
|
||||
// #enddocregion
|
||||
|
@ -1,26 +0,0 @@
|
||||
// #docregion
|
||||
var webpackMerge = require('webpack-merge');
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
var commonConfig = require('./webpack.common.js');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = webpackMerge(commonConfig, {
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
|
||||
output: {
|
||||
path: helpers.root('dist'),
|
||||
publicPath: '/',
|
||||
filename: '[name].js',
|
||||
chunkFilename: '[id].chunk.js'
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new ExtractTextPlugin('[name].css')
|
||||
],
|
||||
|
||||
devServer: {
|
||||
historyApiFallback: true,
|
||||
stats: 'minimal'
|
||||
}
|
||||
});
|
||||
// #enddocregion
|
@ -1,41 +0,0 @@
|
||||
// #docregion
|
||||
var webpack = require('webpack');
|
||||
var webpackMerge = require('webpack-merge');
|
||||
var ExtractTextPlugin = require('extract-text-webpack-plugin');
|
||||
var commonConfig = require('./webpack.common.js');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
const ENV = process.env.NODE_ENV = process.env.ENV = 'production';
|
||||
|
||||
module.exports = webpackMerge(commonConfig, {
|
||||
devtool: 'source-map',
|
||||
|
||||
output: {
|
||||
path: helpers.root('dist'),
|
||||
publicPath: '/',
|
||||
filename: '[name].[hash].js',
|
||||
chunkFilename: '[id].[hash].chunk.js'
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.NoEmitOnErrorsPlugin(),
|
||||
new webpack.optimize.UglifyJsPlugin({ // https://github.com/angular/angular/issues/10618
|
||||
mangle: {
|
||||
keep_fnames: true
|
||||
}
|
||||
}),
|
||||
new ExtractTextPlugin('[name].[hash].css'),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
'ENV': JSON.stringify(ENV)
|
||||
}
|
||||
}),
|
||||
new webpack.LoaderOptionsPlugin({
|
||||
htmlLoader: {
|
||||
minimize: false // workaround for ng2
|
||||
}
|
||||
})
|
||||
]
|
||||
});
|
||||
|
||||
// #enddocregion
|
@ -1,55 +0,0 @@
|
||||
// #docregion
|
||||
var webpack = require('webpack');
|
||||
var helpers = require('./helpers');
|
||||
|
||||
module.exports = {
|
||||
devtool: 'inline-source-map',
|
||||
|
||||
resolve: {
|
||||
extensions: ['.ts', '.js']
|
||||
},
|
||||
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loaders: [
|
||||
{
|
||||
loader: 'awesome-typescript-loader',
|
||||
options: { configFileName: helpers.root('src', 'tsconfig.json') }
|
||||
} , 'angular2-template-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.html$/,
|
||||
loader: 'html-loader'
|
||||
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
|
||||
loader: 'null-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
exclude: helpers.root('src', 'app'),
|
||||
loader: 'null-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
include: helpers.root('src', 'app'),
|
||||
loader: 'raw-loader'
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
plugins: [
|
||||
new webpack.ContextReplacementPlugin(
|
||||
// The (\\|\/) piece accounts for path separators in *nix and Windows
|
||||
/angular(\\|\/)core(\\|\/)@angular/,
|
||||
helpers.root('./src'), // location of your src
|
||||
{} // a map of your routes
|
||||
)
|
||||
]
|
||||
}
|
||||
|
||||
// #enddocregion
|
@ -1,21 +0,0 @@
|
||||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
|
||||
describe('QuickStart E2E Tests', function () {
|
||||
|
||||
let expectedMsg = 'Hello from Angular App with Webpack';
|
||||
|
||||
beforeEach(function () {
|
||||
browser.get('');
|
||||
});
|
||||
|
||||
it(`should display: ${expectedMsg}`, function () {
|
||||
expect(element(by.css('h1')).getText()).toEqual(expectedMsg);
|
||||
});
|
||||
|
||||
it('should display an image', function () {
|
||||
expect(element(by.css('img')).isPresent()).toBe(true);
|
||||
});
|
||||
|
||||
});
|
@ -1,5 +0,0 @@
|
||||
{
|
||||
"build": "build:webpack",
|
||||
"run": "serve:cli",
|
||||
"projectType": "systemjs"
|
||||
}
|
@ -1,2 +0,0 @@
|
||||
// #docregion
|
||||
module.exports = require('./config/karma.conf.js');
|
@ -1,49 +0,0 @@
|
||||
{
|
||||
"name": "angular2-webpack",
|
||||
"version": "1.0.0",
|
||||
"description": "A webpack starter for Angular",
|
||||
"scripts": {
|
||||
"start": "webpack-dev-server --inline --progress --port 8080",
|
||||
"test": "karma start",
|
||||
"build": "rimraf dist && webpack --config config/webpack.prod.js --progress --profile --bail"
|
||||
},
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/common": "~4.2.0",
|
||||
"@angular/compiler": "~4.2.0",
|
||||
"@angular/core": "~4.2.0",
|
||||
"@angular/forms": "~4.2.0",
|
||||
"@angular/http": "~4.2.0",
|
||||
"@angular/platform-browser": "~4.2.0",
|
||||
"@angular/platform-browser-dynamic": "~4.2.0",
|
||||
"@angular/router": "~4.2.0",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "5.0.1",
|
||||
"zone.js": "^0.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^6.0.45",
|
||||
"@types/jasmine": "2.5.36",
|
||||
"angular2-template-loader": "^0.6.0",
|
||||
"awesome-typescript-loader": "^3.0.4",
|
||||
"css-loader": "^0.26.1",
|
||||
"extract-text-webpack-plugin": "2.0.0-beta.5",
|
||||
"file-loader": "^0.9.0",
|
||||
"html-loader": "^0.4.3",
|
||||
"html-webpack-plugin": "^2.16.1",
|
||||
"jasmine-core": "^2.4.1",
|
||||
"karma": "^1.2.0",
|
||||
"karma-chrome-launcher": "^2.0.0",
|
||||
"karma-jasmine": "^1.0.2",
|
||||
"karma-sourcemap-loader": "^0.3.7",
|
||||
"karma-webpack": "^2.0.1",
|
||||
"null-loader": "^0.1.1",
|
||||
"raw-loader": "^0.5.1",
|
||||
"rimraf": "^2.5.2",
|
||||
"style-loader": "^0.13.1",
|
||||
"typescript": "~2.3.1",
|
||||
"webpack": "2.2.1",
|
||||
"webpack-dev-server": "2.4.1",
|
||||
"webpack-merge": "^3.0.0"
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
/* #docregion */
|
||||
main {
|
||||
padding: 1em;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
display: block;
|
||||
}
|
||||
/* #enddocregion */
|
@ -1,7 +0,0 @@
|
||||
<!-- #docregion -->
|
||||
<main>
|
||||
<h1>Hello from Angular App with Webpack</h1>
|
||||
|
||||
<img src="../assets/images/angular.png">
|
||||
</main>
|
||||
<!-- #enddocregion -->
|
@ -1,16 +0,0 @@
|
||||
// #docregion
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
describe('App', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({ declarations: [AppComponent]});
|
||||
});
|
||||
|
||||
it ('should work', () => {
|
||||
let fixture = TestBed.createComponent(AppComponent);
|
||||
expect(fixture.componentInstance instanceof AppComponent).toBe(true, 'should create AppComponent');
|
||||
});
|
||||
});
|
||||
// #enddocregion
|
@ -1,16 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion component
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
// #enddocregion component
|
||||
import '../assets/css/styles.css';
|
||||
|
||||
// #docregion component
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent { }
|
||||
// #enddocregion
|
@ -1,16 +0,0 @@
|
||||
// #docregion
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
@ -1,6 +0,0 @@
|
||||
/* #docregion */
|
||||
body {
|
||||
background: #0147A7;
|
||||
color: #fff;
|
||||
}
|
||||
/* #enddocregion */
|
Binary file not shown.
Before Width: | Height: | Size: 2.3 KiB |
@ -1,14 +0,0 @@
|
||||
<!-- #docregion -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>Angular With Webpack</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
</html>
|
||||
<!-- #enddocregion -->
|
@ -1,14 +0,0 @@
|
||||
// #docregion
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { enableProdMode } from '@angular/core';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
|
||||
// #docregion enable-prod
|
||||
if (process.env.ENV === 'production') {
|
||||
enableProdMode();
|
||||
}
|
||||
// #enddocregion enable-prod
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
// #enddocregion
|
@ -1,12 +0,0 @@
|
||||
// #docregion
|
||||
import 'core-js/es6';
|
||||
import 'core-js/es7/reflect';
|
||||
require('zone.js/dist/zone');
|
||||
|
||||
if (process.env.ENV === 'production') {
|
||||
// Production
|
||||
} else {
|
||||
// Development and test
|
||||
Error['stackTraceLimit'] = Infinity;
|
||||
require('zone.js/dist/long-stack-trace-zone');
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2015", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true
|
||||
}
|
||||
}
|
@ -1,18 +0,0 @@
|
||||
// TODO(i): this no longer works. we need to review this example and if absolutely necessary rewrite it to use the
|
||||
// rxjs-compat package
|
||||
|
||||
// #docregion
|
||||
// Angular
|
||||
import '@angular/platform-browser';
|
||||
import '@angular/platform-browser-dynamic';
|
||||
import '@angular/core';
|
||||
import '@angular/common';
|
||||
import '@angular/http';
|
||||
import '@angular/router';
|
||||
|
||||
// RxJS
|
||||
import 'rxjs';
|
||||
|
||||
// Other vendors for example jQuery, Lodash or Bootstrap
|
||||
// You can import js, ts, css, sass, ...
|
||||
// #enddocregion
|
@ -1,3 +0,0 @@
|
||||
// #docregion
|
||||
module.exports = require('./config/webpack.dev.js');
|
||||
// #enddocregion
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"files":[
|
||||
"!**/*.d.ts",
|
||||
"!**/*.js",
|
||||
"!**/*.[0-9].*",
|
||||
"config/**/*",
|
||||
"webpack.config.js",
|
||||
"karma.webpack.conf.js"
|
||||
],
|
||||
"removeSystemJsConfig": true,
|
||||
"type": "webpack"
|
||||
}
|
@ -895,7 +895,7 @@ For more information on pipes, see [Pipes](guide/pipes).
|
||||
### lowercase
|
||||
|
||||
<code-example hideCopy>
|
||||
<div>{{movie.title | lowercase}}</div>
|
||||
<td>{{movie.title | lowercase}}</td>
|
||||
</code-example>
|
||||
|
||||
|
||||
|
@ -97,6 +97,9 @@ You can control your app compilation by providing template compiler options in t
|
||||
}
|
||||
}
|
||||
```
|
||||
### *enableResourceInlining*
|
||||
This options tell the compiler to replace the `templateUrl` and `styleUrls` property in all `@Component` decorators with inlined contents in `template` and `styles` properties.
|
||||
When enabled, the `.js` output of ngc will have no lazy-loaded `templateUrl` or `styleUrls`.
|
||||
|
||||
### *skipMetadataEmit*
|
||||
|
||||
@ -236,14 +239,14 @@ Tells the compiler to generate all the possible generated files even if they are
|
||||
how `bazel` rules track file dependencies. It is not recommended to use this option outside of the `bazel`
|
||||
rules.
|
||||
|
||||
### *enableIvy*
|
||||
### *enableIvy*
|
||||
|
||||
Tells the compiler to generate definitions using the Render3 style code generation. This option defaults to `false`.
|
||||
Tells the compiler to generate definitions using the Render3 style code generation. This option defaults to `false`.
|
||||
|
||||
Not all features are supported with this option enabled. It is only supported
|
||||
for experimentation and testing of Render3 style code generation.
|
||||
Not all features are supported with this option enabled. It is only supported
|
||||
for experimentation and testing of Render3 style code generation.
|
||||
|
||||
*Note*: Is it not recommended to use this option as it is not yet feature complete with the Render2 code generation.
|
||||
*Note*: Is it not recommended to use this option as it is not yet feature complete with the Render2 code generation.
|
||||
|
||||
|
||||
## Angular Metadata and AOT
|
||||
|
@ -1,175 +0,0 @@
|
||||
<div class="breadcrumb">
|
||||
<a href="#">API<a> / <a href="#">@core<a>
|
||||
</div>
|
||||
<header class="api-header">
|
||||
<h1><label class="api-status-label experimental">experimental</label><label class="api-type-label class">class</label>Class Name</h1>
|
||||
</header>
|
||||
<div class="page-actions">
|
||||
<a href="#"><label class="raised page-label"><i class="material-icons">mode_edit</i>suggest edits</label></a>
|
||||
<a href="#"><label class="raised page-label"><i class="material-icons">code</i>view source</label></a>
|
||||
</div>
|
||||
<p>Class description goes here. This is a short and to the point one or two sentence description that easily introduces the reader to the class.</p>
|
||||
<div class="api-body">
|
||||
<section>
|
||||
<h2>Overview</h2>
|
||||
<code-example language="ts" hidecopy="true" ng-version="5.2.0"><aio-code class="simple-code" ng-reflect-ng-class="[object Object]" ng-reflect-code="
|
||||
class <a href="api/core/Compi" ng-reflect-hide-copy="true" ng-reflect-language="ts" ng-reflect-linenums="" ng-reflect-path="" ng-reflect-region="" ng-reflect-title=""><pre class="prettyprint lang-ts">
|
||||
<code class="animated fadeIn"><span class="kwd">class</span><span class="pln"> </span><a href="api/core/Compiler" class="code-anchor"><span class="typ">Compiler</span></a><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleSync"><span class="pln">compileModuleSync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">NgModuleFactory</span><span class="pun"><</span><span class="pln">T</span><span class="pun">></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleAsync"><span class="pln">compileModuleAsync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun"><</span><span class="typ">NgModuleFactory</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleAndAllComponentsSync"><span class="pln">compileModuleAndAllComponentsSync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">ModuleWithComponentFactories</span><span class="pun"><</span><span class="pln">T</span><span class="pun">></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#compileModuleAndAllComponentsAsync"><span class="pln">compileModuleAndAllComponentsAsync</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>(</span><span class="pln">moduleType</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>):</span><span class="pln"> </span><span class="typ">Promise</span><span class="pun"><</span><span class="typ">ModuleWithComponentFactories</span><span class="pun"><</span><span class="pln">T</span><span class="pun">>></span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#clearCache"><span class="pln">clearCache</span><span class="pun">():</span><span class="pln"> </span><span class="kwd">void</span></a><span class="pln">
|
||||
</span><a class="code-anchor" href="api/core/Compiler#clearCacheFor"><span class="pln">clearCacheFor</span><span class="pun">(</span><span class="pln">type</span><span class="pun">:</span><span class="pln"> </span><span class="typ">Type</span><span class="pun"><</span><span class="pln">any</span><span class="pun">>)</span></a><span class="pln">
|
||||
</span><span class="pun">}</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Description</h2>
|
||||
<p>The longer class description goes here which can include multiple paragraphs.</p>
|
||||
</p>Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball. Ham hock picanha burgdoggen pork belly rump bacon cupim. Bacon kielbasa sirloin shank strip steak ground round. Bresaola cow salami meatloaf pork chop leberkas flank turducken biltong meatball chuck pork tri-tip chicken. Ribeye corned beef shoulder, meatloaf strip steak jerky porchetta capicola alcatra ham.</p>
|
||||
<h3>Subclasses</h3>
|
||||
<ul>
|
||||
<li><a href="#">Subclass1</a></li>
|
||||
<li><a href="#">Subclass2</a></li>
|
||||
<li><a href="#">Subclass3</a></li>
|
||||
</ul>
|
||||
<h3>See Also</h3>
|
||||
<ul>
|
||||
<li><a href="#">Link1</a></li>
|
||||
<li><a href="#">Link2</a></li>
|
||||
</ul>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Constructor</h2>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Properties</h2>
|
||||
<table class="is-full-width list-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Property</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code><strong>Property1</strong></code>
|
||||
</td>
|
||||
<td><label class="property-type-label type">Type</label></td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code><strong>Property2</strong></code>
|
||||
</td>
|
||||
<td>Type</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code><strong>Property3</strong></code>
|
||||
</td>
|
||||
<td>Type</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section class="api-method">
|
||||
<h2>Methods</h2>
|
||||
<table class="is-full-width item-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method1Name( )</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p>Description goes here</p>
|
||||
<br>
|
||||
<p>Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball. Ham hock picanha burgdoggen pork belly rump bacon cupim. Bacon kielbasa sirloin shank strip steak ground round. Bresaola cow salami meatloaf pork chop leberkas flank turducken biltong meatball chuck pork tri-tip chicken. Ribeye corned beef shoulder, meatloaf strip steak jerky porchetta capicola alcatra ham.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="is-full-width api-method item-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Method2Name( )</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p>Description goes here</p>
|
||||
<hr>
|
||||
<h5>Declaration</h5>
|
||||
<code-example language="ts" hidecopy="true" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-ts">
|
||||
<code class="animated fadeIn"><span class="kwd">class</span><span class="pln"> </span><a href="api/animations/AnimationBuilder" class="code-anchor"><span class="typ">AnimationBuilder</span></a><span class="pln"> </span><span class="pun">{</span><span class="pln"></span><a class="code-anchor" href="api/animations/AnimationBuilder#build"><span class="pln">build</span><span class="pun">(</span><span class="pln">animation</span><span class="pun">:</span><span class="pln"> </span><span class="typ">AnimationMetadata</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> </span><span class="typ">AnimationMetadata</span><span class="pun">[]):</span><span class="pln"> </span><span class="typ">AnimationFactory</span></a><span class="pln"></span><span class="pun">}</span></code></pre>
|
||||
</aio-code>
|
||||
</code-example>
|
||||
<h6>Parameters</h6>
|
||||
<h6>Returns</h6>
|
||||
<p>Returns information and results goes here.</p>
|
||||
<h6>Errors</h6>
|
||||
<p>Error information goes here</p>
|
||||
<hr>
|
||||
<p>Further details provided as needed. Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball.</p><hr>
|
||||
<h6>Overloads</h6>
|
||||
<table class="is-full-width">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
</td>
|
||||
<td>Description goes here</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<hr>
|
||||
<h5>Example: Descriptive Title of Method Example</h5>
|
||||
<p>Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball. Ham hock picanha burgdoggen pork belly rump bacon cupim. Bacon kielbasa sirloin shank strip steak ground round. Bresaola cow salami meatloaf pork chop leberkas flank turducken biltong meatball chuck pork tri-tip chicken. Ribeye corned beef shoulder, meatloaf strip steak jerky porchetta capicola alcatra ham.</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section>
|
||||
<h2>Example: Descriptive Title of Combined Example Goes Here</h2>
|
||||
<p>Intro description text about what the example is and how it can be used.</p>
|
||||
<code-example hidecopy="true" class="no-box api-heading" ng-version="5.2.0">
|
||||
<aio-code class="simple-code"><pre class="prettyprint lang-">
|
||||
<code class="animated fadeIn"><span class="kwd">constructor</span><span class="pun">(</span><span class="pln">element</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">,</span><span class="pln"> keyframes</span><span class="pun">:</span><span class="pln"> </span><span class="pun">{</span><span class="pln">
|
||||
</span><span class="pun">[</span><span class="pln">key</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">]:</span><span class="pln"> </span><span class="kwd">string</span><span class="pln"> </span><span class="pun">|</span><span class="pln"> number</span><span class="pun">;</span><span class="pln">
|
||||
</span><span class="pun">}[],</span><span class="pln"> duration</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> delay</span><span class="pun">:</span><span class="pln"> number</span><span class="pun">,</span><span class="pln"> easing</span><span class="pun">:</span><span class="pln"> </span><span class="kwd">string</span><span class="pun">,</span><span class="pln"> previousPlayers</span><span class="pun">:</span><span class="pln"> any</span><span class="pun">[])</span></code>
|
||||
</pre></aio-code></code-example>
|
||||
<p>Further explanation provided as needed. Bacon ipsum dolor amet pork belly capicola sirloin venison alcatra ground round ham hock jowl turkey picanha bresaola pancetta brisket chicken fatback. Burgdoggen kevin salami jowl shoulder jerky leberkas meatball.</p>
|
||||
</section>
|
||||
</div>
|
@ -8,9 +8,10 @@ A _component_ controls a patch of screen called a *view*. For example, individua
|
||||
* The list of heroes.
|
||||
* The hero editor.
|
||||
|
||||
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.
|
||||
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 returns an array of heroes that it acquires from a service. `HeroListComponent` also has a `selectHero()` method that sets a `selectedHero` property when the user clicks to choose a hero from that list.
|
||||
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>
|
||||
|
||||
@ -39,8 +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 that the component's constructor requires a `HeroService` instance
|
||||
in order 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/>
|
||||
|
||||
|
@ -88,7 +88,8 @@ For example, import Angular's `Component` decorator from the `@angular/core` lib
|
||||
|
||||
<code-example path="architecture/src/app/app.component.ts" region="import" linenums="false"></code-example>
|
||||
|
||||
You also import NgModules from Angular _libraries_ using JavaScript import statements:
|
||||
You also import NgModules from Angular _libraries_ using JavaScript import statements.
|
||||
For example, the following code imports the `BrowserModule` NgModule from the `platform-browser` library:
|
||||
|
||||
<code-example path="architecture/src/app/mini-app.ts" region="import-browser-module" linenums="false"></code-example>
|
||||
|
||||
|
@ -60,11 +60,29 @@ The process of `HeroService` injection looks something like this:
|
||||
|
||||
### Providing services
|
||||
|
||||
You must register at least one *provider* of any service you are going to use. You can register providers in modules or in components.
|
||||
You must register at least one *provider* of any service you are going to use. A service can register providers itself, making it available everywhere, or you can register providers with specific modules or components. You register providers in the metadata of the service (in the `@Injectable` decorator), or in the `@NgModule` or `@Component` metadata
|
||||
|
||||
* When you add providers to the [root module](guide/architecture-modules), the same instance of a service is available to all components in your app.
|
||||
* By default, the Angular CLI command `ng generate service` registers a provider with the root injector for your service by including provider metadata in the `@Injectable` decorator. The tutorial uses this method to register the provider of HeroService class definition:
|
||||
|
||||
<code-example path="architecture/src/app/app.module.ts" linenums="false" title="src/app/app.module.ts (module providers)" region="providers"></code-example>
|
||||
```
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
```
|
||||
|
||||
When you provide the service at the root level, Angular creates a single, shared instance of HeroService and injects into any class that asks for it. Registering the provider in the `@Injectable` metadata also allows Angular to optimize an app by removing the service if it turns out not to be used after all.
|
||||
|
||||
* When you register a provider with a [specific NgModule](guide/architecture-modules), the same instance of a service is available to all components in that NgModule. To register at this level, use the `providers` property of the `@NgModule` decorator:
|
||||
|
||||
```
|
||||
@NgModule({
|
||||
providers: [
|
||||
BackendService,
|
||||
Logger
|
||||
],
|
||||
...
|
||||
})
|
||||
```
|
||||
|
||||
* When you register a provider at the component level, you get a new instance of the
|
||||
service with each new instance of that component. At the component level, register a service provider in the `providers` property of the `@Component` metadata:
|
||||
|
@ -96,7 +96,7 @@ Now edit the generated `src/app/highlight.directive.ts` to look as follows:
|
||||
|
||||
The `import` statement specifies an additional `ElementRef` symbol from the Angular `core` library:
|
||||
|
||||
You use the `ElementRef`in the directive's constructor
|
||||
You use the `ElementRef` in the directive's constructor
|
||||
to [inject](guide/dependency-injection) a reference to the host DOM element,
|
||||
the element to which you applied `appHighlight`.
|
||||
|
||||
|
@ -164,7 +164,7 @@ They are _not inherited_ by any components nested within the template nor by any
|
||||
|
||||
</div>
|
||||
|
||||
The CLI defines an empty `styles` array when you create the component with the `--inline-styles` flag.
|
||||
The CLI defines an empty `styles` array when you create the component with the `--inline-style` flag.
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
ng generate component hero-app --inline-style
|
||||
@ -189,7 +189,7 @@ They are _not inherited_ by any components nested within the template nor by any
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can specify more than one styles file or even a combination of `style` and `styleUrls`.
|
||||
You can specify more than one styles file or even a combination of `styles` and `styleUrls`.
|
||||
|
||||
</div>
|
||||
|
||||
@ -280,12 +280,14 @@ To control how this encapsulation happens on a *per
|
||||
component* basis, you can set the *view encapsulation mode* in the component metadata.
|
||||
Choose from the following modes:
|
||||
|
||||
* `Native` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
* `ShadowDom` view encapsulation uses the browser's native shadow DOM implementation (see
|
||||
[Shadow DOM](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Shadow_DOM)
|
||||
on the [MDN](https://developer.mozilla.org) site)
|
||||
to attach a shadow DOM to the component's host element, and then puts the component
|
||||
view inside that shadow DOM. The component's styles are included within the shadow DOM.
|
||||
|
||||
* `Native` view encapsulation uses a now deprecated version of the browser's native shadow DOM implementation - [learn about the changes](https://hayato.io/2016/shadowdomv1/).
|
||||
|
||||
* `Emulated` view encapsulation (the default) emulates the behavior of shadow DOM by preprocessing
|
||||
(and renaming) the CSS code to effectively scope the CSS to the component's view.
|
||||
For details, see [Appendix 1](guide/component-styles#inspect-generated-css).
|
||||
@ -300,8 +302,8 @@ To set the components encapsulation mode, use the `encapsulation` property in th
|
||||
<code-example path="component-styles/src/app/quest-summary.component.ts" region="encapsulation.native" title="src/app/quest-summary.component.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
`Native` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v0](http://caniuse.com/#feat=shadowdom) on the
|
||||
`ShadowDom` view encapsulation only works on browsers that have native support
|
||||
for shadow DOM (see [Shadow DOM v1](https://caniuse.com/#feat=shadowdomv1) on the
|
||||
[Can I use](http://caniuse.com) site). The support is still limited,
|
||||
which is why `Emulated` view encapsulation is the default mode and recommended
|
||||
in most cases.
|
||||
|
@ -32,7 +32,7 @@ from the [The Tour of Heroes](tutorial/).
|
||||
</code-tabs>
|
||||
|
||||
The `HeroesComponent` is the top-level heroes component.
|
||||
It's only purpose is to display the `HeroListComponent`
|
||||
Its only purpose is to display the `HeroListComponent`
|
||||
which displays a list of hero names.
|
||||
|
||||
This version of the `HeroListComponent` gets its `heroes` from the `HEROES` array, an in-memory collection
|
||||
|
@ -152,7 +152,7 @@ Install `source-map-explorer`:
|
||||
Build your app for production _including the source maps_
|
||||
|
||||
<code-example language="none" class="code-shell">
|
||||
ng build --prod --sourcemaps
|
||||
ng build --prod --source-map
|
||||
</code-example>
|
||||
|
||||
List the generated bundles in the `dist/` folder.
|
||||
|
@ -31,6 +31,8 @@ Here are a few essential commands for guide page authors.
|
||||
|
||||
1. http://localhost:4200/ — browse to the app running locally.
|
||||
|
||||
You can combine `yarn docs-watch` and `yarn start` into one command with `yarn serve-and-sync`.
|
||||
|
||||
## Guide pages
|
||||
|
||||
All but a few guide pages are [markdown](https://daringfireball.net/projects/markdown/syntax "markdown") files with an `.md` extension.
|
||||
|
@ -92,7 +92,7 @@ built-in validators—this time, in function form. See below:
|
||||
|
||||
{@a reactive-component-class}
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="form-group" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="form-group" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note that:
|
||||
@ -148,7 +148,7 @@ at which point the form uses the last value emitted for validation.
|
||||
In reactive forms, custom validators are fairly simple to add. All you have to do is pass the function directly
|
||||
to the `FormControl`.
|
||||
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.ts" region="custom-validator" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.1.ts" region="custom-validator" title="reactive/hero-form-reactive.component.ts (validator functions)" linenums="false">
|
||||
</code-example>
|
||||
|
||||
### Adding to template-driven forms
|
||||
@ -208,5 +208,80 @@ set the color of each form control's border.
|
||||
|
||||
</code-example>
|
||||
|
||||
## Cross field validation
|
||||
This section shows how to perform cross field validation. It assumes some basic knowledge of creating custom validators.
|
||||
|
||||
<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).
|
||||
|
||||
</div>
|
||||
|
||||
In the following section, we will make sure that our heroes do not reveal their true identities by filling out the Hero Form. We will do that by validating that the hero names and alter egos do not match.
|
||||
|
||||
### Adding to reactive forms
|
||||
|
||||
The form has the following structure:
|
||||
|
||||
```javascript
|
||||
const heroForm = new FormGroup({
|
||||
'name': new FormControl(),
|
||||
'alterEgo': new FormControl(),
|
||||
'power': new FormControl()
|
||||
});
|
||||
```
|
||||
|
||||
Notice that the name and alterEgo are sibling controls. To evaluate both controls in a single custom validator, we should perform the validation in a common ancestor control: the `FormGroup`. That way, we can query the `FormGroup` for the child controls which will allow us to compare their values.
|
||||
|
||||
To add a validator to the `FormGroup`, pass the new validator in as the second argument on creation.
|
||||
|
||||
```javascript
|
||||
const heroForm = new FormGroup({
|
||||
'name': new FormControl(),
|
||||
'alterEgo': new FormControl(),
|
||||
'power': new FormControl()
|
||||
}, { validators: identityRevealedValidator });
|
||||
```
|
||||
|
||||
The validator code is as follows:
|
||||
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-validator" title="shared/identity-revealed.directive.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
The identity validator implements the `ValidatorFn` interface. It takes an Angular control object as an argument and returns either null if the form is valid, or `ValidationErrors` otherwise.
|
||||
|
||||
First we retrieve the child controls by calling the `FormGroup`'s [get](api/forms/AbstractControl#get) method. Then we simply compare the values of the `name` and `alterEgo` controls.
|
||||
|
||||
If the values do not match, the hero's identity remains secret, and we can safely return null. Otherwise, the hero's identity is revealed and we must mark the form as invalid by returning an error object.
|
||||
|
||||
Next, to provide better user experience, we show an appropriate error message when the form is invalid.
|
||||
<code-example path="form-validation/src/app/reactive/hero-form-reactive.component.html" region="cross-validation-error-message" title="reactive/hero-form-template.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the `FormGroup` has the cross validation error returned by the `identityRevealed` validator,
|
||||
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
|
||||
|
||||
### Adding to template driven forms
|
||||
First we must create a directive that will wrap the validator function. We provide it as the validator using the `NG_VALIDATORS` token. If you are not sure why, or you do not fully understand the syntax, revisit the previous [section](guide/form-validation#adding-to-template-driven-forms).
|
||||
|
||||
<code-example path="form-validation/src/app/shared/identity-revealed.directive.ts" region="cross-validation-directive" title="shared/identity-revealed.directive.ts" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Next, we have to add the directive to the html template. Since the validator must be registered at the highest level in the form, we put the directive on the `form` tag.
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-register-validator" title="template/hero-form-template.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
To provide better user experience, we show an appropriate error message when the form is invalid.
|
||||
<code-example path="form-validation/src/app/template/hero-form-template.component.html" region="cross-validation-error-message" title="template/hero-form-template.component.html" linenums="false">
|
||||
</code-example>
|
||||
|
||||
Note that we check if:
|
||||
- the form has the cross validation error returned by the `identityRevealed` validator,
|
||||
- the user is yet to [interact](guide/form-validation#why-check-dirty-and-touched) with the form.
|
||||
|
||||
This completes the cross validation example. We managed to:
|
||||
- validate the form based on the values of two sibling controls,
|
||||
- show a descriptive error message after the user interacted with the form and the validation failed.
|
||||
|
||||
**You can run the <live-example></live-example> to see the complete reactive and template-driven example code.**
|
||||
|
@ -152,7 +152,8 @@ nothing to distinguish it from any component you've written before.
|
||||
Understanding this component requires only the Angular concepts covered in previous pages.
|
||||
|
||||
* The code imports the Angular core library and the `Hero` model you just created.
|
||||
* The `@Component` selector value of "hero-form" means you can drop this form in a parent template with a `<hero-form>` tag.
|
||||
* The `@Component` selector value of "app-hero-form" means you can drop this form in a parent
|
||||
template with a `<app-hero-form>` tag.
|
||||
* The `templateUrl` property points to a separate file for the template HTML.
|
||||
* You defined dummy data for `model` and `powers`, as befits a demo.
|
||||
|
||||
|
@ -44,25 +44,25 @@ of some of the things they contain:
|
||||
<tr>
|
||||
<td><code>FormsModule</code></td>
|
||||
<td><code>@angular/forms</code></td>
|
||||
<td>When you build template driven forms (includes <code>NgModel</code>)</td>
|
||||
<td>When you want to build template driven forms (includes <code>NgModel</code>)</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>ReactiveFormsModule</code></td>
|
||||
<td><code>@angular/forms</code></td>
|
||||
<td>When building reactive forms</td>
|
||||
<td>When you want to build reactive forms</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>RouterModule</code></td>
|
||||
<td><code>@angular/router</code></td>
|
||||
<td>For Routing and when you want to use <code>RouterLink</code>,<code>.forRoot()</code>, and <code>.forChild()</code></td>
|
||||
<td>When you want to use <code>RouterLink</code>, <code>.forRoot()</code>, and <code>.forChild()</code></td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td><code>HttpClientModule</code></td>
|
||||
<td><code>@angular/common/http</code></td>
|
||||
<td>When you to talk to a server</td>
|
||||
<td>When you want to talk to a server</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
@ -114,19 +114,23 @@ The following class types can be declared:
|
||||
|
||||
A [decorator](guide/glossary#decorator) statement immediately before a field in a class definition that declares the type of that field. Some examples are `@Input` and `@Output`.
|
||||
|
||||
{@a cli}
|
||||
|
||||
## CLI
|
||||
|
||||
The [Angular CLI](https://cli.angular.io/) is a command-line tool that can create a project, add files, and perform a variety of ongoing development tasks such as testing, bundling, and deployment.
|
||||
The [Angular CLI](https://cli.angular.io/) is a command-line tool for managing the Angular development cycle. Use it to create the initial filesystem scaffolding for a [workspace](guide/glossary#workspace) or [project](guide/glossary#project), and to run [schematics](guide/glossary#schematic) that add and modify code for initial generic versions of various elements. The tool supports all stages of the development cycle, including building, testing, bundling, and deployment.
|
||||
|
||||
Learn more in the [Getting Started](guide/quickstart) guide.
|
||||
* To begin using the CLI for a new project, see [Getting Started](guide/quickstart) guide.
|
||||
* To learn more about the full capabilities of the CLI, see the [Angular CLI documentation].(https://github.com/angular/angular-cli/wiki).
|
||||
|
||||
{@a component}
|
||||
|
||||
## Component
|
||||
|
||||
A class with the `@Component` [decorator](guide/glossary#decorator) that associates it with a companion [template](guide/glossary#template).
|
||||
A class with the `@Component` [decorator](guide/glossary#decorator) that associates it with a companion [template](guide/glossary#template). Together, the component and template define a [view](guide/glossary#view).
|
||||
|
||||
A component is a special type of [directive](guide/glossary#directive) that represents a [view](guide/glossary#view).The `@Component` decorator extends the `@Directive` decorator with template-oriented features.
|
||||
A component is a special type of [directive](guide/glossary#directive).
|
||||
The `@Component` decorator extends the `@Directive` decorator with template-oriented features.
|
||||
|
||||
An Angular component class is responsible for exposing data and handling most of the view's display and user-interaction logic through [data binding](guide/glossary#data-binding).
|
||||
|
||||
@ -208,7 +212,8 @@ See [Class decorator](guide/glossary#class-decorator), [Class field decorator](g
|
||||
|
||||
A design pattern and mechanism for creating and delivering parts of an application (dependencies) to other parts of an application that require them.
|
||||
|
||||
In Angular, dependencies are typically services, but can also be values, such as strings or functions. An [injector](guide/glossary#injector) for an app (created automatically during bootstrap) creates dependencies when needed, using a registered [provider](guide/glossary#provider) of the service or value. Different providers can provide different implementations of the same service.
|
||||
In Angular, dependencies are typically services, but can also be values, such as strings or functions.
|
||||
An [injector](guide/glossary#injector) for an app (created automatically during bootstrap) instantiates dependencies when needed, using a configured [provider](guide/glossary#provider) of the service or value.
|
||||
|
||||
Learn more in the [Dependency Injection](guide/dependency-injection) guide.
|
||||
|
||||
@ -280,7 +285,7 @@ Compare [Custom element](guide/glossary#custom-element).
|
||||
|
||||
## Entry point
|
||||
|
||||
A JavaScript ID that makes parts of an NPM package available for import by other code.
|
||||
A JavaScript symbol that makes parts of an npm package available for import by other code.
|
||||
The Angular [scoped packages](guide/glossary#scoped-package) each have an entry point named `index`.
|
||||
|
||||
Within Angular, use [NgModules](guide/glossary#ngmodule) to achieve the same result.
|
||||
@ -310,7 +315,17 @@ Both a [service](guide/glossary#service) and a [component](guide/glossary#compon
|
||||
|
||||
An object in the Angular [dependency-injection system](guide/glossary#dependency-injection)
|
||||
that can find a named dependency in its cache or create a dependency
|
||||
with a registered [provider](guide/glossary#provider). Injectors are created for NgModules automatically as part of the bootstrap process, and inherited through the component hierarchy.
|
||||
using a configured [provider](guide/glossary#provider).
|
||||
Injectors are created for NgModules automatically as part of the bootstrap process
|
||||
and are inherited through the component hierarchy.
|
||||
|
||||
* An injector provides a singleton instance of a dependency, and can inject this same instance in multiple components.
|
||||
|
||||
* A hierarchy of injectors at the NgModule and component level can provide different instances of a dependency to their own components and child components.
|
||||
|
||||
* You can configure injectors with different providers that can provide different implementations of the same dependency.
|
||||
|
||||
Learn more about the injector hierarchy in the [Dependency Injection guide](guide/hierarchical-dependency-injection).
|
||||
|
||||
|
||||
## Input
|
||||
@ -373,6 +388,17 @@ Lazy loading speeds up application load time by splitting the application into m
|
||||
For example, dependencies can be lazy-loaded as needed&emdash;as opposed to "eager-loaded" modules that are required by the root module, and are thus loaded on launch.
|
||||
Similarly, the [router](guide/glossary#router) can load child views only when the parent view is activated, and you can build custom elements that can be loaded into an Angular app when needed.
|
||||
|
||||
{@a library}
|
||||
|
||||
## Library
|
||||
|
||||
In Angular, a [project](guide/glossary#project) that provides functionality that can be included in other Angular apps. A library is not a complete Angular app, and it cannot run independently.
|
||||
|
||||
* Library developers can use the [CLI](guide/glossary#cli) to `generate` scaffolding for a new library in an existing [workspace](guide/glossary#workspace), and can publish a library as an `npm` package.
|
||||
|
||||
* App developers can use the [CLI](guide/glossary#cli) to `add` a published library for use with an app in the same [workspace](guide/glossary#workspace).
|
||||
|
||||
|
||||
## Lifecycle hook
|
||||
|
||||
An interface that allows you to tap into the lifecycle of [directives](guide/glossary#directive) and [components](guide/glossary#component) as they are created, updated, and destroyed.
|
||||
@ -402,7 +428,7 @@ In general, a module collects a block of code dedicated to a single purpose. Ang
|
||||
|
||||
In JavaScript (ECMAScript), each file is a module and all objects defined in the file belong to that module. Objects can exported, making them public, and public objects can be imported for use by other modules.
|
||||
|
||||
Angular ships as a collection of JavaScript modules, or libraries. Each Angular library name begins with the `@angular` prefix. Install them with the NPM package manager and import parts of them with JavaScript `import` declarations.
|
||||
Angular ships as a collection of JavaScript modules, or libraries. Each Angular library name begins with the `@angular` prefix. Install them with the npm package manager and import parts of them with JavaScript `import` declarations.
|
||||
|
||||
Compare the Angular [NgModule](guide/glossary#ngmodule).
|
||||
|
||||
@ -470,8 +496,13 @@ To learn more, see the [pipes](guide/pipes) page.
|
||||
|
||||
## Polyfill
|
||||
|
||||
An [NPM package](guide/npm-packages) that plugs gaps in a browser's JavaScript implementation. See the [Browser Support](guide/browser-support) guide for polyfills that support particular functionality for particular platforms.
|
||||
An [npm package](guide/npm-packages) that plugs gaps in a browser's JavaScript implementation. See the [Browser Support](guide/browser-support) guide for polyfills that support particular functionality for particular platforms.
|
||||
|
||||
{@a project}
|
||||
|
||||
## Project
|
||||
|
||||
In Angular, a folder within a [workspace](guide/glossary#workspace) that contains an Angular app or [library](guide/glossary#library). A workspace can contain multiple projects. All apps in a workspace can use libraries in the same workspace.
|
||||
|
||||
## Provider
|
||||
|
||||
@ -531,9 +562,24 @@ For more information, see the [Routing & Navigation](guide/router) page.
|
||||
|
||||
{@a S}
|
||||
|
||||
{@a schematic}
|
||||
|
||||
## Schematic
|
||||
|
||||
A scaffolding library that defines how to generate or transform a programming project by creating, modifying, refactoring, or moving files and code.
|
||||
|
||||
The Angular [CLI](guide/glossary#cli) uses schematics to generate and modify [Angular projects](guide/glossary#project) and parts of projects.
|
||||
|
||||
* Angular provides a set of schematics for use with the CLI.
|
||||
For details, see [Angular CLI documentation].(https://github.com/angular/angular-cli/wiki).
|
||||
|
||||
* Library developers can create schematics that enable the CLI to generate their published libraries.
|
||||
For more information, see https://www.npmjs.com/package/@angular-devkit/schematics.
|
||||
|
||||
|
||||
## Scoped package
|
||||
|
||||
A way to group related NPM packages.
|
||||
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/http`, and `@angular/router`.
|
||||
|
||||
Import a scoped package in the same way that you import a normal package.
|
||||
@ -677,6 +723,12 @@ The view hierarchy does not imply a component hierarchy. Views that are embedded
|
||||
|
||||
See [Custom element](guide/glossary#custom-element)
|
||||
|
||||
{@a workspace}
|
||||
|
||||
## Workspace
|
||||
|
||||
In Angular, a folder that contains [projects](guide/glossary#project) (that is, apps and libraries).
|
||||
The [CLI](guide/glossary#cli) `new` command creates a workspace to contain projects. Commands such as `add` and `generate`, that create or operate on apps and libraries, must be executed from within a workspace folder.
|
||||
|
||||
{@a X}
|
||||
|
||||
|
@ -450,7 +450,7 @@ Here is a `searchHeroes` method that queries for heroes whose names contain the
|
||||
|
||||
If there is a search term, the code constructs an options object with an HTML URL-encoded search parameter. If the term were "foo", the GET request URL would be `api/heroes/?name=foo`.
|
||||
|
||||
The `HttpParms` are immutable so you'll have to use the `set()` method to update the options.
|
||||
The `HttpParams` are immutable so you'll have to use the `set()` method to update the options.
|
||||
|
||||
### Debouncing requests
|
||||
|
||||
@ -1034,7 +1034,7 @@ Call `request.flush()` with an error message, as seen in the following example.
|
||||
|
||||
<code-example
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="network-error"
|
||||
region="404"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
|
||||
@ -1044,4 +1044,4 @@ Alternatively, you can call `request.error()` with an `ErrorEvent`.
|
||||
path="http/src/testing/http-client.spec.ts"
|
||||
region="network-error"
|
||||
linenums="false">
|
||||
</code-example>
|
||||
</code-example>
|
||||
|
@ -219,7 +219,7 @@ configure services in root and feature modules respectively.
|
||||
|
||||
Angular doesn't recognize these names but Angular developers do.
|
||||
Follow this convention when you write similar modules with configurable service providers.
|
||||
<!--KW--I don't understand how Angular doesn't understand these methods...-->
|
||||
|
||||
|
||||
<hr/>
|
||||
|
||||
@ -233,9 +233,8 @@ When you import an NgModule,
|
||||
Angular adds the module's service providers (the contents of its `providers` list)
|
||||
to the application root injector.
|
||||
|
||||
This makes the provider visible to every class in the application that knows the provider's lookup token, or knows its name.
|
||||
This makes the provider visible to every class in the application that knows the provider's lookup token, or name.
|
||||
|
||||
This is by design.
|
||||
Extensibility through NgModule imports is a primary goal of the NgModule system.
|
||||
Merging NgModule providers into the application injector
|
||||
makes it easy for a module library to enrich the entire application with new services.
|
||||
@ -247,6 +246,8 @@ If the `HeroModule` provides the `HeroService` and the root `AppModule` imports
|
||||
any class that knows the `HeroService` _type_ can inject that service,
|
||||
not just the classes declared in the `HeroModule`.
|
||||
|
||||
To limit access to a service, consider lazy loading the NgModule that provides that service. See [How do I restrict service scope to a module?](guide/ngmodule-faq#service-scope) for more information.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-lazy-loaded-module-provider-visibility}
|
||||
@ -288,6 +289,7 @@ The `AppModule` always wins.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a service-scope}
|
||||
|
||||
## How do I restrict service scope to a module?
|
||||
|
||||
@ -335,6 +337,8 @@ You can embed the child components in the top component's template.
|
||||
Alternatively, make the top component a routing host by giving it a `<router-outlet>`.
|
||||
Define child routes and let the router load module components into that outlet.
|
||||
|
||||
Though you can limit access to a service by providing it in a lazy loaded module or providing it in a component, providing services in a component can lead to multiple instances of those services. Thus, the lazy loading is preferable.
|
||||
|
||||
<hr/>
|
||||
|
||||
{@a q-root-component-or-module}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user