Compare commits
166 Commits
Author | SHA1 | Date | |
---|---|---|---|
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",
|
||||
],
|
||||
)
|
||||
|
78
CHANGELOG.md
78
CHANGELOG.md
@ -1,3 +1,53 @@
|
||||
<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 +92,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 +243,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 +256,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 +274,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.9.1.zip",
|
||||
strip_prefix = "rules_nodejs-0.9.1",
|
||||
sha256 = "6139762b62b37c1fd171d7f22aa39566cb7dc2916f0f801d505a9aaf118c117f",
|
||||
)
|
||||
|
||||
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
|
||||
|
@ -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].*"
|
||||
]
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ export class ComposeMessageComponent {
|
||||
@HostBinding('style.position') position = 'absolute';
|
||||
|
||||
details: string;
|
||||
message: string;
|
||||
sending = false;
|
||||
|
||||
constructor(private router: Router) {}
|
||||
|
@ -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,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,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();
|
||||
}));
|
||||
|
@ -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();
|
||||
}));
|
||||
|
@ -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>
|
@ -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.**
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
||||
@ -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>
|
||||
|
@ -74,22 +74,58 @@ The following table contains our current target release dates for the next two m
|
||||
|
||||
|
||||
{@a lts}
|
||||
## Long-term support
|
||||
{@a support}
|
||||
## Support policy
|
||||
|
||||
All of our releases are supported actively for about 6 months (until the next major release), and then they are supported through long-term support (LTS) for another 12 months.
|
||||
All of our major releases are supported for 18 months.
|
||||
|
||||
During the LTS period, only critical fixes and security patches will be merged and released.
|
||||
* 6 months of active support, during which regularly-scheduled updates and patches are released, as described above in [Release frequency](#frequency "Release frequency").
|
||||
|
||||
The LTS state of one major version starts on the day of the next major release. LTS status ends approximately one year later, when we release another major version.
|
||||
* 12 months of long-term support (LTS). During the LTS period, only critical fixes and security patches will be released.
|
||||
|
||||
The following table provides the support status and key dates for Angular version 4.0.0 and higher.
|
||||
|
||||
<style>
|
||||
|
||||
Version | LTS Start Date | LTS End Date
|
||||
----------- | -------------- | ------------
|
||||
^4.0.0 | October 2017 | October 2018
|
||||
^5.0.0 | April 2018 | April 2019
|
||||
^6.0.0 | October 2018 | October 2019
|
||||
td, th {vertical-align: top}
|
||||
|
||||
</style>
|
||||
|
||||
<table>
|
||||
|
||||
<tr>
|
||||
<th>Version</th>
|
||||
<th>Status</th>
|
||||
<th>Release Date</th>
|
||||
<th>LTS Start Date</th>
|
||||
<th>LTS End Date</th>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>^4.0.0</td>
|
||||
<td>LTS</td>
|
||||
<td>March 23, 2017</td>
|
||||
<td>September 23, 2017</td>
|
||||
<td>September 23, 2018</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>^5.0.0</td>
|
||||
<td>LTS</td>
|
||||
<td>November 1, 2017</td>
|
||||
<td>May 1, 2018</td>
|
||||
<td>May 1, 2019</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>^6.0.0</td>
|
||||
<td>Active</td>
|
||||
<td>May 3, 2018</td>
|
||||
<td>November 3, 2018</td>
|
||||
<td>November 3, 2019</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
@ -106,7 +142,7 @@ To make these transitions as easy as possible, we make two commitments to you:
|
||||
|
||||
To help ensure that you have sufficient time and a clear path to update, this is our deprecation policy:
|
||||
|
||||
* When announce deprecated features in the [change log](https://github.com/angular/angular/blob/master/CHANGELOG.md "Angular change log").
|
||||
* We announce deprecated features in the [change log](https://github.com/angular/angular/blob/master/CHANGELOG.md "Angular change log").
|
||||
|
||||
* When we announce a deprecation, we also announce a recommended update path.
|
||||
|
||||
@ -127,8 +163,6 @@ Any changes to the public API surface will be done using the versioning, support
|
||||
{@a labs}
|
||||
## Angular Labs
|
||||
|
||||
Angular Labs is an initiative to cultivate new features and iterate on them quickly. Angular Labs provides a safe place for exploration and experimentation by the Angular team.
|
||||
Angular Labs is an initiative to cultivate new features and iterate on them quickly. Angular Labs provides a safe place for exploration and experimentation by the Angular team.
|
||||
|
||||
Angular Labs projects are not ready for production use, and no commitment is made to bring them to production. The policies and practices that are described in this document do not apply to Angular Labs projects.
|
||||
|
||||
Angular Labs projects typically are in separate branches in the Angular repo, clearly separated from the main Angular codebase.
|
||||
Angular Labs projects are not ready for production use, and no commitment is made to bring them to production. The policies and practices that are described in this document do not apply to Angular Labs projects.
|
||||
|
@ -933,29 +933,18 @@ As always, strive for consistency.
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 02-06}
|
||||
{@a 05-02}
|
||||
|
||||
### Directive selectors
|
||||
### Component selectors
|
||||
|
||||
#### Style 02-06
|
||||
#### Style 05-02
|
||||
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** Use lower camel case for naming the selectors of directives.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** Keeps the names of the properties defined in the directives that are bound to the view consistent with the attribute names.
|
||||
**Do** use _dashed-case_ or _kebab-case_ for naming the element selectors of components.
|
||||
|
||||
|
||||
</div>
|
||||
@ -966,16 +955,40 @@ As always, strive for consistency.
|
||||
|
||||
|
||||
|
||||
**Why?** The Angular HTML parser is case sensitive and recognizes lower camel case.
|
||||
**Why?** Keeps the element names consistent with the specification for [Custom Elements](https://www.w3.org/TR/custom-elements/).
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/05-02/app/heroes/shared/hero-button/hero-button.component.avoid.ts" region="example" title="app/heroes/shared/hero-button/hero-button.component.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="app/heroes/shared/hero-button/hero-button.component.ts" path="styleguide/src/05-02/app/heroes/shared/hero-button/hero-button.component.ts" region="example">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="app/app.component.html" path="styleguide/src/05-02/app/app.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 02-07}
|
||||
|
||||
### Custom prefix for components
|
||||
### Component custom prefix
|
||||
|
||||
#### Style 02-07
|
||||
|
||||
@ -1078,11 +1091,51 @@ For example, the prefix `toh` represents from **T**our **o**f **H**eroes and the
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 02-06}
|
||||
|
||||
### Directive selectors
|
||||
|
||||
#### Style 02-06
|
||||
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** Use lower camel case for naming the selectors of directives.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why">
|
||||
|
||||
|
||||
|
||||
**Why?** Keeps the names of the properties defined in the directives that are bound to the view consistent with the attribute names.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** The Angular HTML parser is case sensitive and recognizes lower camel case.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 02-08}
|
||||
|
||||
### Custom prefix for directives
|
||||
### Directive custom prefix
|
||||
|
||||
#### Style 02-08
|
||||
|
||||
@ -3056,9 +3109,9 @@ module are referenced across the entire application.
|
||||
|
||||
|
||||
|
||||
**Avoid** providing services in shared modules. Services are usually
|
||||
**Consider** _not_ providing services in shared modules. Services are usually
|
||||
singletons that are provided once for the entire application or
|
||||
in a particular feature module.
|
||||
in a particular feature module. There are exceptions, however. For example, in the sample code that follows, notice that the `SharedModule` provides `FilterTextService`. This is acceptable here because the service is stateless;that is, the consumers of the service aren't impacted by new instances.
|
||||
|
||||
|
||||
</div>
|
||||
@ -3710,59 +3763,6 @@ A typical *lazy loaded folder* contains a *routing component*, its child compone
|
||||
|
||||
## Components
|
||||
|
||||
{@a 05-02}
|
||||
|
||||
### Component selector names
|
||||
|
||||
#### Style 05-02
|
||||
|
||||
|
||||
<div class="s-rule do">
|
||||
|
||||
|
||||
|
||||
**Do** use _dashed-case_ or _kebab-case_ for naming the element selectors of components.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="s-why-last">
|
||||
|
||||
|
||||
|
||||
**Why?** Keeps the element names consistent with the specification for [Custom Elements](https://www.w3.org/TR/custom-elements/).
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<code-example path="styleguide/src/05-02/app/heroes/shared/hero-button/hero-button.component.avoid.ts" region="example" title="app/heroes/shared/hero-button/hero-button.component.ts">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="app/heroes/shared/hero-button/hero-button.component.ts" path="styleguide/src/05-02/app/heroes/shared/hero-button/hero-button.component.ts" region="example">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="app/app.component.html" path="styleguide/src/05-02/app/app.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
<a href="#toc">Back to top</a>
|
||||
|
||||
{@a 05-03}
|
||||
|
||||
### Components as elements
|
||||
|
@ -1,3 +0,0 @@
|
||||
# Testing
|
||||
|
||||
TBD. Original content [here](https://docs.google.com/document/d/1gGP5sqWNCHAWWV_GLdZQ1XyMO4K-CHksUxux0BFtVxk/edit#heading=h.ohqykkhzdhb2).
|
@ -204,8 +204,7 @@ The test consumes that spy in the same way it did earlier.
|
||||
Most test suites in this guide call `beforeEach()` to set the preconditions for each `it()` test
|
||||
and rely on the `TestBed` to create classes and inject services.
|
||||
|
||||
There's another school of testing that never calls `beforeEach()` and
|
||||
and prefers to create classes explicitly rather than use the `TestBed`.
|
||||
There's another school of testing that never calls `beforeEach()` and prefers to create classes explicitly rather than use the `TestBed`.
|
||||
|
||||
Here's how you might rewrite one of the `MasterService` tests in that style.
|
||||
|
||||
@ -347,7 +346,7 @@ It appears within the template of a parent component,
|
||||
which binds a _hero_ to the `@Input` property and
|
||||
listens for an event raised through the _selected_ `@Output` property.
|
||||
|
||||
You can test that the class code works without creating the the `DashboardHeroComponent`
|
||||
You can test that the class code works without creating the `DashboardHeroComponent`
|
||||
or its parent component.
|
||||
|
||||
<code-example
|
||||
@ -399,7 +398,7 @@ But a component is more than just its class.
|
||||
A component interacts with the DOM and with other components.
|
||||
The _class-only_ tests can tell you about class behavior.
|
||||
They cannot tell you if the component is going to render properly,
|
||||
respond to user input and gestures, or integrate with its parent and and child components.
|
||||
respond to user input and gestures, or integrate with its parent and child components.
|
||||
|
||||
None of the _class-only_ tests above can answer key questions about how the
|
||||
components actually behave on screen.
|
||||
@ -2367,9 +2366,9 @@ The [override metadata object](#metadata-override-object) is a generic defined a
|
||||
|
||||
<code-example format="." language="javascript">
|
||||
type MetadataOverride<T> = {
|
||||
add?: T;
|
||||
remove?: T;
|
||||
set?: T;
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
};
|
||||
</code-example>
|
||||
|
||||
@ -2725,9 +2724,9 @@ appropriate to the method, that is, the parameter of an `@NgModule`,
|
||||
|
||||
<code-example format="." language="javascript">
|
||||
type MetadataOverride<T> = {
|
||||
add?: T;
|
||||
remove?: T;
|
||||
set?: T;
|
||||
add?: Partial<T>;
|
||||
remove?: Partial<T>;
|
||||
set?: Partial<T>;
|
||||
};
|
||||
</code-example>
|
||||
|
||||
@ -3379,11 +3378,11 @@ next to their corresponding helper files.
|
||||
{@a q-e2e}
|
||||
#### Why not rely on E2E tests of DOM integration?
|
||||
|
||||
The component DOM tests describe in this guide often require extensive setup and
|
||||
advanced techniques where as the [class-only test](#component-class-testing)
|
||||
were comparatively simple.
|
||||
The component DOM tests described in this guide often require extensive setup and
|
||||
advanced techniques whereas the [unit tests](#component-class-testing)
|
||||
are comparatively simple.
|
||||
|
||||
Why not defer DOM integration tests to end-to-end (E2E) testing?
|
||||
#### Why not defer DOM integration tests to end-to-end (E2E) testing?
|
||||
|
||||
E2E tests are great for high-level validation of the entire system.
|
||||
But they can't give you the comprehensive test coverage that you'd expect from unit tests.
|
||||
@ -3400,4 +3399,4 @@ accidental corruption of remote resources.
|
||||
It can even be hard to navigate to the component you want to test.
|
||||
|
||||
Because of these many obstacles, you should test DOM interaction
|
||||
with unit testing techniques as much as possible.
|
||||
with unit testing techniques as much as possible.
|
||||
|
@ -132,7 +132,7 @@ QuickStart identifies two *typings*, or `d.ts`, files:
|
||||
* [jasmine](http://jasmine.github.io/) typings for the Jasmine test framework.
|
||||
|
||||
* [node](https://www.npmjs.com/package/@types/node) for code that references objects in the *Node.js®* environment;
|
||||
you can view an example in the [webpack](guide/webpack) page.
|
||||
|
||||
|
||||
QuickStart doesn't require these typings but many of the samples do.
|
||||
|
||||
|
352
aio/content/guide/upgrade-performance.md
Normal file
352
aio/content/guide/upgrade-performance.md
Normal file
@ -0,0 +1,352 @@
|
||||
# Upgrading for Performance
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
_Angular_ is the name for the Angular of today and tomorrow.<br />
|
||||
_AngularJS_ is the name for all 1.x versions of Angular.
|
||||
|
||||
</div>
|
||||
|
||||
This guide describes some of the built-in tools for efficiently migrating AngularJS projects over to
|
||||
the Angular platform, one piece at a time. It is very similar to
|
||||
[Upgrading from AngularJS](guide/upgrade) with the exception that this one uses the {@link
|
||||
downgradeModule downgradeModule()} helper function instead of the {@link UpgradeModule
|
||||
UpgradeModule} class. This affects how the app is bootstrapped and how change detection is
|
||||
propagated between the two frameworks. It allows you to upgrade incrementally while improving the
|
||||
speed of your hybrid apps and leveraging the latest of Angular in AngularJS apps early in the
|
||||
process of upgrading.
|
||||
|
||||
|
||||
|
||||
## Preparation
|
||||
|
||||
Before discussing how you can use `downgradeModule()` to create hybrid apps, there are things that
|
||||
you can do to ease the upgrade process even before you begin upgrading. Because the steps are the
|
||||
same regardless of how you upgrade, refer to the [Preparation](guide/upgrade#preparation) section of
|
||||
[Upgrading from AngularJS](guide/upgrade).
|
||||
|
||||
|
||||
## Upgrading with `ngUpgrade`
|
||||
|
||||
With the `ngUpgrade` library in Angular you can upgrade an existing AngularJS app incrementally by
|
||||
building a hybrid app where you can run both frameworks side-by-side. In these hybrid apps you can
|
||||
mix and match AngularJS and Angular components and services and have them interoperate seamlessly.
|
||||
That means you don't have to do the upgrade work all at once as there is a natural coexistence
|
||||
between the two frameworks during the transition period.
|
||||
|
||||
|
||||
### How `ngUpgrade` Works
|
||||
|
||||
Regardless of whether you choose `downgradeModule()` or `UpgradeModule`, the basic principles of
|
||||
upgrading, the mental model behind hybrid apps, and how you use the {@link upgrade/static
|
||||
upgrade/static} utilities remain the same. For more information, see the
|
||||
[How `ngUpgrade` Works](guide/upgrade#how-ngupgrade-works) section of
|
||||
[Upgrading from AngularJS](guide/upgrade).
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
The [Change Detection](guide/upgrade#change-detection) section of
|
||||
[Upgrading from AngularJS](guide/upgrade) only applies to apps that use `UpgradeModule`. Though
|
||||
you handle change detection differently with `downgradeModule()`, which is the focus of this
|
||||
guide, reading the [Change Detection](guide/upgrade#change-detection) section provides helpful
|
||||
context for what follows.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
#### Change Detection with `downgradeModule()`
|
||||
|
||||
As mentioned before, one of the key differences between `downgradeModule()` and `UpgradeModule` has
|
||||
to do with change detection and how it is propagated between the two frameworks.
|
||||
|
||||
With `UpgradeModule`, the two change detection systems are tied together more tightly. Whenever
|
||||
something happens in the AngularJS part of the app, change detection is automatically triggered on
|
||||
the Angular part and vice versa. This is convenient as it ensures that neither framework misses an
|
||||
important change. Most of the time, though, these extra change detection runs are unnecessary.
|
||||
|
||||
`downgradeModule()`, on the other side, avoids explicitly triggering change detection unless it
|
||||
knows the other part of the app is interested in the changes. For example, if a downgraded component
|
||||
defines an `@Input()`, chances are that the app needs to be aware when that value changes. Thus,
|
||||
`downgradeComponent()` automatically triggers change detection on that component.
|
||||
|
||||
In most cases, though, the changes made locally in a particular component are of no interest to the
|
||||
rest of the app. For example, if the user clicks a button that submits a form, the component usually
|
||||
handles the result of this action. That being said, there _are_ cases where you want to propagate
|
||||
changes to some other part of the app that may be controlled by the other framework. In such cases,
|
||||
you are responsible for notifying the interested parties by manually triggering change detection.
|
||||
|
||||
If you want a particular piece of code to trigger change detection in the AngularJS part of the app,
|
||||
you need to wrap it in
|
||||
[scope.$apply()](https://docs.angularjs.org/api/ng/type/$rootScope.Scope#$apply). Similarly, for
|
||||
triggering change detection in Angular you would use {@link NgZone#run ngZone.run()}.
|
||||
|
||||
In many cases, a few extra change detection runs may not matter much. However, on larger or
|
||||
change-detection-heavy apps they can have a noticeable impact. By giving you more fine-grained
|
||||
control over the change detection propagation, `downgradeModule()` allows you to achieve better
|
||||
performance for your hybrid apps.
|
||||
|
||||
|
||||
## Using `downgradeModule()`
|
||||
|
||||
Both AngularJS and Angular have their own concept of modules to help organize an app into cohesive
|
||||
blocks of functionality.
|
||||
|
||||
Their details are quite different in architecture and implementation. In AngularJS, you create a
|
||||
module by specifying its name and dependencies with
|
||||
[angular.module()](https://docs.angularjs.org/api/ng/function/angular.module). Then you can add
|
||||
assets using its various methods. In Angular, you create a class adorned with an {@link NgModule
|
||||
NgModule} decorator that describes assets in metadata.
|
||||
|
||||
In a hybrid app you run both frameworks at the same time. This means that you need at least one
|
||||
module each from both AngularJS and Angular.
|
||||
|
||||
For the most part, you specify the modules in the same way you would for a regular app. Then, you
|
||||
use the `upgrade/static` helpers to let the two frameworks know about assets they can use from each
|
||||
other. This is known as "upgrading" and "downgrading".
|
||||
|
||||
<div class="alert is-helpful">
|
||||
|
||||
<b>Definitions:</b>
|
||||
|
||||
- _Upgrading_: The act of making an AngularJS asset, such as a component or service, available to
|
||||
the Angular part of the app.
|
||||
- _Downgrading_: The act of making an Angular asset, such as a component or service, available to
|
||||
the AngularJS part of the app.
|
||||
|
||||
</div>
|
||||
|
||||
An important part of inter-linking dependencies is linking the two main modules together. This is
|
||||
where `downgradeModule()` comes in. Use it to create an AngularJS module—one that you can use
|
||||
as a dependency in your main AngularJS module—that will bootstrap your main Angular module and
|
||||
kick off the Angular part of the hybrid app. In a sense, it "downgrades" an Angular module to an
|
||||
AngularJS module.
|
||||
|
||||
There are a few things to note, though:
|
||||
|
||||
1. You don't pass the Angular module directly to `downgradeModule()`. All `downgradeModule()` needs
|
||||
is a "recipe", for example, a factory function, to create an instance for your module.
|
||||
|
||||
2. The Angular module is not instantiated until the app actually needs it.
|
||||
|
||||
The following is an example of how you can use `downgradeModule()` to link the two modules.
|
||||
|
||||
```ts
|
||||
// Import `downgradeModule()`.
|
||||
import { downgradeModule } from '@angular/upgrade/static';
|
||||
|
||||
// Use it to downgrade the Angular module to an AngularJS module.
|
||||
const downgradedModule = downgradeModule(MainAngularModuleFactory);
|
||||
|
||||
// Use the downgraded module as a dependency to the main AngularJS module.
|
||||
angular.module('mainAngularJsModule', [
|
||||
downgradedModule
|
||||
]);
|
||||
```
|
||||
|
||||
|
||||
#### Specifying a factory for the Angular module
|
||||
|
||||
As mentioned earlier, `downgradeModule()` needs to know how to instantiate the Angular module. It
|
||||
needs a recipe. You define that recipe by providing a factory function that can create an instance
|
||||
of the Angular module. `downgradeModule()` accepts two types of factory functions:
|
||||
|
||||
1. `NgModuleFactory`
|
||||
2. `(extraProviders: StaticProvider[]) => Promise<NgModuleRef>`
|
||||
|
||||
When you pass an `NgModuleFactory`, `downgradeModule()` uses it to instantiate the module using
|
||||
{@link platformBrowser platformBrowser}'s {@link PlatformRef#bootstrapModuleFactory
|
||||
bootstrapModuleFactory()}, which is compatible with ahead-of-time (AOT) compilation. AOT compilation
|
||||
helps make your apps load faster. For more about AOT and how to create an `NgModuleFactory`, see the
|
||||
[Ahead-of-Time Compilation](guide/aot-compiler) guide.
|
||||
|
||||
Alternatively, you can pass a plain function, which is expected to return a promise resolving to an
|
||||
{@link NgModuleRef NgModuleRef} (i.e. an instance of your Angular module). The function is called
|
||||
with an array of extra {@link StaticProvider Providers} that are expected to be available on the
|
||||
returned `NgModuleRef`'s {@link Injector Injector}. For example, if you are using {@link
|
||||
platformBrowser platformBrowser} or {@link platformBrowserDynamic platformBrowserDynamic}, you can
|
||||
pass the `extraProviders` array to them:
|
||||
|
||||
```ts
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
||||
const platformRef = platformBrowserDynamic(extraProviders);
|
||||
return platformRef.bootstrapModule(MainAngularModule);
|
||||
};
|
||||
// or
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
||||
const platformRef = platformBrowser(extraProviders);
|
||||
return platformRef.bootstrapModuleFactory(MainAngularModuleFactory);
|
||||
};
|
||||
```
|
||||
|
||||
Using an `NgModuleFactory` requires less boilerplate and is a good default option as it supports AOT
|
||||
out-of-the-box. Using a custom function requires slightly more code, but gives you greater
|
||||
flexibility.
|
||||
|
||||
|
||||
#### Instantiating the Angular module on-demand
|
||||
|
||||
Another key difference between `downgradeModule()` and `UpgradeModule` is that the latter requires
|
||||
you to instantiate both the AngularJS and Angular modules up-front. This means that you have to pay
|
||||
the cost of instantiating the Angular part of the app, even if you don't use any Angular assets
|
||||
until later. `downgradeModule()` is again less aggressive. It will only instantiate the Angular part
|
||||
when it is required for the first time; that is, as soon as it needs to create a downgraded
|
||||
component.
|
||||
|
||||
You could go a step further and not even download the code for the Angular part of the app to the
|
||||
user's browser until it is needed. This is especially useful when you use Angular on parts of the
|
||||
hybrid app that are not necessary for the initial rendering or that the user doesn't reach.
|
||||
|
||||
|
||||
A few examples are:
|
||||
|
||||
- You use Angular on specific routes only and you don't need it until/if a user visits such a route.
|
||||
- You use Angular for features that are only visible to specific types of users; for example,
|
||||
logged-in users, administrators, or VIP members. You don't need to load Angular until a user is
|
||||
authenticated.
|
||||
- You use Angular for a feature that is not critical for the initial rendering of the app and you
|
||||
can afford a small delay in favor of better initial load performance.
|
||||
|
||||
|
||||
### Bootstrapping with `downgradeModule()`
|
||||
|
||||
As you might have guessed, you don't need to change anything in the way you bootstrap your existing
|
||||
AngularJS app. Unlike `UpgradeModule`—which requires some extra steps—
|
||||
`downgradeModule()` is able to take care of bootstrapping the Angular module, as long as you provide
|
||||
the recipe.
|
||||
|
||||
In order to start using any `upgrade/static` APIs, you still need to load the Angular framework as
|
||||
you would in a normal Angular app. You can see how this can be done with SystemJS by following the
|
||||
instructions in the [Setup](guide/setup) guide, selectively copying code from the
|
||||
[QuickStart github repository](https://github.com/angular/quickstart).
|
||||
|
||||
You also need to install the `@angular/upgrade` package via `npm install @angular/upgrade --save`
|
||||
and add a mapping for the `@angular/upgrade/static` package:
|
||||
|
||||
<code-example title="system.config.js">
|
||||
'@angular/upgrade/static': 'npm:@angular/upgrade/bundles/upgrade-static.umd.js',
|
||||
</code-example>
|
||||
|
||||
Next, create an `app.module.ts` file and add the following `NgModule` class:
|
||||
|
||||
<code-example title="app.module.ts">
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule
|
||||
]
|
||||
})
|
||||
export class MainAngularModule {
|
||||
// Empty placeholder method to satisfy the `Compiler`.
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
</code-example>
|
||||
|
||||
This bare minimum `NgModule` imports `BrowserModule`, the module every Angular browser-based app
|
||||
must have. It also defines an empty `ngDoBootstrap()` method, to prevent the {@link Compiler
|
||||
Compiler} from returning errors. This is necessary because the module will not have a `bootstrap`
|
||||
declaration on its `NgModule` decorator.
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
You do not add a `bootstrap` declaration to the `NgModule` decorator since AngularJS owns the root
|
||||
template of the app and `ngUpgrade` bootstraps the necessary components.
|
||||
|
||||
</div>
|
||||
|
||||
You can now link the AngularJS and Angular modules together using `downgradeModule()`.
|
||||
|
||||
<code-example title="app.module.ts">
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
import { downgradeModule } from '@angular/upgrade/static';
|
||||
|
||||
const bootstrapFn = (extraProviders: StaticProvider[]) => {
|
||||
const platformRef = platformBrowserDynamic(extraProviders);
|
||||
return platformRef.bootstrapModule(MainAngularModule);
|
||||
};
|
||||
const downgradedModule = downgradeModule(bootstrapFn);
|
||||
|
||||
angular.module('mainAngularJsModule', [
|
||||
downgradedModule
|
||||
]);
|
||||
</code-example>
|
||||
|
||||
The existing AngularJS code works as before _and_ you are ready to start adding Angular code.
|
||||
|
||||
|
||||
### Using Components and Injectables
|
||||
|
||||
The differences between `downgradeModule()` and `UpgradeModule` end here. The rest of the
|
||||
`upgrade/static` APIs and concepts work in the exact same way for both types of hybrid apps.
|
||||
See [Upgrading from AngularJS](guide/upgrade) to learn about:
|
||||
|
||||
- [Using Angular Components from AngularJS Code](guide/upgrade#using-angular-components-from-angularjs-code).
|
||||
- [Using AngularJS Component Directives from Angular Code](guide/upgrade#using-angularjs-component-directives-from-angular-code).
|
||||
- [Projecting AngularJS Content into Angular Components](guide/upgrade#projecting-angularjs-content-into-angular-components).
|
||||
- [Transcluding Angular Content into AngularJS Component Directives](guide/upgrade#transcluding-angular-content-into-angularjs-component-directives).
|
||||
- [Making AngularJS Dependencies Injectable to Angular](guide/upgrade#making-angularjs-dependencies-injectable-to-angular).
|
||||
- [Making Angular Dependencies Injectable to AngularJS](guide/upgrade#making-angular-dependencies-injectable-to-angularjs).
|
||||
|
||||
<div class="alert is-important">
|
||||
|
||||
While it is possible to downgrade injectables, downgraded injectables will not be available until
|
||||
the Angular module is instantiated. In order to be safe, you need to ensure that the downgraded
|
||||
injectables are not used anywhere _outside_ the part of the app that is controlled by Angular.
|
||||
|
||||
For example, it is _OK_ to use a downgraded service in an upgraded component that is only used
|
||||
from Angular components, but it is _not OK_ to use it in an AngularJS component that may be used
|
||||
independently of Angular.
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Using ahead-of-time compilation with hybrid apps
|
||||
|
||||
You can take advantage of ahead-of-time (AOT) compilation in hybrid apps just like in any other
|
||||
Angular app. The setup for a hybrid app is mostly the same as described in the
|
||||
[Ahead-of-Time Compilation](guide/aot-compiler) guide save for differences in `index.html` and
|
||||
`main-aot.ts`.
|
||||
|
||||
AOT needs to load any AngularJS files that are in the `<script>` tags in the AngularJS `index.html`.
|
||||
An easy way to copy them is to add each to the `copy-dist-files.js`file.
|
||||
|
||||
You also need to pass the generated `MainAngularModuleFactory` to `downgradeModule()` instead of the
|
||||
custom bootstrap function:
|
||||
|
||||
<code-example title="app/main-aot.ts">
|
||||
import { downgradeModule } from '@angular/upgrade/static';
|
||||
import { MainAngularModuleNgFactory } from '../aot/app/app.module.ngfactory';
|
||||
|
||||
const downgradedModule = downgradeModule(MainAngularModuleNgFactory);
|
||||
|
||||
angular.module('mainAngularJsModule', [
|
||||
downgradedModule
|
||||
]);
|
||||
</code-example>
|
||||
|
||||
And that is all you need to do to get the full benefit of AOT for hybrid Angular apps.
|
||||
|
||||
|
||||
## Conclusion
|
||||
|
||||
This page covered how to use the {@link upgrade/static upgrade/static} package to incrementally
|
||||
upgrade existing AngularJS apps at your own pace and without impeding further development of the app
|
||||
for the duration of the upgrade process.
|
||||
|
||||
Specifically, this guide showed how you can achieve better performance and greater flexibility in
|
||||
your hybrid apps by using {@link downgradeModule downgradeModule()} instead of {@link UpgradeModule
|
||||
UpgradeModule}.
|
||||
|
||||
To summarize, the key differentiating factors of `downgradeModule()` are:
|
||||
|
||||
1. It allows instantiating or even loading the Angular part lazily, which improves the initial
|
||||
loading time. In some cases this may waive the cost of running a second framework altogether.
|
||||
2. It improves performance by avoiding unnecessary change detection runs while giving the developer
|
||||
greater ability to customize.
|
||||
3. It does not require you to change how you bootstrap your AngularJS app.
|
||||
|
||||
Using `downgradeModule()` is a good option for hybrid apps when you want to keep the AngularJS and
|
||||
Angular parts less coupled. You can still mix and match components and services from both
|
||||
frameworks, but you might need to manually propagate change detection. In return,
|
||||
`downgradeModule()` offers more control and better performance.
|
@ -1,7 +1,7 @@
|
||||
# Upgrading from AngularJS to Angular
|
||||
|
||||
_Angular_ is the name for the Angular of today and tomorrow.
|
||||
_AngularJS_ is the name for all v1.x versions of Angular.
|
||||
_Angular_ is the name for the Angular of today and tomorrow.<br />
|
||||
_AngularJS_ is the name for all 1.x versions of Angular.
|
||||
|
||||
AngularJS apps are great.
|
||||
Always consider the business case before moving to Angular.
|
||||
@ -195,7 +195,7 @@ transition period.
|
||||
|
||||
### How ngUpgrade Works
|
||||
|
||||
The primary tool provided by ngUpgrade is called the `UpgradeModule`.
|
||||
One of the primary tools provided by ngUpgrade is called the `UpgradeModule`.
|
||||
This is a module that contains utilities for bootstrapping and managing hybrid
|
||||
applications that support both Angular and AngularJS code.
|
||||
|
||||
@ -252,7 +252,7 @@ frameworks in how it actually works.
|
||||
</table>
|
||||
|
||||
Even accounting for these differences you can still have dependency injection
|
||||
interoperability. The `UpgradeModule` resolves the differences and makes
|
||||
interoperability. `upgrade/static` resolves the differences and makes
|
||||
everything work seamlessly:
|
||||
|
||||
* You can make AngularJS services available for injection to Angular code
|
||||
@ -569,7 +569,7 @@ So, you can write an Angular component and then use it from AngularJS
|
||||
code. This is useful when you start to migrate from lower-level
|
||||
components and work your way up. But in some cases it is more convenient
|
||||
to do things in the opposite order: To start with higher-level components
|
||||
and work your way down. This too can be done using the `UpgradeModule`.
|
||||
and work your way down. This too can be done using the `upgrade/static`.
|
||||
You can *upgrade* AngularJS component directives and then use them from
|
||||
Angular.
|
||||
|
||||
@ -710,7 +710,7 @@ and then provide the input and output using Angular template syntax:
|
||||
When you are using a downgraded Angular component from an AngularJS
|
||||
template, the need may arise to *transclude* some content into it. This
|
||||
is also possible. While there is no such thing as transclusion in Angular,
|
||||
there is a very similar concept called *content projection*. The `UpgradeModule`
|
||||
there is a very similar concept called *content projection*. `upgrade/static`
|
||||
is able to make these two features interoperate.
|
||||
|
||||
Angular components that support content projection make use of an `<ng-content>`
|
||||
|
@ -1,801 +0,0 @@
|
||||
# Webpack: An Introduction
|
||||
|
||||
<style>
|
||||
h4 {font-size: 17px !important; text-transform: none !important;}
|
||||
.syntax { font-family: Consolas, 'Lucida Sans', Courier, sans-serif; color: black; font-size: 85%; }
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
[**Webpack**](https://webpack.github.io/) is a popular module bundler,
|
||||
a tool for bundling application source code in convenient _chunks_
|
||||
and for loading that code from a server into a browser.
|
||||
|
||||
It's an excellent alternative to the *SystemJS* approach used elsewhere in the documentation.
|
||||
This guide offers a taste of Webpack and explains how to use it with Angular applications.
|
||||
|
||||
|
||||
{@a top}
|
||||
|
||||
<!--
|
||||
|
||||
|
||||
# Contents
|
||||
|
||||
* [What is Webpack?](guide/webpack#what-is-webpack)
|
||||
|
||||
* [Entries and outputs](guide/webpack#entries-outputs)
|
||||
* [Multiple bundles](guide/webpack#multiple-bundles)
|
||||
* [Loaders](guide/webpack#loaders)
|
||||
* [Plugins](guide/webpack#plugins)
|
||||
|
||||
* [Configuring Webpack](guide/webpack#configure-webpack)
|
||||
|
||||
* [Polyfills](guide/webpack#polyfills)
|
||||
* [Common configuration](guide/webpack#common-configuration)
|
||||
* [Inside `webpack.common.js`](guide/webpack#inside-webpack-commonjs)
|
||||
|
||||
* [entry](guide/webpack#common-entries)
|
||||
* [resolve extension-less imports](guide/webpack#common-resolves)
|
||||
* [`module.rules`](guide/webpack#common-rules)
|
||||
* [Plugins](guide/webpack#plugins)
|
||||
* [`CommonsChunkPlugin`](guide/webpack#commons-chunk-plugin)
|
||||
* [`HtmlWebpackPlugin`](guide/webpack#html-webpack-plugin)
|
||||
|
||||
* [Environment specific configuration](guide/webpack#environment-configuration)
|
||||
* [Development configuration](guide/webpack#development-configuration)
|
||||
* [Production configuration](guide/webpack#production-configuration)
|
||||
* [Test configuration](guide/webpack#test-configuration)
|
||||
|
||||
* [Trying it out](guide/webpack#try)
|
||||
* [Highlights](guide/webpack#highlights)
|
||||
* [Conclusion](guide/webpack#conclusion)
|
||||
|
||||
-->
|
||||
|
||||
You can also <a href="generated/zips/webpack/webpack.zip" target="_blank">download the final result.</a>
|
||||
|
||||
{@a what-is-webpack}
|
||||
|
||||
## What is Webpack?
|
||||
|
||||
Webpack is a powerful module bundler.
|
||||
A _bundle_ is a JavaScript file that incorporates _assets_ that *belong* together and
|
||||
should be served to the client in a response to a single file request.
|
||||
A bundle can include JavaScript, CSS styles, HTML, and almost any other kind of file.
|
||||
|
||||
Webpack roams over your application source code,
|
||||
looking for `import` statements, building a dependency graph, and emitting one or more _bundles_.
|
||||
With plugins and rules, Webpack can preprocess and minify different non-JavaScript files such as TypeScript, SASS, and LESS files.
|
||||
|
||||
You determine what Webpack does and how it does it with a JavaScript configuration file, `webpack.config.js`.
|
||||
|
||||
|
||||
{@a entries-outputs}
|
||||
|
||||
|
||||
|
||||
### Entries and outputs
|
||||
|
||||
You supply Webpack with one or more *entry* files and let it find and incorporate the dependencies that radiate from those entries.
|
||||
The one entry point file in this example is the application's root file, `src/main.ts`:
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="one-entry" title="webpack.config.js (single entry)" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Webpack inspects that file and traverses its `import` dependencies recursively.
|
||||
|
||||
|
||||
<code-example path="webpack/src/app/app.component.ts" region="component" title="src/main.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
It sees that you're importing `@angular/core` so it adds that to its dependency list for potential inclusion in the bundle.
|
||||
It opens the `@angular/core` file and follows _its_ network of `import` statements until it has built the complete dependency graph from `main.ts` down.
|
||||
|
||||
Then it **outputs** these files to the `app.js` _bundle file_ designated in configuration:
|
||||
|
||||
<code-example name="webpack.config.js (single output)" language="javascript">
|
||||
output: {
|
||||
filename: 'app.js'
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
This `app.js` output bundle is a single JavaScript file that contains the application source and its dependencies.
|
||||
You'll load it later with a `<script>` tag in the `index.html`.
|
||||
|
||||
|
||||
{@a multiple-bundles}
|
||||
|
||||
|
||||
#### Multiple bundles
|
||||
You probably don't want one giant bundle of everything.
|
||||
It's preferable to separate the volatile application app code from comparatively stable vendor code modules.
|
||||
|
||||
Change the configuration so that it has two entry points, `main.ts` and `vendor.ts`:
|
||||
|
||||
|
||||
<code-example language="javascript">
|
||||
entry: {
|
||||
app: 'src/app.ts',
|
||||
vendor: 'src/vendor.ts'
|
||||
},
|
||||
|
||||
output: {
|
||||
filename: '[name].js'
|
||||
}
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
Webpack constructs two separate dependency graphs
|
||||
and emits *two* bundle files, one called `app.js` containing only the application code and
|
||||
another called `vendor.js` with all the vendor dependencies.
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
The `[name]` in the output name is a *placeholder* that a Webpack plugin replaces with the entry names,
|
||||
`app` and `vendor`. Plugins are [covered later](guide/webpack#commons-chunk-plugin) in the guide.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
To tell Webpack what belongs in the vendor bundle,
|
||||
add a `vendor.ts` file that only imports the application's third-party modules:
|
||||
|
||||
<code-example path="webpack/src/vendor.ts" title="src/vendor.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a loaders}
|
||||
|
||||
|
||||
|
||||
### Loaders
|
||||
|
||||
Webpack can bundle any kind of file: JavaScript, TypeScript, CSS, SASS, LESS, images, HTML, fonts, whatever.
|
||||
Webpack _itself_ only understands JavaScript files.
|
||||
Teach it to transform non-JavaScript file into their JavaScript equivalents with *loaders*.
|
||||
Configure loaders for TypeScript and CSS as follows.
|
||||
|
||||
|
||||
<code-example language="javascript">
|
||||
rules: [
|
||||
{
|
||||
test: /\.ts$/,
|
||||
loader: 'awesome-typescript-loader'
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
loaders: 'style-loader!css-loader'
|
||||
}
|
||||
]
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
When Webpack encounters `import` statements like the following,
|
||||
it applies the `test` RegEx patterns.
|
||||
|
||||
|
||||
<code-example language="typescript">
|
||||
import { AppComponent } from './app.component.ts';
|
||||
|
||||
import 'uiframework/dist/uiframework.css';
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
When a pattern matches the filename, Webpack processes the file with the associated loader.
|
||||
|
||||
The first `import` file matches the `.ts` pattern so Webpack processes it with the `awesome-typescript-loader`.
|
||||
The imported file doesn't match the second pattern so its loader is ignored.
|
||||
|
||||
The second `import` matches the second `.css` pattern for which you have *two* loaders chained by the (!) character.
|
||||
Webpack applies chained loaders *right to left*. So it applies
|
||||
the `css` loader first to flatten CSS `@import` and `url(...)` statements.
|
||||
Then it applies the `style` loader to append the css inside `<style>` elements on the page.
|
||||
|
||||
|
||||
{@a plugins}
|
||||
|
||||
|
||||
|
||||
### Plugins
|
||||
|
||||
Webpack has a build pipeline with well-defined phases.
|
||||
Tap into that pipeline with plugins such as the `uglify` minification plugin:
|
||||
|
||||
<code-example language="javascript">
|
||||
plugins: [
|
||||
new webpack.optimize.UglifyJsPlugin()
|
||||
]
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a configure-webpack}
|
||||
|
||||
|
||||
|
||||
## Configuring Webpack
|
||||
|
||||
After that brief orientation, you are ready to build your own Webpack configuration for Angular apps.
|
||||
|
||||
Begin by setting up the development environment.
|
||||
|
||||
Create a new project folder.
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
mkdir angular-webpack
|
||||
cd angular-webpack
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Add these files:
|
||||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="package.json" path="webpack/package.webpack.json">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/tsconfig.json" path="webpack/src/tsconfig.1.json">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="webpack.config.js" path="webpack/webpack.config.js">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="karma.conf.js" path="webpack/karma.webpack.conf.js">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="config/helpers.js" path="webpack/config/helpers.js">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
Many of these files should be familiar from other Angular documentation guides,
|
||||
especially the [Typescript configuration](guide/typescript-configuration) and
|
||||
[npm packages](guide/npm-packages) guides.
|
||||
|
||||
Webpack, the plugins, and the loaders are also installed as packages.
|
||||
They are listed in the updated `packages.json`.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
Open a terminal window and install the npm packages.
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm install
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a polyfills}
|
||||
|
||||
|
||||
|
||||
### Polyfills
|
||||
|
||||
You'll need polyfills to run an Angular application in most browsers as explained
|
||||
in the [Browser Support](guide/browser-support) guide.
|
||||
|
||||
Polyfills should be bundled separately from the application and vendor bundles.
|
||||
Add a `polyfills.ts` like this one to the `src/` folder.
|
||||
|
||||
|
||||
<code-example path="webpack/src/polyfills.ts" title="src/polyfills.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<div class="callout is-critical">
|
||||
|
||||
|
||||
|
||||
<header>
|
||||
Loading polyfills
|
||||
</header>
|
||||
|
||||
|
||||
|
||||
Load `zone.js` early within `polyfills.ts`, immediately after the other ES6 and metadata shims.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
Because this bundle file will load first, `polyfills.ts` is also a good place to configure the browser environment
|
||||
for production or development.
|
||||
|
||||
|
||||
{@a common-configuration}
|
||||
|
||||
|
||||
|
||||
### Common configuration
|
||||
|
||||
Developers typically have separate configurations for development, production, and test environments.
|
||||
All three have a lot of configuration in common.
|
||||
|
||||
Gather the common configuration in a file called `webpack.common.js`.
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a inside-webpack-commonjs}
|
||||
|
||||
|
||||
### Inside _webpack.common.js_
|
||||
Webpack is a NodeJS-based tool that reads configuration from a JavaScript commonjs module file.
|
||||
|
||||
The configuration imports dependencies with `require` statements
|
||||
and exports several objects as properties of a `module.exports` object.
|
||||
|
||||
* [`entry`](guide/webpack#common-entries)—the entry-point files that define the bundles.
|
||||
* [`resolve`](guide/webpack#common-resolves)—how to resolve file names when they lack extensions.
|
||||
* [`module.rules`](guide/webpack#common-rules)— `module` is an object with `rules` for deciding how files are loaded.
|
||||
* [`plugins`](guide/webpack#common-plugins)—creates instances of the plugins.
|
||||
|
||||
|
||||
{@a common-entries}
|
||||
|
||||
|
||||
#### _entry_
|
||||
|
||||
The first export is the `entry` object:
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="entries" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
This `entry` object defines the three bundles:
|
||||
|
||||
* `polyfills`—the polyfills needed to run Angular applications in most modern browsers.
|
||||
* `vendor`—the third-party dependencies such as Angular, lodash, and bootstrap.css.
|
||||
* `app`—the application code.
|
||||
|
||||
|
||||
{@a common-resolves}
|
||||
|
||||
|
||||
#### _resolve_ extension-less imports
|
||||
|
||||
The app will `import` dozens if not hundreds of JavaScript and TypeScript files.
|
||||
You could write `import` statements with explicit extensions like this example:
|
||||
|
||||
<code-example language="typescript">
|
||||
import { AppComponent } from './app.component.ts';
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
But most `import` statements don't mention the extension at all.
|
||||
Tell Webpack to resolve extension-less file requests by looking for matching files with
|
||||
`.ts` extension or `.js` extension (for regular JavaScript files and pre-compiled TypeScript files).
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="resolve" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
If Webpack should resolve extension-less files for styles and HTML,
|
||||
add `.css` and `.html` to the list.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{@a common-rules}
|
||||
|
||||
|
||||
|
||||
|
||||
#### _module.rules_
|
||||
Rules tell Webpack which loaders to use for each file, or module:
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="loaders" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
* `awesome-typescript-loader`—a loader to transpile the Typescript code to ES5, guided by the `tsconfig.json` file.
|
||||
* `angular2-template-loader`—loads angular components' template and styles.
|
||||
* `html-loader`—for component templates.
|
||||
* images/fonts—Images and fonts are bundled as well.
|
||||
* CSS—the first pattern matches application-wide styles; the second handles
|
||||
component-scoped styles (the ones specified in a component's `styleUrls` metadata property).
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
The first pattern is for the application-wide styles. It excludes `.css` files within the `src/app` directory
|
||||
where the component-scoped styles sit. The `ExtractTextPlugin` (described below) applies the `style` and `css`
|
||||
loaders to these files.
|
||||
|
||||
The second pattern filters for component-scoped styles and loads them as strings via the `raw-loader`,
|
||||
which is what Angular expects to do with styles specified in a `styleUrls` metadata property.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
Multiple loaders can be chained using the array notation.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{@a common-plugins}
|
||||
|
||||
|
||||
|
||||
|
||||
#### _plugins_
|
||||
Finally, create instances of three plugins:
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.common.js" region="plugins" title="config/webpack.common.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a commons-chunk-plugin}
|
||||
|
||||
|
||||
#### *CommonsChunkPlugin*
|
||||
|
||||
The `app.js` bundle should contain only application code. All vendor code belongs in the `vendor.js` bundle.
|
||||
|
||||
Of course the application code imports vendor code.
|
||||
On its own, Webpack is not smart enough to keep the vendor code out of the `app.js` bundle.
|
||||
The `CommonsChunkPlugin` does that job.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
|
||||
|
||||
The `CommonsChunkPlugin` identifies the hierarchy among three _chunks_: `app` -> `vendor` -> `polyfills`.
|
||||
Where Webpack finds that `app` has shared dependencies with `vendor`, it removes them from `app`.
|
||||
It would remove `polyfills` from `vendor` if they shared dependencies, which they don't.
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{@a html-webpack-plugin}
|
||||
|
||||
|
||||
#### _HtmlWebpackPlugin_
|
||||
|
||||
Webpack generates a number of js and CSS files.
|
||||
You _could_ insert them into the `index.html` _manually_. That would be tedious and error-prone.
|
||||
Webpack can inject those scripts and links for you with the `HtmlWebpackPlugin`.
|
||||
|
||||
|
||||
{@a environment-configuration}
|
||||
|
||||
|
||||
|
||||
### Environment-specific configuration
|
||||
|
||||
The `webpack.common.js` configuration file does most of the heavy lifting.
|
||||
Create separate, environment-specific configuration files that build on `webpack.common`
|
||||
by merging into it the peculiarities particular to the target environments.
|
||||
|
||||
These files tend to be short and simple.
|
||||
|
||||
|
||||
{@a development-configuration}
|
||||
|
||||
|
||||
|
||||
### Development configuration
|
||||
|
||||
Here is the `webpack.dev.js` development configuration file.
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.dev.js" title="config/webpack.dev.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
The development build relies on the Webpack development server, configured near the bottom of the file.
|
||||
|
||||
Although you tell Webpack to put output bundles in the `dist` folder,
|
||||
the dev server keeps all bundles in memory; it doesn't write them to disk.
|
||||
You won't find any files in the `dist` folder, at least not any generated from *this development build*.
|
||||
|
||||
|
||||
The `HtmlWebpackPlugin`, added in `webpack.common.js`, uses the `publicPath` and the `filename` settings to generate
|
||||
appropriate `<script>` and `<link>` tags into the `index.html`.
|
||||
|
||||
The CSS styles are buried inside the Javascript bundles by default. The `ExtractTextPlugin` extracts them into
|
||||
external `.css` files that the `HtmlWebpackPlugin` inscribes as `<link>` tags into the `index.html`.
|
||||
|
||||
Refer to the [Webpack documentation](https://webpack.github.io/docs/) for details on these and
|
||||
other configuration options in this file.
|
||||
|
||||
Grab the app code at the end of this guide and try:
|
||||
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm start
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a production-configuration}
|
||||
|
||||
|
||||
|
||||
### Production configuration
|
||||
|
||||
Configuration of a *production* build resembles *development* configuration with a few key changes.
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.prod.js" title="config/webpack.prod.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
You'll deploy the application and its dependencies to a real production server.
|
||||
You won't deploy the artifacts needed only in development.
|
||||
|
||||
Put the production output bundle files in the `dist` folder.
|
||||
|
||||
Webpack generates file names with cache-busting hash.
|
||||
Thanks to the `HtmlWebpackPlugin`, you don't have to update the `index.html` file when the hash changes.
|
||||
|
||||
There are additional plugins:
|
||||
|
||||
* *`NoEmitOnErrorsPlugin`—stops the build if there is an error.
|
||||
* *`UglifyJsPlugin`—minifies the bundles.
|
||||
* *`ExtractTextPlugin`—extracts embedded css as external files, adding cache-busting hash to the filename.
|
||||
* *`DefinePlugin`—use to define environment variables that you can reference within the application.
|
||||
* *`LoaderOptionsPlugins`—to override options of certain loaders.
|
||||
|
||||
Thanks to the `DefinePlugin` and the `ENV` variable defined at top, you can enable Angular production mode like this:
|
||||
|
||||
|
||||
<code-example path="webpack/src/main.ts" region="enable-prod" title="src/main.ts" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Grab the app code at the end of this guide and try:
|
||||
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm run build
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
{@a test-configuration}
|
||||
|
||||
|
||||
|
||||
### Test configuration
|
||||
|
||||
You don't need much configuration to run unit tests.
|
||||
You don't need the loaders and plugins that you declared for your development and production builds.
|
||||
You probably don't need to load and process the application-wide styles files for unit tests and doing so would slow you down;
|
||||
you'll use the `null` loader for those CSS files.
|
||||
|
||||
You could merge the test configuration into the `webpack.common` configuration and override the parts you don't want or need.
|
||||
But it might be simpler to start over with a completely fresh configuration.
|
||||
|
||||
|
||||
<code-example path="webpack/config/webpack.test.js" title="config/webpack.test.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Reconfigure [Karma](https://karma-runner.github.io/1.0/index.html) to use Webpack to run the tests:
|
||||
|
||||
|
||||
<code-example path="webpack/config/karma.conf.js" title="config/karma.conf.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
You don't precompile the TypeScript; Webpack transpiles the Typescript files on the fly, in memory, and feeds the emitted JS directly to Karma.
|
||||
There are no temporary files on disk.
|
||||
|
||||
The `karma-test-shim` tells Karma what files to pre-load and
|
||||
primes the Angular test framework with test versions of the providers that every app expects to be pre-loaded.
|
||||
|
||||
|
||||
<code-example path="webpack/config/karma-test-shim.js" title="config/karma-test-shim.js" linenums="false">
|
||||
|
||||
</code-example>
|
||||
|
||||
|
||||
|
||||
Notice that you do _not_ load the application code explicitly.
|
||||
You tell Webpack to find and load the test files (the files ending in `.spec.ts`).
|
||||
Each spec file imports all—and only—the application source code that it tests.
|
||||
Webpack loads just _those_ specific application files and ignores the other files that you aren't testing.
|
||||
|
||||
|
||||
Grab the app code at the end of this guide and try:
|
||||
|
||||
|
||||
<code-example language="sh" class="code-shell">
|
||||
npm test
|
||||
|
||||
</code-example>
|
||||
|
||||
{@a try}
|
||||
|
||||
## Trying it out
|
||||
|
||||
Here is the source code for a small application that bundles with the
|
||||
Webpack techniques covered in this guide.
|
||||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="src/index.html" path="webpack/src/index.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/main.ts" path="webpack/src/main.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/assets/css/styles.css" path="webpack/src/assets/css/styles.css">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="src/app/app.component.ts" path="webpack/src/app/app.component.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.component.html" path="webpack/src/app/app.component.html">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.component.css" path="webpack/src/app/app.component.css">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.component.spec.ts" path="webpack/src/app/app.component.spec.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/app/app.module.ts" path="webpack/src/app/app.module.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
|
||||
|
||||
The <code>app.component.html</code> displays this downloadable Angular logo
|
||||
<a href="assets/images/logos/angular/angular.png">
|
||||
<img src="assets/images/logos/angular/angular.png" height="40px" title="download Angular logo"></a>.
|
||||
Create a folder called `images` under the project's `assets` folder, then right-click (Cmd+click on Mac)
|
||||
on the image and download it to that folder.
|
||||
|
||||
|
||||
{@a bundle-ts}
|
||||
|
||||
|
||||
Here again are the TypeScript entry-point files that define the `polyfills` and `vendor` bundles.
|
||||
|
||||
<code-tabs>
|
||||
|
||||
<code-pane title="src/polyfills.ts" path="webpack/src/polyfills.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
<code-pane title="src/vendor.ts" path="webpack/src/vendor.ts">
|
||||
|
||||
</code-pane>
|
||||
|
||||
</code-tabs>
|
||||
|
||||
{@a highlights}
|
||||
|
||||
<h3 class="no-toc">Highlights</h3>
|
||||
|
||||
* There are no `<script>` or `<link>` tags in the `index.html`.
|
||||
The `HtmlWebpackPlugin` inserts them dynamically at runtime.
|
||||
|
||||
* The `AppComponent` in `app.component.ts` imports the application-wide css with a simple `import` statement.
|
||||
|
||||
* The `AppComponent` itself has its own html template and css file. WebPack loads them with calls to `require()`.
|
||||
Webpack stashes those component-scoped files in the `app.js` bundle too.
|
||||
You don't see those calls in the source code;
|
||||
they're added behind the scenes by the `angular2-template-loader` plug-in.
|
||||
|
||||
* The `vendor.ts` consists of vendor dependency `import` statements that drive the `vendor.js` bundle.
|
||||
The application imports these modules too; they'd be duplicated in the `app.js` bundle
|
||||
if the `CommonsChunkPlugin` hadn't detected the overlap and removed them from `app.js`.
|
||||
{@a conclusion}
|
||||
|
||||
## Conclusion
|
||||
|
||||
You've learned just enough Webpack to configurate development, test and production builds
|
||||
for a small Angular application.
|
||||
|
||||
_You could always do more_. Search the web for expert advice and expand your Webpack knowledge.
|
||||
|
||||
[Back to top](guide/webpack#top)
|
BIN
aio/content/images/bios/elanaolson.jpg
Normal file
BIN
aio/content/images/bios/elanaolson.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
BIN
aio/content/images/bios/juristr.jpg
Normal file
BIN
aio/content/images/bios/juristr.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 207 KiB |
BIN
aio/content/images/bios/kimmaida.jpg
Normal file
BIN
aio/content/images/bios/kimmaida.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
BIN
aio/content/images/bios/mashhood.jpg
Normal file
BIN
aio/content/images/bios/mashhood.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 9.4 KiB |
@ -12,5 +12,19 @@
|
||||
"message": "Watch ng-conf live stream <br/>Apr 18th-20th, 2018",
|
||||
"imageUrl": "generated/images/marketing/home/ng-conf.png",
|
||||
"linkUrl": "https://www.ng-conf.org/livestream/"
|
||||
},
|
||||
{
|
||||
"startDate": "2018-06-01",
|
||||
"endDate": "2018-08-15",
|
||||
"message": "Join us for Angular Mix<br/>October 10th-12th, 2018",
|
||||
"imageUrl": "generated/images/marketing/home/angular-mix.png",
|
||||
"linkUrl": "https://angularmix.com/"
|
||||
},
|
||||
{
|
||||
"startDate": "2018-08-15",
|
||||
"endDate": "2018-11-06",
|
||||
"message": "Join us for Angular Connect<br/>November 6th-7th, 2018",
|
||||
"imageUrl": "generated/images/marketing/home/angular-connect.png",
|
||||
"linkUrl": "https://angularconnect.com/"
|
||||
}
|
||||
]
|
||||
|
@ -618,5 +618,40 @@
|
||||
"website": "https://mhartington.io",
|
||||
"bio": "Mike is a Developer Advocate for the Ionic Framework and a GDE in Angular. He spends most of his time making fast PWAs and exploring emerging web standards. When not behind a keyboard, you'll probably find him with a guitar and beer.",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
"juristr": {
|
||||
"name": "Juri Strumpflohner",
|
||||
"picture": "juristr.jpg",
|
||||
"twitter": "juristr",
|
||||
"website": "https://juristr.com",
|
||||
"bio": "Juri is a software engineer and freelance trainer and consultant currently mostly focusing on the frontend side using JavaScript, TypeScript and Angular. He has a passion for teaching and sharing his knowledge and experiences with others. This mostly happens by writing tech articles for his personal blog, by creating video courses for Egghead.io, during on-site workshops at companies or by speaking at conferences. In his free time he enjoys practicing Yoseikan Budo, a martial art where he currently owns the 3rd DAN black belt.",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
"mashhoodr": {
|
||||
"name": "Mashhood Rastgar",
|
||||
"picture": "mashhood.jpg",
|
||||
"twitter": "mashhoodr",
|
||||
"website": "http://imars.info/",
|
||||
"bio": "Mashhood is the principal technical consultant at Recurship and a Google Developer Expert. He works with different startups in US and EU to helps them crawl through the technical maze and quickly build amazing products focused around the problems they are trying to solve. He specializes in using the latest web technologies available to execute the best possible solutions.",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
"kimmaida": {
|
||||
"name": "Kim Maida",
|
||||
"picture": "kimmaida.jpg",
|
||||
"twitter": "KimMaida",
|
||||
"website": "https://kmaida.io/",
|
||||
"bio": "Kim is an an Angular consultant, developer, speaker, writer, and Google Developer Expert. She is passionate about learning from and sharing knowledge with other developers through blogging, speaking, workshops, and open source.",
|
||||
"group": "GDE"
|
||||
},
|
||||
|
||||
"elanaolson": {
|
||||
"name": "Elana Olson",
|
||||
"picture": "elanaolson.jpg",
|
||||
"twitter": "elanathellama",
|
||||
"bio": "Elana is a Developer Relations intern on the Angular team at Google. She is working on migration paths from AngularJS to Angular and would love to chat about your experience with upgrading.",
|
||||
"group": "Angular"
|
||||
}
|
||||
}
|
||||
|
@ -160,7 +160,7 @@
|
||||
"desc": "Lightweight yet powerful IDE, perfectly equipped for complex client-side development and server-side development with Node.js",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "Webstorm",
|
||||
"title": "WebStorm",
|
||||
"url": "https://www.jetbrains.com/webstorm/"
|
||||
},
|
||||
"ab3": {
|
||||
@ -175,7 +175,13 @@
|
||||
"rev": true,
|
||||
"title": "Angular IDE by Webclipse",
|
||||
"url": "https://www.genuitec.com/products/angular-ide"
|
||||
}
|
||||
},
|
||||
"amexio-canvas": {
|
||||
"desc": "Amexio Canvas is Drag and Drop Environment to create Fully Responsive Web and Smart Device HTML5/Angular Apps. Code will be auto generated and hot deployed by the Canvas for live testing. Out of the box 50+ Material Design Theme support. Commit your code to GitHub public or private repository.",
|
||||
"rev": true,
|
||||
"title": "Amexio Canvas Web Based Drag and Drop IDE by MetaMagic",
|
||||
"url": "https://amexio.tech/"
|
||||
}
|
||||
}
|
||||
},
|
||||
"Tooling": {
|
||||
@ -345,6 +351,13 @@
|
||||
"title": "Angular Material",
|
||||
"url": "https://github.com/angular/material2"
|
||||
},
|
||||
"mcc": {
|
||||
"desc": "Material components made by the community",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "Material Community Components",
|
||||
"url": "https://github.com/tiaguinho/material-community-components"
|
||||
},
|
||||
"ngzorro": {
|
||||
"desc": "A set of enterprise-class UI components based on Ant Design and Angular",
|
||||
"rev": true,
|
||||
@ -370,6 +383,13 @@
|
||||
"url": "http://www.amexio.tech/",
|
||||
"logo": "http://www.amexio.org/amexio-logo.png"
|
||||
},
|
||||
"bm": {
|
||||
"desc": "A lightweight Material Design library for Angular, based upon Google's Material Components for the Web",
|
||||
"logo": "https://blox.src.zone/assets/bloxmaterial.03ecfe4fa0147a781487749dc1cc4580.svg",
|
||||
"rev": true,
|
||||
"title": "Blox Material",
|
||||
"url": "https://github.com/src-zone/material"
|
||||
},
|
||||
"essentialjs2": {
|
||||
"desc": "Essential JS 2 for Angular is a collection modern TypeScript based true Angular Components. It has support for Ahead Of Time (AOT) compilation and Tree-Shaking. All the components are developed from the ground up to be lightweight, responsive, modular and touch friendly.",
|
||||
"rev": true,
|
||||
@ -470,6 +490,13 @@
|
||||
"rev": true,
|
||||
"title": "Angular-Buch (German)",
|
||||
"url": "https://angular-buch.com/"
|
||||
},
|
||||
"wishtack-guide-angular": {
|
||||
"desc": "The free, open-source and up-to-date Angular guide. This pragmatic guide is focused on best practices and will drive you from scratch to cloud.",
|
||||
"logo": "https://raw.githubusercontent.com/wishtack/gitbook-guide-angular/master/.gitbook/assets/wishtack-logo-with-text.png",
|
||||
"rev": true,
|
||||
"title": "The Angular Guide by Wishtack (Français)",
|
||||
"url": "https://guide-angular.wishtack.io/"
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -528,13 +555,6 @@
|
||||
"title": "Pluralsight",
|
||||
"url": "https://www.pluralsight.com/search?q=angular+2&categories=all"
|
||||
},
|
||||
"ab": {
|
||||
"desc": "Take this introduction to Angular course, to learn the fundamentals in just two days, free of charge.",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "Rangle.io",
|
||||
"url": "https://rangle.io/services/javascript-training/training-angular1-angular2-with-ngupgrade/"
|
||||
},
|
||||
"ab3": {
|
||||
"desc": "Angular courses hosted by Udemy",
|
||||
"logo": "",
|
||||
|
@ -61,12 +61,6 @@
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/webpack",
|
||||
"title": "Webpack: An Introduction",
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/quickstart",
|
||||
"title": "Getting Started",
|
||||
@ -520,6 +514,11 @@
|
||||
"title": "Upgrading Instructions",
|
||||
"tooltip": "Incrementally upgrade an AngularJS application to Angular."
|
||||
},
|
||||
{
|
||||
"url": "guide/upgrade-performance",
|
||||
"title": "Upgrading for Performance",
|
||||
"tooltip": "Upgrade from AngularJS to Angular in a more flexible way."
|
||||
},
|
||||
{
|
||||
"url": "guide/ajs-quick-reference",
|
||||
"title": "AngularJS-Angular Concepts",
|
||||
|
@ -175,7 +175,7 @@ This information is called _metadata_
|
||||
Some of the metadata is in the `@Component` decorators that you added to your component classes.
|
||||
Other critical metadata is in [`@NgModule`](guide/ngmodules) decorators.
|
||||
|
||||
The most important `@NgModule`decorator annotates the top-level **AppModule** class.
|
||||
The most important `@NgModule` decorator annotates the top-level **AppModule** class.
|
||||
|
||||
The Angular CLI generated an `AppModule` class in `src/app/app.module.ts` when it created the project.
|
||||
This is where you _opt-in_ to the `FormsModule`.
|
||||
|
@ -15,7 +15,7 @@
|
||||
// A random bad indexed page that used `api/api`
|
||||
{"type": 301, "source": "/api/api/:rest*", "destination": "/api/:rest*"},
|
||||
|
||||
// Guide renames
|
||||
// Guide renames/removals
|
||||
{"type": 301, "source": "/docs/*/latest/cli-quickstart.html", "destination": "/guide/quickstart"},
|
||||
{"type": 301, "source": "/docs/*/latest/glossary.html", "destination": "/guide/glossary"},
|
||||
{"type": 301, "source": "/docs/*/latest/quickstart.html", "destination": "/guide/quickstart"},
|
||||
@ -25,6 +25,7 @@
|
||||
{"type": 301, "source": "/guide/service-worker-getstart", "destination": "/guide/service-worker-getting-started"},
|
||||
{"type": 301, "source": "/guide/service-worker-comm", "destination": "/guide/service-worker-communications"},
|
||||
{"type": 301, "source": "/guide/service-worker-configref", "destination": "/guide/service-worker-config"},
|
||||
{"type": 301, "source": "/guide/webpack", "destination": "https://v5.angular.io/guide/webpack"},
|
||||
|
||||
// some top level guide pages on old site were moved below the guide folder
|
||||
{"type": 301, "source": "/styleguide", "destination": "/guide/styleguide"},
|
||||
|
@ -18,7 +18,7 @@
|
||||
"routing": {
|
||||
"index": "/index.html",
|
||||
"routes": {
|
||||
"^(?!/styleguide|/docs/.|(?:/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref)|learning-angular)|/news)(?:\\.html|/)?$|/testing|/api/(?:.+/[^/]+-|platform-browser/AnimationDriver|testing/|api/|animate/|(?:common/(?:NgModel|Control|MaxLengthValidator))|(?:[^/]+/)?(?:NgFor(?:$|-)|AnimationStateDeclarationMetadata|CORE_DIRECTIVES|PLATFORM_PIPES|DirectiveMetadata|HTTP_PROVIDERS))|.*/stackblitz(?:\\.html)?(?:\\?.*)?$|.*\\.[^\/.]+$)": {
|
||||
"^(?!/styleguide|/docs/.|(?:/guide/(?:cli-quickstart|metadata|ngmodule|service-worker-(?:getstart|comm|configref)|learning-angular|webpack)|/news)(?:\\.html|/)?$|/testing|/api/(?:.+/[^/]+-|platform-browser/AnimationDriver|testing/|api/|animate/|(?:common/(?:NgModel|Control|MaxLengthValidator))|(?:[^/]+/)?(?:NgFor(?:$|-)|AnimationStateDeclarationMetadata|CORE_DIRECTIVES|PLATFORM_PIPES|DirectiveMetadata|HTTP_PROVIDERS))|.*/stackblitz(?:\\.html)?(?:\\?.*)?$|.*\\.[^\/.]+$)": {
|
||||
"match": "regex"
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
"test-pwa-score": "node scripts/test-pwa-score",
|
||||
"example-e2e": "yarn example-check-local && node ./tools/examples/run-example-e2e",
|
||||
"example-lint": "tslint -c \"content/examples/tslint.json\" \"content/examples/**/*.ts\" -e \"content/examples/styleguide/**/*.avoid.ts\"",
|
||||
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared",
|
||||
"example-use-local": "node tools/ng-packages-installer overwrite ./tools/examples/shared --debug",
|
||||
"example-use-npm": "node tools/ng-packages-installer restore ./tools/examples/shared",
|
||||
"example-check-local": "node tools/ng-packages-installer check ./tools/examples/shared",
|
||||
"deploy-preview": "scripts/deploy-preview.sh",
|
||||
@ -112,7 +112,7 @@
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "^0.26.1",
|
||||
"dgeni-packages": "^0.26.2",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
|
@ -2,11 +2,11 @@
|
||||
"aio": {
|
||||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 2689,
|
||||
"main": 478529,
|
||||
"runtime": 2768,
|
||||
"main": 475855,
|
||||
"polyfills": 38453,
|
||||
"prettify": 14913
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,12 @@
|
||||
</a>
|
||||
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
|
||||
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
|
||||
<div class="toolbar-external-icons-container">
|
||||
<a href="https://twitter.com/angular" title="Twitter">
|
||||
<img src="assets/images/logos/twitter-icon.svg"></a>
|
||||
<a href="https://github.com/angular/angular" title="GitHub">
|
||||
<img src="assets/images/logos/github-icon.svg"></a>
|
||||
</div>
|
||||
</mat-toolbar-row>
|
||||
</mat-toolbar>
|
||||
|
||||
@ -58,7 +64,7 @@
|
||||
</mat-sidenav-container>
|
||||
|
||||
<div *ngIf="hasFloatingToc" class="toc-container no-print" [style.max-height.px]="tocMaxHeight" (mousewheel)="restrainScrolling($event)">
|
||||
<aio-toc></aio-toc>
|
||||
<aio-lazy-ce selector="aio-toc"></aio-lazy-ce>
|
||||
</div>
|
||||
|
||||
<footer class="no-print">
|
||||
|
@ -6,7 +6,7 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { MatProgressBar, MatSidenav } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { timer } from 'rxjs';
|
||||
import { of, timer } from 'rxjs';
|
||||
import { first, mapTo } from 'rxjs/operators';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
@ -14,6 +14,7 @@ import { AppModule } from './app.module';
|
||||
import { DocumentService } from 'app/documents/document.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { ElementsLoader } from 'app/custom-elements/elements-loader';
|
||||
import { GaService } from 'app/shared/ga.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { Logger } from 'app/shared/logger.service';
|
||||
@ -26,7 +27,6 @@ import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { SelectComponent } from 'app/shared/select/select.component';
|
||||
import { TocComponent } from 'app/layout/toc/toc.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
|
||||
const sideBySideBreakPoint = 992;
|
||||
@ -92,11 +92,11 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
describe('hasFloatingToc', () => {
|
||||
it('should initially be true', () => {
|
||||
it('should initially be false', () => {
|
||||
const fixture2 = TestBed.createComponent(AppComponent);
|
||||
const component2 = fixture2.componentInstance;
|
||||
|
||||
expect(component2.hasFloatingToc).toBe(true);
|
||||
expect(component2.hasFloatingToc).toBe(false);
|
||||
});
|
||||
|
||||
it('should be false on narrow screens', () => {
|
||||
@ -621,55 +621,65 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
describe('aio-toc', () => {
|
||||
let tocDebugElement: DebugElement;
|
||||
let tocContainer: DebugElement|null;
|
||||
let tocContainer: HTMLElement|null;
|
||||
let toc: HTMLElement|null;
|
||||
|
||||
const setHasFloatingToc = (hasFloatingToc: boolean) => {
|
||||
component.hasFloatingToc = hasFloatingToc;
|
||||
fixture.detectChanges();
|
||||
|
||||
tocDebugElement = fixture.debugElement.query(By.directive(TocComponent));
|
||||
tocContainer = tocDebugElement && tocDebugElement.parent;
|
||||
tocContainer = fixture.debugElement.nativeElement.querySelector('.toc-container');
|
||||
toc = tocContainer && tocContainer.querySelector('aio-toc');
|
||||
};
|
||||
|
||||
beforeEach(() => setHasFloatingToc(true));
|
||||
|
||||
|
||||
it('should show/hide `<aio-toc>` based on `hasFloatingToc`', () => {
|
||||
expect(tocDebugElement).toBeTruthy();
|
||||
expect(tocContainer).toBeTruthy();
|
||||
|
||||
setHasFloatingToc(false);
|
||||
expect(tocDebugElement).toBeFalsy();
|
||||
expect(tocContainer).toBeFalsy();
|
||||
expect(toc).toBeFalsy();
|
||||
|
||||
setHasFloatingToc(true);
|
||||
expect(tocDebugElement).toBeTruthy();
|
||||
expect(tocContainer).toBeTruthy();
|
||||
expect(toc).toBeTruthy();
|
||||
|
||||
setHasFloatingToc(false);
|
||||
expect(tocContainer).toBeFalsy();
|
||||
expect(toc).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should have a non-embedded `<aio-toc>` element', () => {
|
||||
expect(tocDebugElement.classes['embedded']).toBeFalsy();
|
||||
setHasFloatingToc(true);
|
||||
expect(toc!.classList.contains('embedded')).toBe(false);
|
||||
});
|
||||
|
||||
it('should update the TOC container\'s `maxHeight` based on `tocMaxHeight`', () => {
|
||||
expect(tocContainer!.styles['max-height']).toBeNull();
|
||||
setHasFloatingToc(true);
|
||||
|
||||
expect(tocContainer!.style['max-height']).toBe('');
|
||||
|
||||
component.tocMaxHeight = '100';
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(tocContainer!.styles['max-height']).toBe('100px');
|
||||
expect(tocContainer!.style['max-height']).toBe('100px');
|
||||
});
|
||||
|
||||
it('should restrain scrolling inside the ToC container', () => {
|
||||
const restrainScrolling = spyOn(component, 'restrainScrolling');
|
||||
const evt = {};
|
||||
const evt = new MouseEvent('mousewheel');
|
||||
|
||||
setHasFloatingToc(true);
|
||||
expect(restrainScrolling).not.toHaveBeenCalled();
|
||||
|
||||
tocContainer!.triggerEventHandler('mousewheel', evt);
|
||||
tocContainer!.dispatchEvent(evt);
|
||||
expect(restrainScrolling).toHaveBeenCalledWith(evt);
|
||||
});
|
||||
|
||||
it('should not be loaded/registered until necessary', () => {
|
||||
const loader: TestElementsLoader = fixture.debugElement.injector.get(ElementsLoader);
|
||||
expect(loader.loadCustomElement).not.toHaveBeenCalled();
|
||||
|
||||
setHasFloatingToc(true);
|
||||
expect(loader.loadCustomElement).toHaveBeenCalledWith('aio-toc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('footer', () => {
|
||||
@ -1280,6 +1290,7 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
|
||||
imports: [ AppModule ],
|
||||
providers: [
|
||||
{ provide: APP_BASE_HREF, useValue: '/' },
|
||||
{ provide: ElementsLoader, useClass: TestElementsLoader },
|
||||
{ provide: GaService, useClass: TestGaService },
|
||||
{ provide: HttpClient, useClass: TestHttpClient },
|
||||
{ provide: LocationService, useFactory: () => mockLocationService },
|
||||
@ -1294,6 +1305,14 @@ function createTestingModule(initialUrl: string, mode: string = 'stable') {
|
||||
});
|
||||
}
|
||||
|
||||
class TestElementsLoader {
|
||||
loadContainedCustomElements = jasmine.createSpy('loadContainedCustomElements')
|
||||
.and.returnValue(of(undefined));
|
||||
|
||||
loadCustomElement = jasmine.createSpy('loadCustomElement')
|
||||
.and.returnValue(Promise.resolve());
|
||||
}
|
||||
|
||||
class TestGaService {
|
||||
locationChanged = jasmine.createSpy('locationChanged');
|
||||
}
|
||||
@ -1368,7 +1387,7 @@ class TestHttpClient {
|
||||
const id = match[1]!;
|
||||
// Make up a title for test purposes
|
||||
const title = id.split('/').pop()!.replace(/^([a-z])/, (_, letter) => letter.toUpperCase());
|
||||
const h1 = (id === 'no-title') ? '' : `<h1>${title}</h1>`;
|
||||
const h1 = (id === 'no-title') ? '' : `<h1 class="no-toc">${title}</h1>`;
|
||||
const contents = `${h1}<h2 id="#somewhere">Some heading</h2>`;
|
||||
data = { id, contents };
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ export class AppComponent implements OnInit {
|
||||
topMenuNodes: NavigationNode[];
|
||||
topMenuNarrowNodes: NavigationNode[];
|
||||
|
||||
hasFloatingToc = true;
|
||||
hasFloatingToc = false;
|
||||
private showFloatingToc = new BehaviorSubject(false);
|
||||
private showFloatingTocWidth = 800;
|
||||
tocMaxHeight: string;
|
||||
|
@ -32,7 +32,6 @@ import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { NotificationComponent } from 'app/layout/notification/notification.component';
|
||||
import { TocComponent } from 'app/layout/toc/toc.component';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
import { CurrentDateToken, currentDateProvider } from 'app/shared/current-date';
|
||||
import { WindowToken, windowProvider } from 'app/shared/window';
|
||||
@ -111,7 +110,6 @@ export const svgIconProviders = [
|
||||
NavItemComponent,
|
||||
SearchBoxComponent,
|
||||
NotificationComponent,
|
||||
TocComponent,
|
||||
TopMenuComponent,
|
||||
],
|
||||
providers: [
|
||||
@ -133,7 +131,6 @@ export const svgIconProviders = [
|
||||
{ provide: CurrentDateToken, useFactory: currentDateProvider },
|
||||
{ provide: WindowToken, useFactory: windowProvider },
|
||||
],
|
||||
entryComponents: [ TocComponent ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user