Compare commits
222 Commits
5.0.0-rc.1
...
labs/eleme
Author | SHA1 | Date | |
---|---|---|---|
21bfaf2265 | |||
94526930b1 | |||
4d42d9adca | |||
c1c9083d04 | |||
bb9565d5a2 | |||
46280177b6 | |||
118e5a56a8 | |||
fb61771e3e | |||
3db7112b89 | |||
2ea76cce31 | |||
b8bb2dd0f5 | |||
27ae0f9475 | |||
171dceb010 | |||
1ebc0d1e33 | |||
1d8e0758fa | |||
cd55643f85 | |||
486b8e6f69 | |||
215d373ebd | |||
4038b42396 | |||
9ab1f4a9c9 | |||
e9afc59a81 | |||
83c826c3f9 | |||
abfc41d661 | |||
11fd7eaf63 | |||
54eba606cb | |||
3337865913 | |||
f24397c5d0 | |||
9d52bf27de | |||
19fbfbc371 | |||
6578b30b77 | |||
10c1c2ba5a | |||
e18bfd1f3d | |||
3c2ddbe9ab | |||
a149605c9f | |||
b396029d39 | |||
040b376052 | |||
175c872514 | |||
71291aa2c0 | |||
415e75716a | |||
3216abee2e | |||
327ad02a28 | |||
1df0c9e1b0 | |||
280dadaaad | |||
c30eff898a | |||
ca129ba549 | |||
171ae154c2 | |||
4426c0f14e | |||
901436e46f | |||
6fbc2b3be0 | |||
93d23ddcc8 | |||
548a809a05 | |||
a161b4ab6d | |||
60b91656cd | |||
1858d99559 | |||
3a8665409d | |||
005a78bd83 | |||
7553ce9dfe | |||
42bfe4477f | |||
3bdbb18c8b | |||
13f8648a00 | |||
b6abcb2500 | |||
f1a9e1e361 | |||
54480f7dfc | |||
951bd33b09 | |||
bf57df3e04 | |||
420852e2f5 | |||
04eb80cc2b | |||
308fc8e328 | |||
9bb2939d68 | |||
2f63899be2 | |||
22c66f0e02 | |||
00bc80bb37 | |||
394d951883 | |||
98b26381f6 | |||
31797d3b50 | |||
957be960d2 | |||
18e9d86a3b | |||
a869aeecd2 | |||
eca822b756 | |||
17142a778a | |||
5adb7c9669 | |||
703fcda590 | |||
f16a7cd7e3 | |||
fde5f2fa14 | |||
d56724659f | |||
56b18ff063 | |||
7bfeac746e | |||
c92efc15fb | |||
b51d57deb8 | |||
c357b40dca | |||
a0ae120093 | |||
d3211a2468 | |||
adab4f3e49 | |||
82fed62af2 | |||
bde57016c6 | |||
b16f4bce98 | |||
6bed189e37 | |||
4abacb58f1 | |||
04200150d5 | |||
fc0b1d5b61 | |||
8d45fefc31 | |||
5da96c75a2 | |||
90d1423fb4 | |||
910735d732 | |||
fc86352adf | |||
441e01c568 | |||
14380ff086 | |||
820bb7bd8c | |||
230b98d4dd | |||
065ea926c0 | |||
9b9820858e | |||
b922743f6e | |||
04ab9f1917 | |||
25cbc98979 | |||
3861ba2929 | |||
3bcf0cf472 | |||
396c2417d9 | |||
fcfb1544e8 | |||
56774dfb79 | |||
c0cc6eeca1 | |||
6f2939da62 | |||
7d1abd9adb | |||
81173b0d29 | |||
b0c7ea8181 | |||
30ecb6e88a | |||
8d735da5d8 | |||
5a9ed2de27 | |||
41f57affb6 | |||
c569b75249 | |||
01e4aa5427 | |||
ad130d62d8 | |||
621f87b2bd | |||
64b36190de | |||
507290d30d | |||
15a8429b96 | |||
9723a362b6 | |||
d75a9fabdc | |||
9b264c5c78 | |||
60bdcd6f5f | |||
d035175cdb | |||
f42d317d2f | |||
405ccc7195 | |||
836c889baa | |||
43f9d917d9 | |||
62c7b7842b | |||
97969a85cd | |||
91fcfcb042 | |||
88c46feb20 | |||
c3f07b329f | |||
6121083ba5 | |||
717c68089d | |||
09b4244baf | |||
2e45267705 | |||
e81d1fc361 | |||
02394d2d80 | |||
653a211743 | |||
522ec9a25b | |||
0fdb7f7181 | |||
443ff33f63 | |||
6b7cead0c5 | |||
94a925a1b0 | |||
055b802713 | |||
41fec319c8 | |||
a22121d65d | |||
3acf9c7063 | |||
566606ef46 | |||
afa2bb246b | |||
dc038113a5 | |||
ad7e781a18 | |||
437e803f27 | |||
dbe6cdad7e | |||
46992b4bda | |||
d0af45c31a | |||
8b571309ed | |||
b0befd7376 | |||
dfa0973563 | |||
ac50bd678e | |||
9130505b57 | |||
931cf78057 | |||
6ade68cff1 | |||
d30ce19231 | |||
d7eac7ee56 | |||
d53b96f2a2 | |||
79deeacbd7 | |||
32ca6851af | |||
cff8c05442 | |||
0ea5f8b5ed | |||
555b1cdf29 | |||
0b0d25fa33 | |||
963a4d0dc8 | |||
f393d86c96 | |||
6651541230 | |||
43c5b638b9 | |||
c3a52697f5 | |||
c4704c8abc | |||
b1ca5d4ddf | |||
9fecd72f44 | |||
f4480d46b4 | |||
8e5b582b61 | |||
0cc87c5ba1 | |||
6aa7cc1d96 | |||
a8920eb774 | |||
fdfa31798b | |||
b67d574a95 | |||
1c77cdadaf | |||
f12e15e682 | |||
d5c9c5f183 | |||
f83989bb0d | |||
81167d9c4a | |||
0b06ea177a | |||
03227e65cf | |||
ca7f2f8c8f | |||
340d94afb9 | |||
9ef8d8b85a | |||
ab8ee4d0d9 | |||
d1a00459a8 | |||
9fe6363575 | |||
142a2b7341 | |||
6586265a0c | |||
d3fd088162 | |||
4b3f82a47e | |||
1279d75f7f |
@ -29,7 +29,7 @@ jobs:
|
||||
- restore_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
|
||||
- run: yarn install
|
||||
- run: yarn install --frozen-lockfile --non-interactive
|
||||
- run: ./node_modules/.bin/gulp lint
|
||||
|
||||
build:
|
||||
@ -42,6 +42,7 @@ jobs:
|
||||
|
||||
- run: bazel run @yarn//:yarn
|
||||
- run: bazel build packages/...
|
||||
- run: bazel test @angular//...
|
||||
- save_cache:
|
||||
key: angular-{{ .Branch }}-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
|
@ -7,9 +7,12 @@
|
||||
#
|
||||
# alexeagle - Alex Eagle
|
||||
# alxhub - Alex Rickabaugh
|
||||
# brocco - Mike Brocchi
|
||||
# chuckjaz - Chuck Jazdzewski
|
||||
# filipesilva - Filipe Silva
|
||||
# Foxandxss - Jesús Rodríguez
|
||||
# gkalpak - George Kalpakas
|
||||
# hansl - Hans Larsen
|
||||
# IgorMinar - Igor Minar
|
||||
# jasonaden - Jason Aden
|
||||
# juleskremer - Jules Kremer
|
||||
@ -101,7 +104,6 @@ groups:
|
||||
- vicb
|
||||
- IgorMinar #fallback
|
||||
|
||||
|
||||
core:
|
||||
conditions:
|
||||
files:
|
||||
@ -116,7 +118,7 @@ groups:
|
||||
animations:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/animation/*"
|
||||
- "packages/animations/*"
|
||||
- "packages/platform-browser/animations/*"
|
||||
users:
|
||||
- matsko #primary
|
||||
@ -145,11 +147,23 @@ groups:
|
||||
- mhevery
|
||||
- IgorMinar #fallback
|
||||
|
||||
compiler-cli/ngtools:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/compiler-cli/src/ngtools*"
|
||||
users:
|
||||
- hansl
|
||||
- filipesilva #fallback
|
||||
- brocco #fallback
|
||||
|
||||
compiler-cli:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/compiler-cli/*"
|
||||
- "packages/bazel/*"
|
||||
include:
|
||||
- "packages/compiler-cli/*"
|
||||
- "packages/bazel/*"
|
||||
exclude:
|
||||
- "packages/compiler-cli/src/ngtools*"
|
||||
users:
|
||||
- alexeagle
|
||||
- chuckjaz
|
||||
@ -255,7 +269,23 @@ groups:
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
service-worker:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/service-worker/*"
|
||||
users:
|
||||
- alxhub #primary
|
||||
- gkalpak
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
||||
elements:
|
||||
conditions:
|
||||
files:
|
||||
- "packages/elements/*"
|
||||
users:
|
||||
- mhevery #primary
|
||||
- IgorMinar #fallback
|
||||
|
||||
benchpress:
|
||||
conditions:
|
||||
|
@ -1,12 +1,10 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
# force trusty as Google Chrome addon is not supported on Precise
|
||||
dist: trusty
|
||||
node_js:
|
||||
- '6.9.5'
|
||||
|
||||
addons:
|
||||
chrome: stable
|
||||
# firefox: "38.0"
|
||||
apt:
|
||||
sources:
|
||||
@ -50,7 +48,8 @@ env:
|
||||
- CI_MODE=e2e_2
|
||||
- CI_MODE=js
|
||||
- CI_MODE=saucelabs_required
|
||||
- CI_MODE=browserstack_required
|
||||
# deactivated, see #19768
|
||||
# - CI_MODE=browserstack_required
|
||||
- CI_MODE=saucelabs_optional
|
||||
- CI_MODE=browserstack_optional
|
||||
- CI_MODE=aio_tools_test
|
||||
|
@ -12,6 +12,7 @@ filegroup(
|
||||
# This won't scale in the general case.
|
||||
# TODO(alexeagle): figure out what to do
|
||||
srcs = glob(["/".join(["node_modules", pkg, "**", ext]) for pkg in [
|
||||
"jasmine",
|
||||
"typescript",
|
||||
"zone.js",
|
||||
"rxjs",
|
||||
|
697
CHANGELOG.md
697
CHANGELOG.md
@ -1,318 +1,109 @@
|
||||
<a name="5.0.0-rc.1"></a>
|
||||
# [5.0.0-rc.1](https://github.com/angular/angular/compare/5.0.0-rc.0...5.0.0-rc.1) (2017-10-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** always emit summaries for jit with ng_module bazel rule. ([1058b2a](https://github.com/angular/angular/commit/1058b2a))
|
||||
* don’t rely on `goog.DEBUG` but on `COMPILED` instead ([db74f44](https://github.com/angular/angular/commit/db74f44))
|
||||
* **compiler:** add typings for `COMPILED`. ([0038712](https://github.com/angular/angular/commit/0038712))
|
||||
* **compiler:** also count generated files to determine whether to use single file emit ([7c5ecb5](https://github.com/angular/angular/commit/7c5ecb5))
|
||||
* **compiler:** disallow references for select and index evaluation ([f3f4c3d](https://github.com/angular/angular/commit/f3f4c3d))
|
||||
* **compiler:** don’t use `ng://` in AOT source maps, and never point to the original source file ([01f7112](https://github.com/angular/angular/commit/01f7112)), closes [#19538](https://github.com/angular/angular/issues/19538)
|
||||
* **compiler:** only don’t emit already emitted files in incremental compilation ([caa5195](https://github.com/angular/angular/commit/caa5195))
|
||||
* **compiler:** properly work on windows ([696af79](https://github.com/angular/angular/commit/696af79)), closes [#19492](https://github.com/angular/angular/issues/19492)
|
||||
* **compiler:** set `emitSkipped` to false for incremental compilation ([c412913](https://github.com/angular/angular/commit/c412913))
|
||||
* **service-worker:** several misc fixes for corner cases ([f10f8db](https://github.com/angular/angular/commit/f10f8db))
|
||||
* **upgrade:** call `ngOnInit()` after `ngOnChanges()` (on components with inputs) ([eef7d8a](https://github.com/angular/angular/commit/eef7d8a)), closes [#18913](https://github.com/angular/angular/issues/18913)
|
||||
* **upgrade:** correctly run change detection when `propagateDigest` is false ([617b3d2](https://github.com/angular/angular/commit/617b3d2))
|
||||
* **upgrade:** ensure downgraded components are destroyed in the Angular zone ([4e6aa9c](https://github.com/angular/angular/commit/4e6aa9c))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** don’t emit summaries for jit by default ([b086891](https://github.com/angular/angular/commit/b086891))
|
||||
* **compiler:** fix perf issue in loading aot summaries in jit compiler ([fbc9537](https://github.com/angular/angular/commit/fbc9537))
|
||||
* **compiler:** only emit changed files for incremental compilation ([745b59f](https://github.com/angular/angular/commit/745b59f))
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-rc.0"></a>
|
||||
# [5.0.0-rc.0](https://github.com/angular/angular/compare/5.0.0-beta.7...5.0.0-rc.0) (2017-09-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** properly support the query limit option value ([b54368b](https://github.com/angular/angular/commit/b54368b)), closes [#19232](https://github.com/angular/angular/issues/19232)
|
||||
* **common:** use correct pipe name in error messages ([#19403](https://github.com/angular/angular/issues/19403)) ([f9b0863](https://github.com/angular/angular/commit/f9b0863)), closes [#19373](https://github.com/angular/angular/issues/19373)
|
||||
* **compiler:** add parens around binary / ternary expressions ([3799f43](https://github.com/angular/angular/commit/3799f43))
|
||||
* **compiler:** allow to use flat modules and summaries ([ec2be5d](https://github.com/angular/angular/commit/ec2be5d))
|
||||
* **compiler:** allow to use lowering with `export *`. ([e31a76c](https://github.com/angular/angular/commit/e31a76c))
|
||||
* **compiler:** also create `.ngfactory.js` files in non obvious cases ([#19301](https://github.com/angular/angular/issues/19301)) ([1a647c3](https://github.com/angular/angular/commit/1a647c3))
|
||||
* **compiler:** collect non exported symbols in d.ts files ([#19301](https://github.com/angular/angular/issues/19301)) ([62602b9](https://github.com/angular/angular/commit/62602b9))
|
||||
* **compiler:** correctly derive `fileExists` for generated files ([#19301](https://github.com/angular/angular/issues/19301)) ([f2bad19](https://github.com/angular/angular/commit/f2bad19))
|
||||
* **compiler:** correctly map error message locations ([#19424](https://github.com/angular/angular/issues/19424)) ([ff5b050](https://github.com/angular/angular/commit/ff5b050))
|
||||
* **compiler:** do not consider a reference with members as a reference ([#19454](https://github.com/angular/angular/issues/19454)) ([b3db3f8](https://github.com/angular/angular/commit/b3db3f8))
|
||||
* **compiler:** don’t lower property accesses of exported symbols ([#19301](https://github.com/angular/angular/issues/19301)) ([45747ed](https://github.com/angular/angular/commit/45747ed))
|
||||
* **compiler:** don’t type check property access of literal maps ([#19301](https://github.com/angular/angular/issues/19301)) ([04997c8](https://github.com/angular/angular/commit/04997c8))
|
||||
* **compiler:** implement i18n with new compiler ([627f048](https://github.com/angular/angular/commit/627f048)), closes [#19429](https://github.com/angular/angular/issues/19429)
|
||||
* **compiler:** make sure our out path calculation is correct ([2f6ae52](https://github.com/angular/angular/commit/2f6ae52))
|
||||
* **compiler:** make sure to detect paths that start with `rootDir` correctly ([bb1665c](https://github.com/angular/angular/commit/bb1665c)), closes [#19362](https://github.com/angular/angular/issues/19362)
|
||||
* **compiler:** make watch mode work with `declaration: false` ([7c1d3e0](https://github.com/angular/angular/commit/7c1d3e0)), closes [#19464](https://github.com/angular/angular/issues/19464)
|
||||
* **compiler:** remove deprecated `Compiler.ngGetContentSelectors()` ([#19347](https://github.com/angular/angular/issues/19347)) ([f57b7df](https://github.com/angular/angular/commit/f57b7df))
|
||||
* **compiler:** skip when trimming / removing whitespaces ([#19310](https://github.com/angular/angular/issues/19310)) ([13613d4](https://github.com/angular/angular/commit/13613d4)), closes [#19304](https://github.com/angular/angular/issues/19304)
|
||||
* **compiler:** support `noResolve` ([#19301](https://github.com/angular/angular/issues/19301)) ([c76da27](https://github.com/angular/angular/commit/c76da27))
|
||||
* **compiler:** various squashed fixes for the new ngc ([a8a9660](https://github.com/angular/angular/commit/a8a9660))
|
||||
* **compiler:** work well with `forwardRef` with `useValue` / `useFactory` ([1dacae2](https://github.com/angular/angular/commit/1dacae2))
|
||||
* **compiler-cli:** do not validate metadata from declaration files ([#19324](https://github.com/angular/angular/issues/19324)) ([4767902](https://github.com/angular/angular/commit/4767902)), closes [#18867](https://github.com/angular/angular/issues/18867)
|
||||
* **compiler-cli:** don't join errors with comma ([#19331](https://github.com/angular/angular/issues/19331)) ([e889c68](https://github.com/angular/angular/commit/e889c68))
|
||||
* **compiler-cli:** don't rewrite imports when annotating for closure ([#19444](https://github.com/angular/angular/issues/19444)) ([f24ea59](https://github.com/angular/angular/commit/f24ea59))
|
||||
* create proper externs so that closure does not clobber e.g. `ng` for internal variables ([#19423](https://github.com/angular/angular/issues/19423)) ([b21a1d1](https://github.com/angular/angular/commit/b21a1d1))
|
||||
* **compiler-cli:** set source file ranges in node emitter ([#19348](https://github.com/angular/angular/issues/19348)) ([27c6638](https://github.com/angular/angular/commit/27c6638))
|
||||
* **compiler-cli:** update ngtools2 EmitFlags ([#19375](https://github.com/angular/angular/issues/19375)) ([e224e3d](https://github.com/angular/angular/commit/e224e3d))
|
||||
* **core:** make dynamic & inline code checking behave the same ([#19189](https://github.com/angular/angular/issues/19189)) ([473a577](https://github.com/angular/angular/commit/473a577))
|
||||
* **http:** introduce named type for HttpParams options ([#19360](https://github.com/angular/angular/issues/19360)) ([8a0e458](https://github.com/angular/angular/commit/8a0e458))
|
||||
* **language-service:** do not report errors for `OpaqueToken` ([#19427](https://github.com/angular/angular/issues/19427)) ([c1b029a](https://github.com/angular/angular/commit/c1b029a))
|
||||
* **router:** fix activation events toString and docs ([#19147](https://github.com/angular/angular/issues/19147)) ([2c4107c](https://github.com/angular/angular/commit/2c4107c))
|
||||
* **router:** resolve and guards should be able to reject with null and undefined ([#19418](https://github.com/angular/angular/issues/19418)) ([a9d32a3](https://github.com/angular/angular/commit/a9d32a3)), closes [#17148](https://github.com/angular/angular/issues/17148)
|
||||
* **tsc-wrapped:** deduplicate metadata only when the module is the same ([#19249](https://github.com/angular/angular/issues/19249)) ([b6b18c1](https://github.com/angular/angular/commit/b6b18c1)), closes [#19219](https://github.com/angular/angular/issues/19219)
|
||||
* don’t use the global `ng` at all with closure enhanced optimizations ([a7798f2](https://github.com/angular/angular/commit/a7798f2))
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** support negative query limit values ([86ffacf](https://github.com/angular/angular/commit/86ffacf)), closes [#19259](https://github.com/angular/angular/issues/19259)
|
||||
* **compiler:** enabled strict checking of parameters to an `@Injectable` ([#19412](https://github.com/angular/angular/issues/19412)) ([dfb8d21](https://github.com/angular/angular/commit/dfb8d21))
|
||||
* **compiler:** reuse the TypeScript typecheck for template typechecking. ([#19152](https://github.com/angular/angular/issues/19152)) ([996c7c2](https://github.com/angular/angular/commit/996c7c2))
|
||||
* **core:** support for bootstrap with custom zone ([#17672](https://github.com/angular/angular/issues/17672)) ([344a5ca](https://github.com/angular/angular/commit/344a5ca))
|
||||
* **platform-server:** add an API to transfer state from server ([#19134](https://github.com/angular/angular/issues/19134)) ([cfd9ca0](https://github.com/angular/angular/commit/cfd9ca0))
|
||||
* **service-worker:** introduce the [@angular](https://github.com/angular)/service-worker package ([#19274](https://github.com/angular/angular/issues/19274)) ([d442b68](https://github.com/angular/angular/commit/d442b68))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** make the creation of `ts.Program` faster. ([#19275](https://github.com/angular/angular/issues/19275)) ([edd5f5a](https://github.com/angular/angular/commit/edd5f5a))
|
||||
* **compiler:** only use tsickle if needed ([#19275](https://github.com/angular/angular/issues/19275)) ([8f95b75](https://github.com/angular/angular/commit/8f95b75))
|
||||
* **compiler:** speed up watch mode ([#19275](https://github.com/angular/angular/issues/19275)) ([6665d76](https://github.com/angular/angular/commit/6665d76))
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* **compiler:** The method `ngGetConentSelectors()`, deprecated in Angular 4.0, has been removed.
|
||||
Use `ComponentFactory.ngContentSelectors` instead.
|
||||
|
||||
<a name="4.4.4"></a>
|
||||
## [4.4.4](https://github.com/angular/angular/compare/4.4.3...4.4.4) (2017-09-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** support negative query limit value ([#19419](https://github.com/angular/angular/issues/19419)) ([bc81fbd](https://github.com/angular/angular/commit/bc81fbd)), closes [#19232](https://github.com/angular/angular/issues/19232)
|
||||
* **compiler:** correctly map error message locations ([#19424](https://github.com/angular/angular/issues/19424)) ([c3b39ba](https://github.com/angular/angular/commit/c3b39ba))
|
||||
* **compiler:** do not consider a reference with members as a reference ([#19466](https://github.com/angular/angular/issues/19466)) ([7fc2dce](https://github.com/angular/angular/commit/7fc2dce))
|
||||
* **compiler:** skip when trimming / removing whitespaces ([#19310](https://github.com/angular/angular/issues/19310)) ([c7aa8a1](https://github.com/angular/angular/commit/c7aa8a1)), closes [#19304](https://github.com/angular/angular/issues/19304)
|
||||
* **tsc-wrapped:** add metadata for `type` declarations ([#19040](https://github.com/angular/angular/issues/19040)) ([ae52851](https://github.com/angular/angular/commit/ae52851))
|
||||
|
||||
|
||||
<a name="4.4.3"></a>
|
||||
## [4.4.3](https://github.com/angular/angular/compare/4.4.2...4.4.3) (2017-09-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tsc-wrapped:** deduplicate metadata only when the module is the same ([#19261](https://github.com/angular/angular/issues/19261)) ([0371538](https://github.com/angular/angular/commit/0371538)), closes [#19219](https://github.com/angular/angular/issues/19219)
|
||||
|
||||
|
||||
<a name="4.4.2"></a>
|
||||
## [4.4.2](https://github.com/angular/angular/compare/4.4.1...4.4.2) (2017-09-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **platform-server**: fix for packaging issues [#19250](https://github.com/angular/angular/issues/19250)
|
||||
|
||||
|
||||
<a name="4.4.1"></a>
|
||||
## [4.4.1](https://github.com/angular/angular/compare/4.3.6...4.4.1) (2017-09-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** do not leak DOM nodes/styling for host triggered animations ([#18853](https://github.com/angular/angular/issues/18853)) ([1cc3fe2](https://github.com/angular/angular/commit/1cc3fe2)), closes [#18606](https://github.com/angular/angular/issues/18606)
|
||||
* **common:** fix improper packaging for [@angular](https://github.com/angular)/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||
* **common:** fix XSSI prefix stripping by using JSON.parse always ([#18466](https://github.com/angular/angular/issues/18466)) ([8821723](https://github.com/angular/angular/commit/8821723)), closes [#18396](https://github.com/angular/angular/issues/18396) [#18453](https://github.com/angular/angular/issues/18453)
|
||||
* **compiler:** normalize the locale name ([#18963](https://github.com/angular/angular/issues/18963)) ([497e017](https://github.com/angular/angular/commit/497e017))
|
||||
* **core:** complete EventEmitter in QueryList on component destroy ([#18902](https://github.com/angular/angular/issues/18902)) ([7d137d7](https://github.com/angular/angular/commit/7d137d7)), closes [#18741](https://github.com/angular/angular/issues/18741)
|
||||
* **tsc-wrapped:** deduplicate metadata for re-exported modules ([48ae1a6](https://github.com/angular/angular/commit/48ae1a6))
|
||||
* **tsc-wrapped:** fix metadata symbol reference ([f6a7183](https://github.com/angular/angular/commit/f6a7183))
|
||||
* **upgrade:** remove code setting id attribute. ([#19182](https://github.com/angular/angular/issues/19182)) ([b20c5d2](https://github.com/angular/angular/commit/b20c5d2)), closes [#18446](https://github.com/angular/angular/issues/18446)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** allow multiple exportAs names ([#18723](https://github.com/angular/angular/issues/18723)) ([7ec28fe](https://github.com/angular/angular/commit/7ec28fe))
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([#18823](https://github.com/angular/angular/issues/18823)) ([b8b551c](https://github.com/angular/angular/commit/b8b551c))
|
||||
|
||||
|
||||
Note: the 4.4.0 release on npm accidentally glitched-out midway, so we cut 4.4.1 instead. oops :-)
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.7"></a>
|
||||
## [5.0.0-beta.7](https://github.com/angular/angular/compare/5.0.0-beta.6...5.0.0-beta.7) (2017-09-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** emit preamble in generated files. ([b1055a5](https://github.com/angular/angular/commit/b1055a5))
|
||||
* **compiler:** fix bazel integration and make `perform-compile` more flexible ([a69172f](https://github.com/angular/angular/commit/a69172f))
|
||||
* **compiler:** lower variables with a closure by exporting the variable. ([5ef6e63](https://github.com/angular/angular/commit/5ef6e63))
|
||||
* **platform-browser:** run BLACK_LISTED_EVENTS outside of ngZone ([#18993](https://github.com/angular/angular/issues/18993)) ([d52f426](https://github.com/angular/angular/commit/d52f426))
|
||||
* **platform-browser:** simple version of zone aware addEventListener ([#18993](https://github.com/angular/angular/issues/18993)) ([ed1175f](https://github.com/angular/angular/commit/ed1175f))
|
||||
* **platform-server:** support setting innerText property ([831613a](https://github.com/angular/angular/commit/831613a))
|
||||
* **router:** adjust ChildActivation events to only fire when the child is actually changing ([#19043](https://github.com/angular/angular/issues/19043)) ([66f0ab0](https://github.com/angular/angular/commit/66f0ab0)), closes [#18942](https://github.com/angular/angular/issues/18942)
|
||||
* **tsc-wrapped:** deduplicate metadata for re-exported modules ([c056b8c](https://github.com/angular/angular/commit/c056b8c))
|
||||
* **tsc-wrapped:** fix metadata symbol reference ([626555c](https://github.com/angular/angular/commit/626555c))
|
||||
* **upgrade:** add testability hook to downgraded component ([97cc6ca](https://github.com/angular/angular/commit/97cc6ca))
|
||||
* **upgrade:** remove code setting id attribute. ([b6833d1](https://github.com/angular/angular/commit/b6833d1)), closes [#18446](https://github.com/angular/angular/issues/18446)
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **router:** remove deprecated `RouterOutlet` properties ([a9ef858](https://github.com/angular/angular/commit/a9ef858))
|
||||
* update angular to support TypeScript 2.4 ([ca5aeba](https://github.com/angular/angular/commit/ca5aeba))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** deprecate i18n comments in favor of `ng-container` ([#18998](https://github.com/angular/angular/issues/18998)) ([66a5dab](https://github.com/angular/angular/commit/66a5dab))
|
||||
* **platform-server:** provide a way to hook into renderModule* ([#19023](https://github.com/angular/angular/issues/19023)) ([8dfc3c3](https://github.com/angular/angular/commit/8dfc3c3))
|
||||
* **router:** add ActivationStart/End events ([8f79150](https://github.com/angular/angular/commit/8f79150))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* - the Angular compiler now requires TypeScript 2.4.x.
|
||||
* router: `RouterOutlet` properties `locationInjector` and `locationFactoryResolver` have been removed as they were deprecated since v4.
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.6"></a>
|
||||
## [5.0.0-beta.6](https://github.com/angular/angular/compare/5.0.0-beta.5...5.0.0-beta.6) (2017-09-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** do not leak DOM nodes/styling for host triggered animations ([#18853](https://github.com/angular/angular/issues/18853)) ([fcadeb2](https://github.com/angular/angular/commit/fcadeb2)), closes [#18606](https://github.com/angular/angular/issues/18606)
|
||||
* **common:** fix a duplicate case in the locale switch ([#18941](https://github.com/angular/angular/issues/18941)) ([fdd5010](https://github.com/angular/angular/commit/fdd5010))
|
||||
* **common:** fix improper packaging for [@angular](https://github.com/angular)/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([65e26d7](https://github.com/angular/angular/commit/65e26d7))
|
||||
* **common:** fix XSSI prefix stripping by using JSON.parse always ([#18466](https://github.com/angular/angular/issues/18466)) ([452a7ae](https://github.com/angular/angular/commit/452a7ae)), closes [#18396](https://github.com/angular/angular/issues/18396) [#18453](https://github.com/angular/angular/issues/18453)
|
||||
* **common:** update closure-locale generation for tree shaking ([#18938](https://github.com/angular/angular/issues/18938)) ([946e5bd](https://github.com/angular/angular/commit/946e5bd))
|
||||
* **common:** use correct group separator for currency pipe ([#18932](https://github.com/angular/angular/issues/18932)) ([4ec5e28](https://github.com/angular/angular/commit/4ec5e28))
|
||||
* **common:** use v4 plurals when importing `DeprecatedI18NPipesModule` ([#18955](https://github.com/angular/angular/issues/18955)) ([30d53a8](https://github.com/angular/angular/commit/30d53a8))
|
||||
* **compiler:** always check summaries first before falling back to metadata from .d.ts files ([#18788](https://github.com/angular/angular/issues/18788)) ([f83b819](https://github.com/angular/angular/commit/f83b819))
|
||||
* **compiler:** always emit ngfactories with reexports ([#18788](https://github.com/angular/angular/issues/18788)) ([0262e37](https://github.com/angular/angular/commit/0262e37))
|
||||
* **compiler:** don’t emit stubs when we didn’t generate code for a file. ([#18788](https://github.com/angular/angular/issues/18788)) ([506d2e9](https://github.com/angular/angular/commit/506d2e9))
|
||||
* **compiler:** don’t reexport types in `.ngfactory` files ([#18788](https://github.com/angular/angular/issues/18788)) ([8c858d7](https://github.com/angular/angular/commit/8c858d7))
|
||||
* **compiler:** normalize the locale name ([#18963](https://github.com/angular/angular/issues/18963)) ([043f104](https://github.com/angular/angular/commit/043f104))
|
||||
* **compiler:** quote non identifiers in map keys. ([#18788](https://github.com/angular/angular/issues/18788)) ([2fbc92f](https://github.com/angular/angular/commit/2fbc92f))
|
||||
* **compiler:** treat absolute imports as package imports ([#18912](https://github.com/angular/angular/issues/18912)) ([fce7ae1](https://github.com/angular/angular/commit/fce7ae1))
|
||||
* **compiler:** use either summary or metadata information when reading .d.ts files ([#18912](https://github.com/angular/angular/issues/18912)) ([f1e526f](https://github.com/angular/angular/commit/f1e526f))
|
||||
* **compiler:** workaround bugs in TS when combining transformers ([#18912](https://github.com/angular/angular/issues/18912)) ([4059a72](https://github.com/angular/angular/commit/4059a72))
|
||||
* **compiler-cli:** fix memory leaks in watch mode ([#18961](https://github.com/angular/angular/issues/18961)) ([83e5deb](https://github.com/angular/angular/commit/83e5deb))
|
||||
* **compiler-cli:** use `--locale` parameter for transformers ([#18988](https://github.com/angular/angular/issues/18988)) ([22c4090](https://github.com/angular/angular/commit/22c4090))
|
||||
* **core:** complete EventEmitter in QueryList on component destroy ([#18902](https://github.com/angular/angular/issues/18902)) ([36d37cc](https://github.com/angular/angular/commit/36d37cc)), closes [#18741](https://github.com/angular/angular/issues/18741)
|
||||
* **tsc-wrapped:** decouple bundle index host from tsickle dependency ([#18999](https://github.com/angular/angular/issues/18999)) ([d1afadb](https://github.com/angular/angular/commit/d1afadb))
|
||||
* **upgrade:** deprecate the dynamic version of `ngUpgrade` ([450a13d](https://github.com/angular/angular/commit/450a13d))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **core:** remove deprecated `OpaqueToken` ([#18971](https://github.com/angular/angular/issues/18971)) ([3c4eef8](https://github.com/angular/angular/commit/3c4eef8))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **http**: deprecate @angular/http in favor of @angular/common/http ([#18906](https://github.com/angular/angular/issues/18906)) ([72c7b6e](https://github.com/angular/angular/commit/72c7b6e))
|
||||
* **common:** accept object map for HttpClient headers & params ([#18490](https://github.com/angular/angular/issues/18490)) ([1b1d5f1](https://github.com/angular/angular/commit/1b1d5f1))
|
||||
* **common:** generate `closure-locale.ts` to tree shake locale data ([#18907](https://github.com/angular/angular/issues/18907)) ([4878936](https://github.com/angular/angular/commit/4878936))
|
||||
* **compiler:** set `enableLegacyTemplate` to false by default ([#18756](https://github.com/angular/angular/issues/18756)) ([56238fe](https://github.com/angular/angular/commit/56238fe))
|
||||
* **compiler-cli:** add watch mode to `ngc` ([#18818](https://github.com/angular/angular/issues/18818)) ([cf7d47d](https://github.com/angular/angular/commit/cf7d47d))
|
||||
* **compiler-cli:** add watch mode to `ngc` ([#18818](https://github.com/angular/angular/issues/18818)) ([06d01b2](https://github.com/angular/angular/commit/06d01b2))
|
||||
* **compiler-cli:** lower metadata `useValue` and `data` literal fields ([#18905](https://github.com/angular/angular/issues/18905)) ([0e64261](https://github.com/angular/angular/commit/0e64261))
|
||||
* **compiler-cli:** lower metadata `useValue` and `data` literal fields ([#18905](https://github.com/angular/angular/issues/18905)) ([c685cc2](https://github.com/angular/angular/commit/c685cc2))
|
||||
* **platform-server:** provide a DOM implementation on the server ([2f2d5f3](https://github.com/angular/angular/commit/2f2d5f3)), closes [#14638](https://github.com/angular/angular/issues/14638)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* core: `OpaqueToken` has been removed as it was deprecated since v4. Use `InjectionToken` instead.
|
||||
* compiler: the compiler option `enableLegacyTemplate` is now disabled by default as the `<template>` element has been deprecated since v4. Use `<ng-template>` instead. The option `enableLegacyTemplate` and the `<template>` element will both be removed in Angular v6.
|
||||
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** allow multiple exportAs names ([#18723](https://github.com/angular/angular/issues/18723)) ([7ec28fe](https://github.com/angular/angular/commit/7ec28fe))
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([#18823](https://github.com/angular/angular/issues/18823)) ([b8b551c](https://github.com/angular/angular/commit/b8b551c))
|
||||
|
||||
|
||||
<a name="5.0.0-beta.5"></a>
|
||||
## [5.0.0-beta.5](https://github.com/angular/angular/compare/5.0.0-beta.4...5.0.0-beta.5) (2017-08-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure animations are disabled on the element containing the @.disabled flag ([#18714](https://github.com/angular/angular/issues/18714)) ([791c7ef](https://github.com/angular/angular/commit/791c7ef))
|
||||
* **animations:** make sure @.disabled respects disabled parent/sub animation sequences ([#18715](https://github.com/angular/angular/issues/18715)) ([e25f05a](https://github.com/angular/angular/commit/e25f05a))
|
||||
* **animations:** make sure animation cancellations respect AUTO style values ([#18787](https://github.com/angular/angular/issues/18787)) ([29aa8b3](https://github.com/angular/angular/commit/29aa8b3)), closes [#17450](https://github.com/angular/angular/issues/17450)
|
||||
* **animations:** resolve error when using AnimationBuilder with platform-server ([#18642](https://github.com/angular/angular/issues/18642)) ([845c68f](https://github.com/angular/angular/commit/845c68f)), closes [#18635](https://github.com/angular/angular/issues/18635)
|
||||
* **animations:** restore auto-style support for removed DOM nodes ([#18787](https://github.com/angular/angular/issues/18787)) ([7062811](https://github.com/angular/angular/commit/7062811))
|
||||
* **compiler-cli:** propagate preserveWhitespaces option to codegen ([#18773](https://github.com/angular/angular/issues/18773)) ([8ea6c56](https://github.com/angular/angular/commit/8ea6c56))
|
||||
* **compiler-cli:** use forward slashes for ts.resolveModuleName ([#18784](https://github.com/angular/angular/issues/18784)) ([e228f2c](https://github.com/angular/angular/commit/e228f2c))
|
||||
* **core:** correct order in ContentChildren query result ([#18326](https://github.com/angular/angular/issues/18326)) ([f53f724](https://github.com/angular/angular/commit/f53f724)), closes [#16568](https://github.com/angular/angular/issues/16568)
|
||||
* **core:** make sure onStable runs in the right zone ([#18706](https://github.com/angular/angular/issues/18706)) ([713d7c2](https://github.com/angular/angular/commit/713d7c2))
|
||||
* **tsc-wrapped:** add metadata for `type` declarations ([#18704](https://github.com/angular/angular/issues/18704)) ([6e3498c](https://github.com/angular/angular/commit/6e3498c)), closes [#18675](https://github.com/angular/angular/issues/18675)
|
||||
* **tsc-wrapped:** make `test.sh tools` run the tsc-wrapped tests again ([#18683](https://github.com/angular/angular/issues/18683)) ([2da45e6](https://github.com/angular/angular/commit/2da45e6))
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* **common:** remove deprecated `NgFor` ([#18758](https://github.com/angular/angular/issues/18758)) ([ec56760](https://github.com/angular/angular/commit/ec56760))
|
||||
* **common:** remove deprecated `NgTemplateOutlet#ngOutletContext` ([#18780](https://github.com/angular/angular/issues/18780)) ([7522987](https://github.com/angular/angular/commit/7522987))
|
||||
* **compiler:** remove option `useDebug` ([#18778](https://github.com/angular/angular/issues/18778)) ([499d05d](https://github.com/angular/angular/commit/499d05d))
|
||||
* **compiler:** split compiler and core ([#18683](https://github.com/angular/angular/issues/18683)) ([0cc77b4](https://github.com/angular/angular/commit/0cc77b4))
|
||||
* **core:** remove deprecated `ChangeDetectionRef` argument in `DifferFactory#create` ([#18757](https://github.com/angular/angular/issues/18757)) ([be9713c](https://github.com/angular/angular/commit/be9713c))
|
||||
* **core:** remove deprecated `DebugNode#source` ([#18779](https://github.com/angular/angular/issues/18779)) ([d61b902](https://github.com/angular/angular/commit/d61b902))
|
||||
* **core:** remove deprecated `Testability#findBindings` ([#18782](https://github.com/angular/angular/issues/18782)) ([f2a2a6b](https://github.com/angular/angular/commit/f2a2a6b))
|
||||
* **core:** remove deprecated `TrackByFn` ([#18757](https://github.com/angular/angular/issues/18757)) ([596e9f4](https://github.com/angular/angular/commit/596e9f4))
|
||||
* **core:** remove deprecated parameter for `ErrorHandler` ([#18759](https://github.com/angular/angular/issues/18759)) ([8f41326](https://github.com/angular/angular/commit/8f41326))
|
||||
* **platform-browser:** remove deprecated `NgProbeToken` ([#18760](https://github.com/angular/angular/issues/18760)) ([d7f42bf](https://github.com/angular/angular/commit/d7f42bf))
|
||||
* **platform-webworker:** remove deprecated `PRIMITIVE` ([#18761](https://github.com/angular/angular/issues/18761)) ([a56468c](https://github.com/angular/angular/commit/a56468c))
|
||||
* **router:** remove deprecated `initialNavigation` options ([#18781](https://github.com/angular/angular/issues/18781)) ([d76761b](https://github.com/angular/angular/commit/d76761b))
|
||||
* **router:** remove deprecated `RouterOutlet` properties ([#18781](https://github.com/angular/angular/issues/18781)) ([d1c4a94](https://github.com/angular/angular/commit/d1c4a94))
|
||||
|
||||
<a name="5.0.0"></a>
|
||||
# [5.0.0](https://github.com/angular/angular/compare/5.0.0-rc.9...5.0.0) pentagonal-donut (2017-11-01)
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** allow @.disabled property to work without an expression ([#18713](https://github.com/angular/angular/issues/18713)) ([2159342](https://github.com/angular/angular/commit/2159342))
|
||||
* **animations:** report errors when invalid CSS properties are detected ([#18718](https://github.com/angular/angular/issues/18718)) ([409688f](https://github.com/angular/angular/commit/409688f)), closes [#18701](https://github.com/angular/angular/issues/18701)
|
||||
* **animations:** support :increment and :decrement transition aliases ([6f45519](https://github.com/angular/angular/commit/6f45519))
|
||||
* **animations:** support negative query limit values ([86ffacf](https://github.com/angular/angular/commit/86ffacf)), closes [#19259](https://github.com/angular/angular/issues/19259)
|
||||
* **common:** accept object map for HttpClient headers & params ([#18490](https://github.com/angular/angular/issues/18490)) ([1b1d5f1](https://github.com/angular/angular/commit/1b1d5f1))
|
||||
* **common:** add an empty DeprecatedI18NPipesModule module ([#18737](https://github.com/angular/angular/issues/18737)) ([83713dd](https://github.com/angular/angular/commit/83713dd))
|
||||
* **common:** drop use of the Intl API to improve browser support ([#18284](https://github.com/angular/angular/issues/18284)) ([079d884](https://github.com/angular/angular/commit/079d884)), closes [#10809](https://github.com/angular/angular/issues/10809) [#9524](https://github.com/angular/angular/issues/9524) [#7008](https://github.com/angular/angular/issues/7008) [#9324](https://github.com/angular/angular/issues/9324) [#7590](https://github.com/angular/angular/issues/7590) [#6724](https://github.com/angular/angular/issues/6724) [#3429](https://github.com/angular/angular/issues/3429) [#17576](https://github.com/angular/angular/issues/17576) [#17478](https://github.com/angular/angular/issues/17478) [#17319](https://github.com/angular/angular/issues/17319) [#17200](https://github.com/angular/angular/issues/17200) [#16838](https://github.com/angular/angular/issues/16838) [#16624](https://github.com/angular/angular/issues/16624) [#16625](https://github.com/angular/angular/issues/16625) [#16591](https://github.com/angular/angular/issues/16591) [#14131](https://github.com/angular/angular/issues/14131) [#12632](https://github.com/angular/angular/issues/12632) [#11376](https://github.com/angular/angular/issues/11376) [#11187](https://github.com/angular/angular/issues/11187)
|
||||
* **compiler:** allow multiple exportAs names ([3a50098](https://github.com/angular/angular/commit/3a50098))
|
||||
* **common:** generate `closure-locale.ts` to tree shake locale data ([#18907](https://github.com/angular/angular/issues/18907)) ([4878936](https://github.com/angular/angular/commit/4878936))
|
||||
* **common:** mark NgTemplateOutlet API as stable ([0a73e8d](https://github.com/angular/angular/commit/0a73e8d))
|
||||
* **compiler-cli:** add watch mode to `ngc` ([#18818](https://github.com/angular/angular/issues/18818)) ([06d01b2](https://github.com/angular/angular/commit/06d01b2))
|
||||
* **compiler-cli:** lower metadata `useValue` and `data` literal fields ([#18905](https://github.com/angular/angular/issues/18905)) ([0e64261](https://github.com/angular/angular/commit/0e64261))
|
||||
* **compiler:** add representation of placeholders to xliff & xmb ([b3085e9](https://github.com/angular/angular/commit/b3085e9)), closes [#17345](https://github.com/angular/angular/issues/17345)
|
||||
* **compiler:** allow multiple exportAs names ([#18723](https://github.com/angular/angular/issues/18723)) ([7ec28fe](https://github.com/angular/angular/commit/7ec28fe))
|
||||
* **compiler:** deprecate i18n comments in favor of `ng-container` ([#18998](https://github.com/angular/angular/issues/18998)) ([66a5dab](https://github.com/angular/angular/commit/66a5dab))
|
||||
* **compiler:** enabled strict checking of parameters to an `@Injectable` ([#19412](https://github.com/angular/angular/issues/19412)) ([dfb8d21](https://github.com/angular/angular/commit/dfb8d21))
|
||||
* **compiler:** make `.ngsummary.json` files portable ([2572bf5](https://github.com/angular/angular/commit/2572bf5))
|
||||
* **compiler:** reuse the TypeScript typecheck for template typechecking. ([#19152](https://github.com/angular/angular/issues/19152)) ([996c7c2](https://github.com/angular/angular/commit/996c7c2))
|
||||
* **compiler:** set `enableLegacyTemplate` to false by default ([#18756](https://github.com/angular/angular/issues/18756)) ([56238fe](https://github.com/angular/angular/commit/56238fe))
|
||||
* **compiler:** use typescript for resolving resource paths ([43226cb](https://github.com/angular/angular/commit/43226cb))
|
||||
* **core:** Create StaticInjector which does not depend on Reflect polyfill. ([d9d00bd](https://github.com/angular/angular/commit/d9d00bd))
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([#18823](https://github.com/angular/angular/issues/18823)) ([b8b551c](https://github.com/angular/angular/commit/b8b551c))
|
||||
* **core:** support for bootstrap with custom zone ([#17672](https://github.com/angular/angular/issues/17672)) ([344a5ca](https://github.com/angular/angular/commit/344a5ca))
|
||||
* **forms:** add default updateOn values for groups and arrays ([#18536](https://github.com/angular/angular/issues/18536)) ([ff5c58b](https://github.com/angular/angular/commit/ff5c58b))
|
||||
* **forms:** add options arg to abstract controls ([ebef5e6](https://github.com/angular/angular/commit/ebef5e6))
|
||||
* **forms:** add status to `AbstractControlDirective` ([233ef93](https://github.com/angular/angular/commit/233ef93))
|
||||
* **forms:** add updateOn and ngFormOptions to NgForm ([0d45828](https://github.com/angular/angular/commit/0d45828))
|
||||
* **forms:** add updateOn blur option to FormControls ([#18408](https://github.com/angular/angular/issues/18408)) ([333a708](https://github.com/angular/angular/commit/333a708)), closes [#7113](https://github.com/angular/angular/issues/7113)
|
||||
* **forms:** add updateOn submit option to FormControls ([#18514](https://github.com/angular/angular/issues/18514)) ([f69561b](https://github.com/angular/angular/commit/f69561b))
|
||||
* **forms:** add updateOn support to ngModelOptions ([1cfa79c](https://github.com/angular/angular/commit/1cfa79c))
|
||||
* **http**: deprecate @angular/http in favor of @angular/common/http ([#18906](https://github.com/angular/angular/issues/18906)) ([72c7b6e](https://github.com/angular/angular/commit/72c7b6e))
|
||||
* **platform-server:** add an API to transfer state from server ([#19134](https://github.com/angular/angular/issues/19134)) ([cfd9ca0](https://github.com/angular/angular/commit/cfd9ca0))
|
||||
* **platform-server:** provide a DOM implementation on the server ([2f2d5f3](https://github.com/angular/angular/commit/2f2d5f3)), closes [#14638](https://github.com/angular/angular/issues/14638)
|
||||
* **platform-server:** provide a way to hook into renderModule* ([#19023](https://github.com/angular/angular/issues/19023)) ([8dfc3c3](https://github.com/angular/angular/commit/8dfc3c3))
|
||||
* **router:** add ActivationStart/End events ([8f79150](https://github.com/angular/angular/commit/8f79150))
|
||||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
||||
* **service-worker:** introduce the [@angular](https://github.com/angular)/service-worker package ([#19274](https://github.com/angular/angular/issues/19274)) ([d442b68](https://github.com/angular/angular/commit/d442b68))
|
||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
||||
* update angular to support TypeScript 2.4 ([ca5aeba](https://github.com/angular/angular/commit/ca5aeba))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **animations:** reduce size of bundle by removing AST classes ([#19539](https://github.com/angular/angular/issues/19539)) ([d5c9c5f](https://github.com/angular/angular/commit/d5c9c5f))
|
||||
* **compiler:** don’t emit summaries for jit by default ([b086891](https://github.com/angular/angular/commit/b086891))
|
||||
* **compiler:** fix perf issue in loading aot summaries in jit compiler ([fbc9537](https://github.com/angular/angular/commit/fbc9537))
|
||||
* **compiler:** make the creation of `ts.Program` faster. ([#19275](https://github.com/angular/angular/issues/19275)) ([edd5f5a](https://github.com/angular/angular/commit/edd5f5a))
|
||||
* **compiler:** only emit changed files for incremental compilation ([745b59f](https://github.com/angular/angular/commit/745b59f))
|
||||
* **compiler:** only type check input files when using bazel ([#19581](https://github.com/angular/angular/issues/19581)) ([0b06ea1](https://github.com/angular/angular/commit/0b06ea1))
|
||||
* **compiler:** only use tsickle if needed ([#19275](https://github.com/angular/angular/issues/19275)) ([8f95b75](https://github.com/angular/angular/commit/8f95b75))
|
||||
* **compiler:** skip type check and emit in bazel in some cases. ([#19646](https://github.com/angular/angular/issues/19646)) ([a22121d](https://github.com/angular/angular/commit/a22121d))
|
||||
* **compiler:** speed up loading of summaries for bazel. ([#19581](https://github.com/angular/angular/issues/19581)) ([81167d9](https://github.com/angular/angular/commit/81167d9))
|
||||
* **compiler:** speed up watch mode ([#19275](https://github.com/angular/angular/issues/19275)) ([6665d76](https://github.com/angular/angular/commit/6665d76))
|
||||
* **core:** Remove decorator DSL which depends on Reflect ([cac130e](https://github.com/angular/angular/commit/cac130e))
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([d2c0d98](https://github.com/angular/angular/commit/d2c0d98))
|
||||
* **core:** use native addEventListener for faster rendering. ([#18107](https://github.com/angular/angular/issues/18107)) ([6279e50](https://github.com/angular/angular/commit/6279e50))
|
||||
* latest tsickle to tree shake: abstract class methods & interfaces ([#18236](https://github.com/angular/angular/issues/18236)) ([b7a6f52](https://github.com/angular/angular/commit/b7a6f52))
|
||||
* switch angular to use StaticInjector instead of ReflectiveInjector ([fcadbf4](https://github.com/angular/angular/commit/fcadbf4)), closes [#18496](https://github.com/angular/angular/issues/18496)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
#### Deprecated code
|
||||
* compiler: The method `ngGetContentSelectors()`, deprecated in Angular 4.0, has been removed.
|
||||
Use `ComponentFactory.ngContentSelectors` instead.
|
||||
* - the Angular compiler now requires TypeScript 2.4.x.
|
||||
* router: `RouterOutlet` properties `locationInjector` and `locationFactoryResolver` have been removed as they were deprecated since v4.
|
||||
* common: `NgFor` has been removed as it was deprecated since v4. Use `NgForOf` instead. This does not impact the use of`*ngFor` in your templates.
|
||||
* common: `NgTemplateOutlet#ngOutletContext` has been removed as it was deprecated since v4. Use `NgTemplateOutlet#ngTemplateOutletContext` instead.
|
||||
* core: `Testability#findBindings` has been removed as it was deprecated since v4. Use `Testability#findProviders` instead.
|
||||
* core: `DebugNode#source` has been removed as it was deprecated since v4.
|
||||
* router: the values `true`, `false`, `legacy_enabled` and `legacy_disabled` for the router parameter `initialNavigation` have been removed as they were deprecated. Use `enabled` or `disabled` instead.
|
||||
* core: `DifferFactory.create` no longer takes ChangeDetectionRef as a first argument as it was not used and deprecated since v4.
|
||||
* core: `TrackByFn` has been removed because it was deprecated since v4. Use `TrackByFunction` instead.
|
||||
* platform-webworker: `PRIMITIVE` has been removed as it was deprecated since v4. Use `SerializerTypes.PRIMITIVE` instead.
|
||||
* platform-browser: `NgProbeToken` has been removed from `@angular/platform-browser` as it was deprecated since v4. Import it from `@angular/core` instead.
|
||||
* core: `ErrorHandler` no longer takes a parameter as it was not used and deprecated since v4.
|
||||
* compiler: the option `useDebug` for the compiler has been removed as it had no effect and was deprecated since v4.
|
||||
* compiler: the compiler option `enableLegacyTemplate` is now disabled by default as the `<template>` element has been deprecated since v4. Use `<ng-template>` instead. The option `enableLegacyTemplate` and the `<template>` element will both be removed in Angular v6.
|
||||
* core: `OpaqueToken` has been removed as it was deprecated since v4. Use `InjectionToken` instead.
|
||||
* `platformXXXX()` no longer accepts providers which depend on reflection.
|
||||
Specifically the method signature when from `Provider[]` to
|
||||
`StaticProvider[]`.
|
||||
|
||||
Example:
|
||||
Before:
|
||||
```
|
||||
[
|
||||
MyClass,
|
||||
{provide: ClassA, useClass: SubClassA}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
[
|
||||
{provide: MyClass, deps: [Dep1,...]},
|
||||
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
|
||||
]
|
||||
```
|
||||
|
||||
NOTE: This only applies to platform creation and providers for the JIT
|
||||
compiler. It does not apply to `@Component` or `@NgModule` provides
|
||||
declarations.
|
||||
|
||||
Benchpress note: Previously Benchpress also supported reflective
|
||||
provides, which now require static providers.
|
||||
|
||||
|
||||
#### I18n Changes (@angular/common)
|
||||
Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR). Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes.
|
||||
|
||||
#### Other breaking changes
|
||||
* compiler: - `@angular/platform-server` now additionally depends on
|
||||
`@angular/platform-browser-dynamic` as a peer dependency.
|
||||
* common: Because of multiple bugs and browser inconsistencies, we have dropped the intl api in favor of data exported from the Unicode Common Locale Data Repository (CLDR).
|
||||
Unfortunately we had to change the i18n pipes (date, number, currency, percent) and there are some breaking changes.
|
||||
##### I18n pipes:
|
||||
- Breaking change:
|
||||
- By default Angular now only contains locale data for the language `en-US`, if you set the value of `LOCALE_ID` to another locale, you will have to import new locale data for this language because we don't use the intl API anymore.
|
||||
@ -386,6 +177,148 @@ Unfortunately we had to change the i18n pipes (date, number, currency, percent)
|
||||
- if you don't specify the number of digits to round to, the local format will be used (and it usually rounds numbers to 0 digits, instead of not rounding previously), e.g. `{{ 3.141592 | percent }}` will output `314%` for the locale `en-US` instead of `314.1592%` previously.
|
||||
|
||||
|
||||
### Deprecated code
|
||||
* router: `RouterOutlet` properties `locationInjector` and `locationFactoryResolver` have been removed as they were deprecated since v4.
|
||||
* common: `NgFor` has been removed as it was deprecated since v4. Use `NgForOf` instead. This does not impact the use of`*ngFor` in your templates.
|
||||
* common: `NgTemplateOutlet#ngOutletContext` has been removed as it was deprecated since v4. Use `NgTemplateOutlet#ngTemplateOutletContext` instead.
|
||||
* core: `Testability#findBindings` has been removed as it was deprecated since v4. Use `Testability#findProviders` instead.
|
||||
* core: `DebugNode#source` has been removed as it was deprecated since v4.
|
||||
* router: the values `true`, `false`, `legacy_enabled` and `legacy_disabled` for the router parameter `initialNavigation` have been removed as they were deprecated. Use `enabled` or `disabled` instead.
|
||||
* core: `DifferFactory.create` no longer takes ChangeDetectionRef as a first argument as it was not used and deprecated since v4.
|
||||
* core: `TrackByFn` has been removed because it was deprecated since v4. Use `TrackByFunction` instead.
|
||||
* platform-webworker: `PRIMITIVE` has been removed as it was deprecated since v4. Use `SerializerTypes.PRIMITIVE` instead.
|
||||
* platform-browser: `NgProbeToken` has been removed from `@angular/platform-browser` as it was deprecated since v4. Import it from `@angular/core` instead.
|
||||
* core: `ErrorHandler` no longer takes a parameter as it was not used and deprecated since v4.
|
||||
* compiler: the option `useDebug` for the compiler has been removed as it had no effect and was deprecated since v4.
|
||||
* common: remove deprecated `NgFor` ([#18758](https://github.com/angular/angular/issues/18758)) ([ec56760](https://github.com/angular/angular/commit/ec56760))
|
||||
* common: remove deprecated `NgTemplateOutlet#ngOutletContext` ([#18780](https://github.com/angular/angular/issues/18780)) ([7522987](https://github.com/angular/angular/commit/7522987))
|
||||
* compiler: remove option `useDebug` ([#18778](https://github.com/angular/angular/issues/18778)) ([499d05d](https://github.com/angular/angular/commit/499d05d))
|
||||
* compiler: split compiler and core ([#18683](https://github.com/angular/angular/issues/18683)) ([0cc77b4](https://github.com/angular/angular/commit/0cc77b4))
|
||||
* compiler: - `@angular/platform-server` now additionally depends on
|
||||
`@angular/platform-browser-dynamic` as a peer dependency.
|
||||
* core: remove deprecated `ChangeDetectionRef` argument in `DifferFactory#create` ([#18757](https://github.com/angular/angular/issues/18757)) ([be9713c](https://github.com/angular/angular/commit/be9713c))
|
||||
* core: remove deprecated `DebugNode#source` ([#18779](https://github.com/angular/angular/issues/18779)) ([d61b902](https://github.com/angular/angular/commit/d61b902))
|
||||
* core: remove deprecated `OpaqueToken` ([#18971](https://github.com/angular/angular/issues/18971)) ([3c4eef8](https://github.com/angular/angular/commit/3c4eef8))
|
||||
* core: remove deprecated `Testability#findBindings` ([#18782](https://github.com/angular/angular/issues/18782)) ([f2a2a6b](https://github.com/angular/angular/commit/f2a2a6b))
|
||||
* core: remove deprecated `TrackByFn` ([#18757](https://github.com/angular/angular/issues/18757)) ([596e9f4](https://github.com/angular/angular/commit/596e9f4))
|
||||
* core: remove deprecated parameter for `ErrorHandler` ([#18759](https://github.com/angular/angular/issues/18759)) ([8f41326](https://github.com/angular/angular/commit/8f41326))
|
||||
* platform-browser: remove deprecated `NgProbeToken` ([#18760](https://github.com/angular/angular/issues/18760)) ([d7f42bf](https://github.com/angular/angular/commit/d7f42bf))
|
||||
* platform-webworker: remove deprecated `PRIMITIVE` ([#18761](https://github.com/angular/angular/issues/18761)) ([a56468c](https://github.com/angular/angular/commit/a56468c))
|
||||
* router: remove deprecated `RouterOutlet` properties ([#18781](https://github.com/angular/angular/issues/18781)) ([d1c4a94](https://github.com/angular/angular/commit/d1c4a94))
|
||||
* router: remove deprecated `RouterOutlet` properties ([a9ef858](https://github.com/angular/angular/commit/a9ef858))
|
||||
* router: remove deprecated `initialNavigation` options ([#18781](https://github.com/angular/angular/issues/18781)) ([d76761b](https://github.com/angular/angular/commit/d76761b))
|
||||
- `ReflectiveInjector` is now deprecated as it will be remove. Use `Injector.create` as a replacement.
|
||||
|
||||
|
||||
|
||||
<a name="4.3.1"></a>
|
||||
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always camelcase style property names that contain auto styles ([383d896](https://github.com/angular/angular/commit/383d896)), closes [#17938](https://github.com/angular/angular/issues/17938)
|
||||
* **animations:** capture cancelled animation styles within grouped animations ([333ffd8](https://github.com/angular/angular/commit/333ffd8)), closes [#17170](https://github.com/angular/angular/issues/17170)
|
||||
* **animations:** do not crash animations if a nested component fires CD during CD ([4c1f32b](https://github.com/angular/angular/commit/4c1f32b)), closes [#18193](https://github.com/angular/angular/issues/18193)
|
||||
* **animations:** make sure @.disabled works in non-animation components ([a5c4bb5](https://github.com/angular/angular/commit/a5c4bb5))
|
||||
* **common:** send flushed body as error instead of null ([17b7bc3](https://github.com/angular/angular/commit/17b7bc3)), closes [#18181](https://github.com/angular/angular/issues/18181)
|
||||
* **compiler:** ensure jit external id arguments names are unique ([4671168](https://github.com/angular/angular/commit/4671168))
|
||||
* **compiler-cli:** don't generate empty `<target/>` when extracting xliff ([f0476fc](https://github.com/angular/angular/commit/f0476fc)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
||||
* **platform-server:** provide XhrFactory for HttpClient ([4ce29f3](https://github.com/angular/angular/commit/4ce29f3))
|
||||
* **router:** canDeactivate guards should run from bottom to top ([1ac78bf](https://github.com/angular/angular/commit/1ac78bf)), closes [#15657](https://github.com/angular/angular/issues/15657)
|
||||
* **router:** should navigate to the same url when config changes ([4340bea](https://github.com/angular/angular/commit/4340bea)), closes [#15535](https://github.com/angular/angular/issues/15535)
|
||||
* **router:** should run resolvers for the same route concurrently ([ec89f37](https://github.com/angular/angular/commit/ec89f37)), closes [#14279](https://github.com/angular/angular/issues/14279)
|
||||
* **router:** terminal route in custom matcher ([5d275e9](https://github.com/angular/angular/commit/5d275e9))
|
||||
|
||||
|
||||
<a name="4.4.6"></a>
|
||||
## [4.4.6](https://github.com/angular/angular/compare/4.4.5...4.4.6) (2017-10-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** properly support boolean-based transitions and state changes ([#19672](https://github.com/angular/angular/issues/19672)) ([f983a6c](https://github.com/angular/angular/commit/f983a6c)), closes [#9396](https://github.com/angular/angular/issues/9396) [#12337](https://github.com/angular/angular/issues/12337)
|
||||
* **common:** attempt to JSON.parse errors for JSON responses ([#19773](https://github.com/angular/angular/issues/19773)) ([269f5ac](https://github.com/angular/angular/commit/269f5ac))
|
||||
* **router:** RouterLinkActive should update its state right after checking the children ([53a807a](https://github.com/angular/angular/commit/53a807a)), closes [#18983](https://github.com/angular/angular/issues/18983)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **animations:** reduce size of bundle by removing AST classes ([#19673](https://github.com/angular/angular/issues/19673)) ([76d2496](https://github.com/angular/angular/commit/76d2496))
|
||||
|
||||
|
||||
|
||||
<a name="4.4.5"></a>
|
||||
## [4.4.5](https://github.com/angular/angular/compare/4.4.4...4.4.5) (2017-10-12)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** `TestBed.overrideProvider` should keep imported `NgModule`s eager ([#19624](https://github.com/angular/angular/issues/19624)) ([734378c](https://github.com/angular/angular/commit/734378c))
|
||||
* **compiler:** correctly instantiate eager providers that are used via `Injector.get` ([#19558](https://github.com/angular/angular/issues/19558)) ([e292548](https://github.com/angular/angular/commit/e292548)), closes [#15501](https://github.com/angular/angular/issues/15501)
|
||||
* **compiler:** disallow references for select and index evaluation ([95f3b1d](https://github.com/angular/angular/commit/95f3b1d))
|
||||
* **core:** make dynamic & inline code checking behave the same ([#19189](https://github.com/angular/angular/issues/19189)) ([6c66031](https://github.com/angular/angular/commit/6c66031))
|
||||
* **platform-browser:** support customEqualityTesters when overriding Jasmine toEqual ([cc8ae32](https://github.com/angular/angular/commit/cc8ae32))
|
||||
* **tsc-wrapped:** don't rewrite imports when annotating for closure ([#19579](https://github.com/angular/angular/issues/19579)) ([c9f8718](https://github.com/angular/angular/commit/c9f8718))
|
||||
|
||||
|
||||
<a name="4.4.4"></a>
|
||||
## [4.4.4](https://github.com/angular/angular/compare/4.4.3...4.4.4) (2017-09-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** support negative query limit value ([#19419](https://github.com/angular/angular/issues/19419)) ([bc81fbd](https://github.com/angular/angular/commit/bc81fbd)), closes [#19232](https://github.com/angular/angular/issues/19232)
|
||||
* **compiler:** correctly map error message locations ([#19424](https://github.com/angular/angular/issues/19424)) ([c3b39ba](https://github.com/angular/angular/commit/c3b39ba))
|
||||
* **compiler:** do not consider a reference with members as a reference ([#19466](https://github.com/angular/angular/issues/19466)) ([7fc2dce](https://github.com/angular/angular/commit/7fc2dce))
|
||||
* **compiler:** skip when trimming / removing whitespaces ([#19310](https://github.com/angular/angular/issues/19310)) ([c7aa8a1](https://github.com/angular/angular/commit/c7aa8a1)), closes [#19304](https://github.com/angular/angular/issues/19304)
|
||||
* **tsc-wrapped:** add metadata for `type` declarations ([#19040](https://github.com/angular/angular/issues/19040)) ([ae52851](https://github.com/angular/angular/commit/ae52851))
|
||||
|
||||
|
||||
<a name="4.4.3"></a>
|
||||
## [4.4.3](https://github.com/angular/angular/compare/4.4.2...4.4.3) (2017-09-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **tsc-wrapped:** deduplicate metadata only when the module is the same ([#19261](https://github.com/angular/angular/issues/19261)) ([0371538](https://github.com/angular/angular/commit/0371538)), closes [#19219](https://github.com/angular/angular/issues/19219)
|
||||
|
||||
|
||||
<a name="4.4.2"></a>
|
||||
## [4.4.2](https://github.com/angular/angular/compare/4.4.1...4.4.2) (2017-09-18)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **platform-server**: fix for packaging issues [#19250](https://github.com/angular/angular/issues/19250)
|
||||
|
||||
|
||||
<a name="4.4.1"></a>
|
||||
## [4.4.1](https://github.com/angular/angular/compare/4.3.6...4.4.1) (2017-09-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** do not leak DOM nodes/styling for host triggered animations ([#18853](https://github.com/angular/angular/issues/18853)) ([1cc3fe2](https://github.com/angular/angular/commit/1cc3fe2)), closes [#18606](https://github.com/angular/angular/issues/18606)
|
||||
* **common:** fix improper packaging for [@angular](https://github.com/angular)/common/http ([#18613](https://github.com/angular/angular/issues/18613)) ([a203a95](https://github.com/angular/angular/commit/a203a95))
|
||||
* **common:** fix XSSI prefix stripping by using JSON.parse always ([#18466](https://github.com/angular/angular/issues/18466)) ([8821723](https://github.com/angular/angular/commit/8821723)), closes [#18396](https://github.com/angular/angular/issues/18396) [#18453](https://github.com/angular/angular/issues/18453)
|
||||
* **compiler:** normalize the locale name ([#18963](https://github.com/angular/angular/issues/18963)) ([497e017](https://github.com/angular/angular/commit/497e017))
|
||||
* **core:** complete EventEmitter in QueryList on component destroy ([#18902](https://github.com/angular/angular/issues/18902)) ([7d137d7](https://github.com/angular/angular/commit/7d137d7)), closes [#18741](https://github.com/angular/angular/issues/18741)
|
||||
* **tsc-wrapped:** deduplicate metadata for re-exported modules ([48ae1a6](https://github.com/angular/angular/commit/48ae1a6))
|
||||
* **tsc-wrapped:** fix metadata symbol reference ([f6a7183](https://github.com/angular/angular/commit/f6a7183))
|
||||
* **upgrade:** remove code setting id attribute. ([#19182](https://github.com/angular/angular/issues/19182)) ([b20c5d2](https://github.com/angular/angular/commit/b20c5d2)), closes [#18446](https://github.com/angular/angular/issues/18446)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** allow multiple exportAs names ([#18723](https://github.com/angular/angular/issues/18723)) ([7ec28fe](https://github.com/angular/angular/commit/7ec28fe))
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([#18823](https://github.com/angular/angular/issues/18823)) ([b8b551c](https://github.com/angular/angular/commit/b8b551c))
|
||||
|
||||
|
||||
Note: the 4.4.0 release on npm accidentally glitched-out midway, so we cut 4.4.1 instead. oops :-)
|
||||
|
||||
|
||||
|
||||
<a name="4.3.6"></a>
|
||||
## [4.3.6](https://github.com/angular/angular/compare/4.3.5...4.3.6) (2017-08-23)
|
||||
|
||||
@ -408,31 +341,6 @@ Unfortunately we had to change the i18n pipes (date, number, currency, percent)
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.4"></a>
|
||||
## [5.0.0-beta.4](https://github.com/angular/angular/compare/5.0.0-beta.3...5.0.0-beta.4) (2017-08-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** Don't strip CSS source maps ([64b4be9](https://github.com/angular/angular/commit/64b4be9))
|
||||
* **forms:** re-assigning options should not clear select ([32ff21c](https://github.com/angular/angular/commit/32ff21c)), closes [#18330](https://github.com/angular/angular/issues/18330)
|
||||
* **language-service:** remove tsickle dependency ([bc22ff1](https://github.com/angular/angular/commit/bc22ff1))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **common:** mark NgTemplateOutlet API as stable ([0a73e8d](https://github.com/angular/angular/commit/0a73e8d))
|
||||
* **forms:** add status to `AbstractControlDirective` ([233ef93](https://github.com/angular/angular/commit/233ef93))
|
||||
* **forms:** add updateOn support to ngModelOptions ([1cfa79c](https://github.com/angular/angular/commit/1cfa79c))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **core:** add option to remove blank text nodes from compiled templates ([d2c0d98](https://github.com/angular/angular/commit/d2c0d98))
|
||||
* **core:** Remove decorator DSL which depends on Reflect ([cac130e](https://github.com/angular/angular/commit/cac130e))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.5"></a>
|
||||
## [4.3.5](https://github.com/angular/angular/compare/4.3.4...4.3.5) (2017-08-16)
|
||||
|
||||
@ -455,89 +363,6 @@ Unfortunately we had to change the i18n pipes (date, number, currency, percent)
|
||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([e7e7622](https://github.com/angular/angular/commit/e7e7622)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||
|
||||
|
||||
<a name="5.0.0-beta.3"></a>
|
||||
## [5.0.0-beta.3](https://github.com/angular/angular/compare/5.0.0-beta.2...5.0.0-beta.3) (2017-08-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** revert container/queried animations accordingly during cancel ([#18516](https://github.com/angular/angular/issues/18516)) ([c0c03dc](https://github.com/angular/angular/commit/c0c03dc))
|
||||
* **animations:** support persisting dynamic styles within animation states ([#18468](https://github.com/angular/angular/issues/18468)) ([05472cb](https://github.com/angular/angular/commit/05472cb)), closes [#18423](https://github.com/angular/angular/issues/18423) [#17505](https://github.com/angular/angular/issues/17505)
|
||||
* **benchpress:** compile cleanly with TS 2.4 ([#18455](https://github.com/angular/angular/issues/18455)) ([e25b3dd](https://github.com/angular/angular/commit/e25b3dd))
|
||||
* **common:** don't recreate view when context shape doesn't change ([#18277](https://github.com/angular/angular/issues/18277)) ([685cc26](https://github.com/angular/angular/commit/685cc26)), closes [#13407](https://github.com/angular/angular/issues/13407)
|
||||
* **compiler:** cleanly compile with TypeScript 2.4 ([#18456](https://github.com/angular/angular/issues/18456)) ([7c47b62](https://github.com/angular/angular/commit/7c47b62))
|
||||
* **compiler:** ignore [@import](https://github.com/import) in multi-line css ([#18452](https://github.com/angular/angular/issues/18452)) ([1dca575](https://github.com/angular/angular/commit/1dca575)), closes [#18038](https://github.com/angular/angular/issues/18038)
|
||||
* **compiler-cli:** disable buggy expression lowering ([#18513](https://github.com/angular/angular/issues/18513)) ([ca695e0](https://github.com/angular/angular/commit/ca695e0))
|
||||
* **compiler-cli:** fix and re-enable expression lowering ([#18570](https://github.com/angular/angular/issues/18570)) ([6f2038c](https://github.com/angular/angular/commit/6f2038c)), closes [#18388](https://github.com/angular/angular/issues/18388)
|
||||
* **compiler-cli:** modified ngc to throw all errors, not just syntax ([#18388](https://github.com/angular/angular/issues/18388)) ([5651e4a](https://github.com/angular/angular/commit/5651e4a))
|
||||
* **compiler-cli:** remove minimist dependency of compiler-cli/index ([#18532](https://github.com/angular/angular/issues/18532)) ([5b7432b](https://github.com/angular/angular/commit/5b7432b))
|
||||
* **core:** fix platform-browser-dynamic ([#18576](https://github.com/angular/angular/issues/18576)) ([f0a5501](https://github.com/angular/angular/commit/f0a5501))
|
||||
* **core:** forbid destroyed views to be inserted or moved in VC ([#18568](https://github.com/angular/angular/issues/18568)) ([e54bd59](https://github.com/angular/angular/commit/e54bd59)), closes [#17780](https://github.com/angular/angular/issues/17780)
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** Create StaticInjector which does not depend on Reflect polyfill. ([d9d00bd](https://github.com/angular/angular/commit/d9d00bd))
|
||||
* **forms:** add default updateOn values for groups and arrays ([#18536](https://github.com/angular/angular/issues/18536)) ([ff5c58b](https://github.com/angular/angular/commit/ff5c58b))
|
||||
* **forms:** add updateOn blur option to FormControls ([#18408](https://github.com/angular/angular/issues/18408)) ([333a708](https://github.com/angular/angular/commit/333a708)), closes [#7113](https://github.com/angular/angular/issues/7113)
|
||||
* **forms:** add updateOn submit option to FormControls ([#18514](https://github.com/angular/angular/issues/18514)) ([f69561b](https://github.com/angular/angular/commit/f69561b))
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* switch angular to use StaticInjector instead of ReflectiveInjector ([fcadbf4](https://github.com/angular/angular/commit/fcadbf4)), closes [#18496](https://github.com/angular/angular/issues/18496)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* `platformXXXX()` no longer accepts providers which depend on reflection.
|
||||
Specifically the method signature when from `Provider[]` to
|
||||
`StaticProvider[]`.
|
||||
|
||||
Example:
|
||||
Before:
|
||||
```
|
||||
[
|
||||
MyClass,
|
||||
{provide: ClassA, useClass: SubClassA}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
After:
|
||||
```
|
||||
[
|
||||
{provide: MyClass, deps: [Dep1,...]},
|
||||
{provide: ClassA, useClass: SubClassA, deps: [Dep1,...]}
|
||||
]
|
||||
```
|
||||
|
||||
NOTE: This only applies to platform creation and providers for the JIT
|
||||
compiler. It does not apply to `@Component` or `@NgModule` provides
|
||||
declarations.
|
||||
|
||||
Benchpress note: Previously Benchpress also supported reflective
|
||||
provides, which now require static providers.
|
||||
|
||||
DEPRECATION:
|
||||
|
||||
- `ReflectiveInjector` is now deprecated as it will be remove. Use
|
||||
`Injector.create` as a replacement.
|
||||
|
||||
<a name="5.0.0-beta.2"></a>
|
||||
## [5.0.0-beta.2](https://github.com/angular/angular/compare/5.0.0-beta.1...5.0.0-beta.2) (2017-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** do not consider arguments when determining recursion ([e64b54b](https://github.com/angular/angular/commit/e64b54b))
|
||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([381471d](https://github.com/angular/angular/commit/381471d)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **forms:** add options arg to abstract controls ([ebef5e6](https://github.com/angular/angular/commit/ebef5e6))
|
||||
* **router:** add events tracking activation of individual routes ([49cd851](https://github.com/angular/angular/commit/49cd851))
|
||||
|
||||
|
||||
<a name="4.3.3"></a>
|
||||
## [4.3.3](https://github.com/angular/angular/compare/4.3.2...4.3.3) (2017-08-02)
|
||||
|
||||
@ -546,35 +371,6 @@ DEPRECATION:
|
||||
* **compiler:** fix for element needing implicit parent placed in top-level ng-container ([f5cbc2e](https://github.com/angular/angular/commit/f5cbc2e)), closes [#18314](https://github.com/angular/angular/issues/18314)
|
||||
|
||||
|
||||
<a name="5.0.0-beta.1"></a>
|
||||
## [5.0.0-beta.1](https://github.com/angular/angular/compare/5.0.0-beta.0...5.0.0-beta.1) (2017-07-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** export BrowserModule as apart of BrowserAnimationsModule ([#18263](https://github.com/angular/angular/issues/18263)) ([fd0cc01](https://github.com/angular/angular/commit/fd0cc01))
|
||||
* **compiler:** add equiv & disp attributes to Xliff2 ICU placeholders ([#18283](https://github.com/angular/angular/issues/18283)) ([38ec05f](https://github.com/angular/angular/commit/38ec05f)), closes [#17344](https://github.com/angular/angular/issues/17344)
|
||||
* **compiler:** allow numbers for ICU message cases in lexer ([#18095](https://github.com/angular/angular/issues/18095)) ([a3a5429](https://github.com/angular/angular/commit/a3a5429)), closes [#17799](https://github.com/angular/angular/issues/17799)
|
||||
* **core:** invoke error handler outside of the Angular Zone ([#18269](https://github.com/angular/angular/issues/18269)) ([7ae7573](https://github.com/angular/angular/commit/7ae7573)), closes [#17073](https://github.com/angular/angular/issues/17073) [#7774](https://github.com/angular/angular/issues/7774)
|
||||
* **platform-server:** don't clobber parse5 properties when setting ([#18237](https://github.com/angular/angular/issues/18237)) ([a094769](https://github.com/angular/angular/commit/a094769)), closes [#17050](https://github.com/angular/angular/issues/17050)
|
||||
* **router:** child CanActivate guard should wait for parent to complete ([#18110](https://github.com/angular/angular/issues/18110)) ([086f4aa](https://github.com/angular/angular/commit/086f4aa)), closes [#15670](https://github.com/angular/angular/issues/15670)
|
||||
* **router:** should throw when lazy loaded module doesn't define any routes ([#15001](https://github.com/angular/angular/issues/15001)) ([82923a3](https://github.com/angular/angular/commit/82923a3)), closes [#14596](https://github.com/angular/angular/issues/14596)
|
||||
* **upgrade:** ensure downgraded components are created in the Angular zone ([#18209](https://github.com/angular/angular/issues/18209)) ([43c33d5](https://github.com/angular/angular/commit/43c33d5))
|
||||
* **upgrade:** throw error if trying to get injector before setting ([#18209](https://github.com/angular/angular/issues/18209)) ([d31dc7b](https://github.com/angular/angular/commit/d31dc7b))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** add representation of placeholders to xliff & xmb ([b3085e9](https://github.com/angular/angular/commit/b3085e9)), closes [#17345](https://github.com/angular/angular/issues/17345)
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* latest tsickle to tree shake: abstract class methods & interfaces ([#18236](https://github.com/angular/angular/issues/18236)) ([b7a6f52](https://github.com/angular/angular/commit/b7a6f52))
|
||||
* **core:** use native addEventListener for faster rendering. ([#18107](https://github.com/angular/angular/issues/18107)) ([6279e50](https://github.com/angular/angular/commit/6279e50))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.2"></a>
|
||||
## [4.3.2](https://github.com/angular/angular/compare/4.3.1...4.3.2) (2017-07-26)
|
||||
|
||||
@ -592,35 +388,6 @@ DEPRECATION:
|
||||
|
||||
|
||||
|
||||
<a name="5.0.0-beta.0"></a>
|
||||
## [5.0.0-beta.0](https://github.com/angular/angular/compare/4.3.0...5.0.0-beta.0) (2017-07-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always camelcase style property names that contain auto styles ([d22f8f5](https://github.com/angular/angular/commit/d22f8f5)), closes [#17938](https://github.com/angular/angular/issues/17938)
|
||||
* **animations:** capture cancelled animation styles within grouped animations ([23146c9](https://github.com/angular/angular/commit/23146c9)), closes [#17170](https://github.com/angular/angular/issues/17170)
|
||||
* **animations:** do not crash animations if a nested component fires CD during CD ([5db6f38](https://github.com/angular/angular/commit/5db6f38)), closes [#18193](https://github.com/angular/angular/issues/18193)
|
||||
* **animations:** make sure @.disabled works in non-animation components ([5344be5](https://github.com/angular/angular/commit/5344be5))
|
||||
* **common:** send flushed body as error instead of null ([5c62e30](https://github.com/angular/angular/commit/5c62e30)), closes [#18181](https://github.com/angular/angular/issues/18181)
|
||||
* **compiler:** ensure jit external id arguments names are unique ([95635c1](https://github.com/angular/angular/commit/95635c1))
|
||||
* **compiler-cli:** don't generate empty <target/> when extracting xliff ([65c9e13](https://github.com/angular/angular/commit/65c9e13)), closes [#15754](https://github.com/angular/angular/issues/15754)
|
||||
* **platform-server:** provide XhrFactory for HttpClient ([8076482](https://github.com/angular/angular/commit/8076482))
|
||||
* **router:** canDeactivate guards should run from bottom to top ([e20cfe1](https://github.com/angular/angular/commit/e20cfe1)), closes [#15657](https://github.com/angular/angular/issues/15657)
|
||||
* **router:** should navigate to the same url when config changes ([eb6fb5f](https://github.com/angular/angular/commit/eb6fb5f)), closes [#15535](https://github.com/angular/angular/issues/15535)
|
||||
* **router:** should run resolvers for the same route concurrently ([ad3029e](https://github.com/angular/angular/commit/ad3029e)), closes [#14279](https://github.com/angular/angular/issues/14279)
|
||||
* **router:** terminal route in custom matcher ([b399cb2](https://github.com/angular/angular/commit/b399cb2))
|
||||
* **upgrade:** allow accessing AngularJS injector from downgraded module ([a5205c6](https://github.com/angular/angular/commit/a5205c6))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **animations:** support :increment and :decrement transition aliases ([6f45519](https://github.com/angular/angular/commit/6f45519))
|
||||
* **upgrade:** propagate touched state of NgModelController ([59c23c7](https://github.com/angular/angular/commit/59c23c7))
|
||||
* **upgrade:** support lazy-loading Angular module into AngularJS app ([30e76fc](https://github.com/angular/angular/commit/30e76fc))
|
||||
|
||||
|
||||
|
||||
<a name="4.3.1"></a>
|
||||
## [4.3.1](https://github.com/angular/angular/compare/4.3.0...4.3.1) (2017-07-19)
|
||||
|
||||
|
@ -211,6 +211,7 @@ The following is the list of supported scopes:
|
||||
* **compiler**
|
||||
* **compiler-cli**
|
||||
* **core**
|
||||
* **elements**
|
||||
* **forms**
|
||||
* **http**
|
||||
* **language-service**
|
||||
@ -220,6 +221,7 @@ The following is the list of supported scopes:
|
||||
* **platform-webworker**
|
||||
* **platform-webworker-dynamic**
|
||||
* **router**
|
||||
* **service-worker**
|
||||
* **upgrade**
|
||||
|
||||
There are currently a few exceptions to the "use package name" rule:
|
||||
|
@ -5,7 +5,8 @@ load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")
|
||||
git_repository(
|
||||
name = "build_bazel_rules_nodejs",
|
||||
remote = "https://github.com/bazelbuild/rules_nodejs.git",
|
||||
tag = "0.1.6",
|
||||
# TODO(alexeagle): use the correct tag here.
|
||||
commit = "2c6243df53fd33fdab283ebdd01582e4eb815db8",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
||||
@ -20,4 +21,4 @@ local_repository(
|
||||
local_repository(
|
||||
name = "angular",
|
||||
path = "packages/bazel",
|
||||
)
|
||||
)
|
||||
|
@ -156,7 +156,7 @@ RUN find $AIO_SCRIPTS_SH_DIR -maxdepth 1 -type f -printf "%P\n" \
|
||||
# Set up the Node.js scripts
|
||||
COPY scripts-js/ $AIO_SCRIPTS_JS_DIR/
|
||||
WORKDIR $AIO_SCRIPTS_JS_DIR/
|
||||
RUN yarn install --production
|
||||
RUN yarn install --production --frozen-lockfile
|
||||
|
||||
|
||||
# Set up health check
|
||||
|
@ -9,7 +9,7 @@ readonly defaultImageNameAndTag="aio-builds:latest"
|
||||
# (Necessary, because only `scripts-js/dist/` is copied to the docker image.)
|
||||
(
|
||||
cd "$SCRIPTS_JS_DIR"
|
||||
yarn install
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
yarn build
|
||||
)
|
||||
|
||||
|
@ -7,6 +7,6 @@ source "`dirname $0`/_env.sh"
|
||||
# Test `scripts-js/`
|
||||
(
|
||||
cd "$SCRIPTS_JS_DIR"
|
||||
yarn install
|
||||
yarn install --frozen-lockfile --non-interactive
|
||||
yarn test
|
||||
)
|
||||
|
14
aio/content/examples/.gitignore
vendored
14
aio/content/examples/.gitignore
vendored
@ -2,6 +2,7 @@
|
||||
**/src/environments/environment.prod.ts
|
||||
**/src/environments/environment.ts
|
||||
**/src/assets/.gitkeep
|
||||
**/src/favicon.ico
|
||||
**/src/styles.css
|
||||
**/src/systemjs-angular-loader.js
|
||||
**/src/systemjs.config.js
|
||||
@ -29,11 +30,7 @@
|
||||
# built files
|
||||
*.map
|
||||
_test-output
|
||||
protractor-helpers.js
|
||||
*/e2e-spec.js
|
||||
**/*.js
|
||||
**/ts/**/*.js
|
||||
**/js-es6*/**/*.js
|
||||
dist/
|
||||
|
||||
|
||||
@ -57,10 +54,10 @@ dist/
|
||||
**/app/**/*.ajs.js
|
||||
|
||||
# aot
|
||||
*/aot/**/*
|
||||
!*/aot/bs-config.json
|
||||
!*/aot/index.html
|
||||
**/*.ngsummary.json
|
||||
!rollup-config.js
|
||||
aot-compiler/**/*.d.ts
|
||||
aot-compiler/**/*.factory.d.ts
|
||||
|
||||
# i18n
|
||||
!i18n/src/systemjs-text-plugin.js
|
||||
@ -76,6 +73,9 @@ dist/
|
||||
# styleguide
|
||||
!styleguide/src/systemjs.custom.js
|
||||
|
||||
# universal
|
||||
!universal/webpack.server.config.js
|
||||
|
||||
# plunkers
|
||||
*plnkr.no-link.html
|
||||
|
||||
|
@ -335,17 +335,17 @@ describe('Animation Tests', () => {
|
||||
});
|
||||
}
|
||||
|
||||
function getBoundingClientWidth(el: ElementFinder): promise.Promise<number> {
|
||||
function getBoundingClientWidth(el: ElementFinder) {
|
||||
return browser.executeScript(
|
||||
'return arguments[0].getBoundingClientRect().width',
|
||||
el.getWebElement()
|
||||
);
|
||||
) as PromiseLike<number>;
|
||||
}
|
||||
|
||||
function getOffsetWidth(el: ElementFinder): promise.Promise<number> {
|
||||
function getOffsetWidth(el: ElementFinder) {
|
||||
return browser.executeScript(
|
||||
'return arguments[0].offsetWidth',
|
||||
el.getWebElement()
|
||||
);
|
||||
) as PromiseLike<number>;
|
||||
}
|
||||
});
|
||||
|
@ -5,9 +5,6 @@
|
||||
<title>Animations</title>
|
||||
<base href="/">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!-- Polyfill for Web Animations -->
|
||||
<script src="https://unpkg.com/web-animations-js@2.2.1"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -7,7 +7,7 @@ import uglify from 'rollup-plugin-uglify';
|
||||
export default {
|
||||
entry: 'src/main.js',
|
||||
dest: 'src/build.js', // output a single application bundle
|
||||
sourceMap: false,
|
||||
sourceMap: true,
|
||||
format: 'iife',
|
||||
onwarn: function(warning) {
|
||||
// Skip certain warnings
|
||||
|
@ -1,6 +1,6 @@
|
||||
// #docregion
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
import { AppModuleNgFactory } from '../aot/src/app/app.module.ngfactory';
|
||||
import { AppModuleNgFactory } from './app/app.module.ngfactory';
|
||||
|
||||
console.log('Running AOT compiled');
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
||||
|
@ -20,7 +20,8 @@
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "aot",
|
||||
"skipMetadataEmit" : true
|
||||
}
|
||||
"annotationsAs": "decorators",
|
||||
"genDir": ".",
|
||||
"skipMetadataEmit" : true
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,10 @@ describe('Component Style Tests', function () {
|
||||
let componentH1 = element(by.css('app-root > h1'));
|
||||
let externalH1 = element(by.css('body > h1'));
|
||||
|
||||
expect(componentH1.getCssValue('fontWeight')).toEqual('normal');
|
||||
expect(externalH1.getCssValue('fontWeight')).not.toEqual('normal');
|
||||
// Note: sometimes webdriver returns the fontWeight as "normal",
|
||||
// othertimes as "400", both of which are equal in CSS terms.
|
||||
expect(componentH1.getCssValue('fontWeight')).toMatch(/normal|400/);
|
||||
expect(externalH1.getCssValue('fontWeight')).not.toMatch(/normal|400/);
|
||||
});
|
||||
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict'; // necessary for es6 output in node
|
||||
|
||||
import { browser, element, by } from 'protractor';
|
||||
const { version: angularVersion } = require('@angular/core/package.json');
|
||||
|
||||
describe('Pipes', function () {
|
||||
|
||||
@ -28,7 +29,11 @@ describe('Pipes', function () {
|
||||
|
||||
it('should be able to toggle birthday formats', function () {
|
||||
let birthDayEle = element(by.css('app-hero-birthday2 > p'));
|
||||
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/88`);
|
||||
if (angularVersion.indexOf('4.') === 0) { // Breaking change between v4 and v5 (https://github.com/angular/angular/commit/079d884)
|
||||
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/1988`);
|
||||
} else {
|
||||
expect(birthDayEle.getText()).toEqual(`The hero's birthday is 4/15/88`);
|
||||
}
|
||||
let buttonEle = element(by.cssContainingText('app-hero-birthday2 > button', 'Toggle Format'));
|
||||
expect(buttonEle.isDisplayed()).toBe(true);
|
||||
buttonEle.click().then(function() {
|
||||
|
@ -20,12 +20,12 @@ export class HeroService {
|
||||
|
||||
getHero(id: number) {
|
||||
return this.http.get(`api/heroes/${id}`)
|
||||
.map(response => response.json().data as Hero);
|
||||
.map(response => response.json() as Hero);
|
||||
}
|
||||
|
||||
getHeroes() {
|
||||
return this.http.get(`api/heroes`)
|
||||
.map(response => response.json().data as Hero[]);
|
||||
.map(response => response.json() as Hero[]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ export class HeroService {
|
||||
|
||||
getHeroes() {
|
||||
return this.http.get('api/heroes')
|
||||
.map((response: Response) => <Hero[]>response.json().data);
|
||||
.map((response: Response) => <Hero[]>response.json());
|
||||
}
|
||||
}
|
||||
// #enddocregion example
|
||||
|
@ -0,0 +1,3 @@
|
||||
{
|
||||
"projectType": "universal"
|
||||
}
|
||||
|
@ -1,62 +0,0 @@
|
||||
{
|
||||
"name": "toh-universal",
|
||||
"version": "1.0.0",
|
||||
"description": "Tour-of-Heroes application with ng-universal server-side rendering",
|
||||
"scripts": {
|
||||
"build": "tsc -p src/",
|
||||
"build:watch": "tsc -w",
|
||||
"build:aot": "webpack --config webpack.config.aot.js",
|
||||
"build:uni": "webpack --config webpack.config.uni.js",
|
||||
|
||||
"serve": "lite-server -c=bs-config.json",
|
||||
"serve:aot": "lite-server -c=bs-config.aot.js",
|
||||
"serve:uni": "node src/dist/server.js",
|
||||
"serve:uni2": "lite-server -c bs-config.uni.js",
|
||||
|
||||
"prestart": "npm run build",
|
||||
"start": "concurrently \"npm run build:watch\" \"npm run serve\"",
|
||||
|
||||
"lint": "tslint ./src/**/*.ts -t verbose",
|
||||
"ngc": "ngc",
|
||||
"clean": "rimraf src/dist && rimraf src/app/*.js* && rimraf src/uni/*.js* && rimraf src/main.js*"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/common": "angular/common-builds",
|
||||
"@angular/compiler": "angular/compiler-builds",
|
||||
"@angular/compiler-cli": "angular/compiler-cli-builds",
|
||||
"@angular/core": "angular/core-builds",
|
||||
"@angular/forms": "angular/forms-builds",
|
||||
"@angular/http": "angular/http-builds",
|
||||
"@angular/platform-browser": "angular/platform-browser-builds",
|
||||
"@angular/platform-browser-dynamic": "angular/platform-browser-dynamic-builds",
|
||||
"@angular/platform-server": "angular/platform-server-builds",
|
||||
"@angular/router": "angular/router-builds",
|
||||
|
||||
"angular-in-memory-web-api": "^0.3.1",
|
||||
"systemjs": "0.19.40",
|
||||
"core-js": "^2.4.1",
|
||||
"rxjs": "5.1.1",
|
||||
"zone.js": "^0.7.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"concurrently": "^3.2.0",
|
||||
"lite-server": "^2.2.2",
|
||||
"typescript": "~2.1.6",
|
||||
|
||||
"canonical-path": "0.0.2",
|
||||
"tslint": "^3.15.1",
|
||||
"lodash": "^4.16.4",
|
||||
"rimraf": "^2.5.4",
|
||||
|
||||
"@types/node": "^6.0.46",
|
||||
|
||||
"@ngtools/webpack": "^1.2.11",
|
||||
"@types/express": "^4.0.35",
|
||||
"raw-loader": "^0.5.1",
|
||||
"webpack": "^2.2.1"
|
||||
},
|
||||
"repository": {}
|
||||
}
|
61
aio/content/examples/universal/server.ts
Normal file
61
aio/content/examples/universal/server.ts
Normal file
@ -0,0 +1,61 @@
|
||||
// These are important and needed before anything else
|
||||
import 'zone.js/dist/zone-node';
|
||||
import 'reflect-metadata';
|
||||
|
||||
import { enableProdMode } from '@angular/core';
|
||||
|
||||
import * as express from 'express';
|
||||
import { join } from 'path';
|
||||
|
||||
// Faster server renders w/ Prod mode (dev mode never needed)
|
||||
enableProdMode();
|
||||
|
||||
// Express server
|
||||
const app = express();
|
||||
|
||||
const PORT = process.env.PORT || 4000;
|
||||
const DIST_FOLDER = join(process.cwd(), 'dist');
|
||||
|
||||
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
|
||||
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main.bundle');
|
||||
|
||||
// Express Engine
|
||||
import { ngExpressEngine } from '@nguniversal/express-engine';
|
||||
// Import module map for lazy loading
|
||||
import { provideModuleMap } from '@nguniversal/module-map-ngfactory-loader';
|
||||
|
||||
// #docregion ngExpressEngine
|
||||
app.engine('html', ngExpressEngine({
|
||||
bootstrap: AppServerModuleNgFactory,
|
||||
providers: [
|
||||
provideModuleMap(LAZY_MODULE_MAP)
|
||||
]
|
||||
}));
|
||||
// #enddocregion ngExpressEngine
|
||||
|
||||
app.set('view engine', 'html');
|
||||
app.set('views', join(DIST_FOLDER, 'browser'));
|
||||
|
||||
// #docregion data-request
|
||||
// TODO: implement data requests securely
|
||||
app.get('/api/*', (req, res) => {
|
||||
res.status(404).send('data requests are not supported');
|
||||
});
|
||||
// #enddocregion data-request
|
||||
|
||||
// #docregion static
|
||||
// Server static files from /browser
|
||||
app.get('*.*', express.static(join(DIST_FOLDER, 'browser')));
|
||||
// #enddocregion static
|
||||
|
||||
// #docregion navigation-request
|
||||
// All regular routes use the Universal engine
|
||||
app.get('*', (req, res) => {
|
||||
res.render(join(DIST_FOLDER, 'browser', 'index.html'), { req });
|
||||
});
|
||||
// #enddocregion navigation-request
|
||||
|
||||
// Start up the Node server
|
||||
app.listen(PORT, () => {
|
||||
console.log(`Node server listening on http://localhost:${PORT}`);
|
||||
});
|
@ -1,15 +1,15 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { HeroesComponent } from './heroes/heroes.component';
|
||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
|
||||
{ path: 'dashboard', component: DashboardComponent },
|
||||
{ path: 'dashboard', component: DashboardComponent },
|
||||
{ path: 'detail/:id', component: HeroDetailComponent },
|
||||
{ path: 'heroes', component: HeroesComponent }
|
||||
{ path: 'heroes', component: HeroesComponent }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* #docregion */
|
||||
/* AppComponent's private CSS styles */
|
||||
h1 {
|
||||
font-size: 1.2em;
|
||||
color: #999;
|
||||
|
@ -0,0 +1,7 @@
|
||||
<h1>{{title}}</h1>
|
||||
<nav>
|
||||
<a routerLink="/dashboard">Dashboard</a>
|
||||
<a routerLink="/heroes">Heroes</a>
|
||||
</nav>
|
||||
<router-outlet></router-outlet>
|
||||
<app-messages></app-messages>
|
@ -1,17 +1,8 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component } from '@angular/core';
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'my-app',
|
||||
template: `
|
||||
<h1>{{title}}</h1>
|
||||
<nav>
|
||||
<a routerLink="/dashboard" routerLinkActive="active">Dashboard</a>
|
||||
<a routerLink="/heroes" routerLinkActive="active">Heroes</a>
|
||||
</nav>
|
||||
<router-outlet></router-outlet>
|
||||
`,
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.css']
|
||||
})
|
||||
export class AppComponent {
|
||||
|
@ -1,54 +1,60 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
// #docregion v1, v2
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClientModule } from '@angular/common/http';
|
||||
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
// #enddocregion v1
|
||||
// Imports for loading & configuring the in-memory web api
|
||||
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
import { HttpClientInMemoryWebApiModule } from 'angular-in-memory-web-api';
|
||||
import { InMemoryDataService } from './in-memory-data.service';
|
||||
|
||||
// #docregion v1
|
||||
import { AppRoutingModule } from './app-routing.module';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
import { HeroDetailComponent } from './hero-detail.component';
|
||||
import { DashboardComponent } from './dashboard/dashboard.component';
|
||||
import { HeroDetailComponent } from './hero-detail/hero-detail.component';
|
||||
import { HeroesComponent } from './heroes/heroes.component';
|
||||
import { HeroSearchComponent } from './hero-search/hero-search.component';
|
||||
import { HeroService } from './hero.service';
|
||||
// #enddocregion v1, v2
|
||||
import { HeroSearchComponent } from './hero-search.component';
|
||||
// #docregion v1, v2
|
||||
import { MessageService } from './message.service';
|
||||
import { MessagesComponent } from './messages/messages.component';
|
||||
|
||||
// #docregion platform-detection
|
||||
import { PLATFORM_ID, APP_ID, Inject } from '@angular/core';
|
||||
import { isPlatformBrowser } from '@angular/common';
|
||||
|
||||
// #enddocregion platform-detection
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
BrowserModule.withServerTransition({
|
||||
appId: 'toh-universal'
|
||||
}),
|
||||
// #docregion browsermodule
|
||||
BrowserModule.withServerTransition({ appId: 'tour-of-heroes' }),
|
||||
// #enddocregion browsermodule
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
// #enddocregion v1
|
||||
// #docregion in-mem-web-api
|
||||
InMemoryWebApiModule.forRoot(InMemoryDataService),
|
||||
// #enddocregion in-mem-web-api
|
||||
// #docregion v1
|
||||
AppRoutingModule
|
||||
AppRoutingModule,
|
||||
HttpClientModule,
|
||||
HttpClientInMemoryWebApiModule.forRoot(
|
||||
InMemoryDataService, { dataEncapsulation: false }
|
||||
)
|
||||
],
|
||||
// #docregion search
|
||||
declarations: [
|
||||
AppComponent,
|
||||
DashboardComponent,
|
||||
HeroDetailComponent,
|
||||
HeroesComponent,
|
||||
// #enddocregion v1, v2
|
||||
HeroDetailComponent,
|
||||
MessagesComponent,
|
||||
HeroSearchComponent
|
||||
// #docregion v1, v2
|
||||
],
|
||||
// #enddocregion search
|
||||
providers: [ HeroService ],
|
||||
providers: [ HeroService, MessageService ],
|
||||
bootstrap: [ AppComponent ]
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {
|
||||
// #docregion platform-detection
|
||||
constructor(
|
||||
@Inject(PLATFORM_ID) private platformId: Object,
|
||||
@Inject(APP_ID) private appId: string) {
|
||||
const platform = isPlatformBrowser(platformId) ?
|
||||
'on the server' : 'in the browser';
|
||||
console.log(`Running ${platform} with appId=${appId}`);
|
||||
}
|
||||
// #enddocregion platform-detection
|
||||
}
|
||||
|
19
aio/content/examples/universal/src/app/app.server.module.ts
Normal file
19
aio/content/examples/universal/src/app/app.server.module.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { ServerModule } from '@angular/platform-server';
|
||||
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
AppModule,
|
||||
ServerModule,
|
||||
ModuleMapLoaderModule
|
||||
],
|
||||
providers: [
|
||||
// Add universal-only providers here
|
||||
],
|
||||
bootstrap: [ AppComponent ],
|
||||
})
|
||||
export class AppServerModule {}
|
@ -1,4 +1,4 @@
|
||||
/* #docregion */
|
||||
/* DashboardComponent's private CSS styles */
|
||||
[class*='col-'] {
|
||||
float: left;
|
||||
padding-right: 20px;
|
@ -1,10 +1,11 @@
|
||||
<!-- #docregion -->
|
||||
<h3>Top Heroes</h3>
|
||||
<div class="grid grid-pad">
|
||||
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4">
|
||||
<a *ngFor="let hero of heroes" class="col-1-4"
|
||||
routerLink="/detail/{{hero.id}}">
|
||||
<div class="module hero">
|
||||
<h4>{{hero.name}}</h4>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<hero-search></hero-search>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { DashboardComponent } from './dashboard.component';
|
||||
|
||||
describe('DashboardComponent', () => {
|
||||
let component: DashboardComponent;
|
||||
let fixture: ComponentFixture<DashboardComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DashboardComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -1,22 +1,23 @@
|
||||
// #docregion , search
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
import { Hero } from '../hero';
|
||||
import { HeroService } from '../hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'my-dashboard',
|
||||
selector: 'app-dashboard',
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: [ './dashboard.component.css' ]
|
||||
})
|
||||
// #enddocregion search
|
||||
export class DashboardComponent implements OnInit {
|
||||
heroes: Hero[] = [];
|
||||
|
||||
constructor(private heroService: HeroService) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
ngOnInit() {
|
||||
this.getHeroes();
|
||||
}
|
||||
|
||||
getHeroes(): void {
|
||||
this.heroService.getHeroes()
|
||||
.then(heroes => this.heroes = heroes.slice(1, 5));
|
||||
.subscribe(heroes => this.heroes = heroes.slice(1, 5));
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<!-- #docregion -->
|
||||
<div *ngIf="hero">
|
||||
<h2>{{hero.name}} details!</h2>
|
||||
<div>
|
||||
<label>id: </label>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name: </label>
|
||||
<input [(ngModel)]="hero.name" placeholder="name" />
|
||||
</div>
|
||||
<button (click)="goBack()">Back</button>
|
||||
<!-- #docregion save -->
|
||||
<button (click)="save()">Save</button>
|
||||
<!-- #enddocregion save -->
|
||||
</div>
|
@ -1,40 +0,0 @@
|
||||
// #docregion
|
||||
import 'rxjs/add/operator/switchMap';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'my-hero-detail',
|
||||
templateUrl: './hero-detail.component.html',
|
||||
styleUrls: [ './hero-detail.component.css' ]
|
||||
})
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
hero: Hero;
|
||||
|
||||
constructor(
|
||||
private heroService: HeroService,
|
||||
private route: ActivatedRoute,
|
||||
private location: Location
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.route.paramMap
|
||||
.switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
|
||||
.subscribe(hero => this.hero = hero);
|
||||
}
|
||||
|
||||
// #docregion save
|
||||
save(): void {
|
||||
this.heroService.update(this.hero)
|
||||
.then(() => this.goBack());
|
||||
}
|
||||
// #enddocregion save
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
/* #docregion */
|
||||
/* HeroDetailComponent's private CSS styles */
|
||||
label {
|
||||
display: inline-block;
|
||||
width: 3em;
|
||||
@ -25,6 +25,6 @@ button:hover {
|
||||
}
|
||||
button:disabled {
|
||||
background-color: #eee;
|
||||
color: #ccc;
|
||||
color: #ccc;
|
||||
cursor: auto;
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<div *ngIf="hero">
|
||||
<h2>{{ hero.name | uppercase }} Details</h2>
|
||||
<div><span>id: </span>{{hero.id}}</div>
|
||||
<div>
|
||||
<label>name:
|
||||
<input [(ngModel)]="hero.name" placeholder="name"/>
|
||||
</label>
|
||||
</div>
|
||||
<button (click)="goBack()">go back</button>
|
||||
<button (click)="save()">save</button>
|
||||
</div>
|
@ -0,0 +1,40 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { Location } from '@angular/common';
|
||||
|
||||
import { Hero } from '../hero';
|
||||
import { HeroService } from '../hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-hero-detail',
|
||||
templateUrl: './hero-detail.component.html',
|
||||
styleUrls: [ './hero-detail.component.css' ]
|
||||
})
|
||||
export class HeroDetailComponent implements OnInit {
|
||||
@Input() hero: Hero;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private heroService: HeroService,
|
||||
private location: Location
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getHero();
|
||||
}
|
||||
|
||||
getHero(): void {
|
||||
const id = +this.route.snapshot.paramMap.get('id');
|
||||
this.heroService.getHero(id)
|
||||
.subscribe(hero => this.hero = hero);
|
||||
}
|
||||
|
||||
goBack(): void {
|
||||
this.location.back();
|
||||
}
|
||||
|
||||
save(): void {
|
||||
this.heroService.updateHero(this.hero)
|
||||
.subscribe(() => this.goBack());
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
/* #docregion */
|
||||
.search-result{
|
||||
border-bottom: 1px solid gray;
|
||||
border-left: 1px solid gray;
|
||||
border-right: 1px solid gray;
|
||||
width:195px;
|
||||
height: 16px;
|
||||
padding: 5px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.search-result:hover {
|
||||
color: #eee;
|
||||
background-color: #607D8B;
|
||||
}
|
||||
|
||||
#search-box{
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
}
|
@ -1,11 +0,0 @@
|
||||
<!-- #docregion -->
|
||||
<div id="search-component">
|
||||
<h4>Hero Search</h4>
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
<div>
|
||||
<div *ngFor="let hero of heroes | async"
|
||||
(click)="gotoDetail(hero)" class="search-result" >
|
||||
{{hero.name}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,69 +0,0 @@
|
||||
// #docplaster
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
// #docregion rxjs-imports
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
|
||||
// Observable class extensions
|
||||
import 'rxjs/add/observable/of';
|
||||
|
||||
// Observable operators
|
||||
import 'rxjs/add/operator/catch';
|
||||
import 'rxjs/add/operator/debounceTime';
|
||||
import 'rxjs/add/operator/distinctUntilChanged';
|
||||
// #enddocregion rxjs-imports
|
||||
|
||||
import { HeroSearchService } from './hero-search.service';
|
||||
import { Hero } from './hero';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-search',
|
||||
templateUrl: './hero-search.component.html',
|
||||
styleUrls: [ './hero-search.component.css' ],
|
||||
providers: [HeroSearchService]
|
||||
})
|
||||
export class HeroSearchComponent implements OnInit {
|
||||
// #docregion search
|
||||
heroes: Observable<Hero[]>;
|
||||
// #enddocregion search
|
||||
// #docregion searchTerms
|
||||
private searchTerms = new Subject<string>();
|
||||
// #enddocregion searchTerms
|
||||
|
||||
constructor(
|
||||
private heroSearchService: HeroSearchService,
|
||||
private router: Router) {}
|
||||
// #docregion searchTerms
|
||||
|
||||
// Push a search term into the observable stream.
|
||||
search(term: string): void {
|
||||
this.searchTerms.next(term);
|
||||
}
|
||||
// #enddocregion searchTerms
|
||||
// #docregion search
|
||||
|
||||
ngOnInit(): void {
|
||||
this.heroes = this.searchTerms
|
||||
.debounceTime(300) // wait 300ms after each keystroke before considering the term
|
||||
.distinctUntilChanged() // ignore if next search term is same as previous
|
||||
.switchMap(term => term // switch to new observable each time the term changes
|
||||
// return the http search observable
|
||||
? this.heroSearchService.search(term)
|
||||
// or the observable of empty heroes if there was no search term
|
||||
: Observable.of<Hero[]>([]))
|
||||
.catch(error => {
|
||||
// TODO: add real error handling
|
||||
console.log(error);
|
||||
return Observable.of<Hero[]>([]);
|
||||
});
|
||||
}
|
||||
// #enddocregion search
|
||||
|
||||
gotoDetail(hero: Hero): void {
|
||||
let link = ['/detail', hero.id];
|
||||
this.router.navigate(link);
|
||||
}
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
// #docregion
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Http } from '@angular/http';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import 'rxjs/add/operator/map';
|
||||
|
||||
import { Hero } from './hero';
|
||||
|
||||
@Injectable()
|
||||
export class HeroSearchService {
|
||||
|
||||
constructor(private http: Http) {}
|
||||
|
||||
search(term: string): Observable<Hero[]> {
|
||||
return this.http
|
||||
.get(`api/heroes/?name=${term}`)
|
||||
.map(response => response.json().data as Hero[]);
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/* HeroSearch private styles */
|
||||
.search-result li {
|
||||
border-bottom: 1px solid gray;
|
||||
border-left: 1px solid gray;
|
||||
border-right: 1px solid gray;
|
||||
width:195px;
|
||||
height: 16px;
|
||||
padding: 5px;
|
||||
background-color: white;
|
||||
cursor: pointer;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.search-result li:hover {
|
||||
background-color: #607D8B;
|
||||
}
|
||||
|
||||
.search-result li a {
|
||||
color: #888;
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.search-result li a:hover {
|
||||
color: white;
|
||||
}
|
||||
.search-result li a:active {
|
||||
color: white;
|
||||
}
|
||||
#search-box {
|
||||
width: 200px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
|
||||
ul.search-result {
|
||||
margin-top: 0;
|
||||
padding-left: 0;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
<div id="search-component">
|
||||
<h4>Hero Search</h4>
|
||||
|
||||
<input #searchBox id="search-box" (keyup)="search(searchBox.value)" />
|
||||
|
||||
<ul class="search-result">
|
||||
<li *ngFor="let hero of heroes | async" >
|
||||
<a routerLink="/detail/{{hero.id}}">
|
||||
{{hero.name}}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeroSearchComponent } from './hero-search.component';
|
||||
|
||||
describe('HeroSearchComponent', () => {
|
||||
let component: HeroSearchComponent;
|
||||
let fixture: ComponentFixture<HeroSearchComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HeroSearchComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeroSearchComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,42 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subject } from 'rxjs/Subject';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
|
||||
import {
|
||||
debounceTime, distinctUntilChanged, switchMap
|
||||
} from 'rxjs/operators';
|
||||
|
||||
import { Hero } from '../hero';
|
||||
import { HeroService } from '../hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'hero-search',
|
||||
templateUrl: './hero-search.component.html',
|
||||
styleUrls: [ './hero-search.component.css' ]
|
||||
})
|
||||
export class HeroSearchComponent implements OnInit {
|
||||
heroes: Observable<Hero[]>;
|
||||
private searchTerms = new Subject<string>();
|
||||
|
||||
constructor(private heroService: HeroService) {}
|
||||
|
||||
// Push a search term into the observable stream.
|
||||
search(term: string): void {
|
||||
this.searchTerms.next(term);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.heroes = this.searchTerms.pipe(
|
||||
// wait 300ms after each keystroke before considering the term
|
||||
debounceTime(300),
|
||||
|
||||
// ignore new term if same as previous term
|
||||
distinctUntilChanged(),
|
||||
|
||||
// switch to new search observable each time the term changes
|
||||
switchMap((term: string) => this.heroService.searchHeroes(term)),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,87 +1,129 @@
|
||||
// #docplaster
|
||||
// #docregion , imports
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Headers, Http } from '@angular/http';
|
||||
import { Injectable, Inject, Optional } from '@angular/core';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpClient, HttpHeaders }from '@angular/common/http';
|
||||
|
||||
// #docregion rxjs
|
||||
import 'rxjs/add/operator/toPromise';
|
||||
// #enddocregion rxjs
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
import { catchError, map, tap } from 'rxjs/operators';
|
||||
|
||||
import { Hero } from './hero';
|
||||
// #enddocregion imports
|
||||
import { MessageService } from './message.service';
|
||||
|
||||
const httpOptions = {
|
||||
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class HeroService {
|
||||
|
||||
// #docregion update
|
||||
private headers = new Headers({'Content-Type': 'application/json'});
|
||||
// #enddocregion update
|
||||
// #docregion getHeroes
|
||||
private heroesUrl = 'api/heroes'; // URL to web api
|
||||
|
||||
constructor(private http: Http) { }
|
||||
// #docregion ctor
|
||||
constructor(
|
||||
private http: HttpClient,
|
||||
private messageService: MessageService,
|
||||
@Optional() @Inject(APP_BASE_HREF) origin: string) {
|
||||
this.heroesUrl = `${origin}${this.heroesUrl}`;
|
||||
}
|
||||
// #enddocregion ctor
|
||||
|
||||
getHeroes(): Promise<Hero[]> {
|
||||
return this.http.get(this.heroesUrl)
|
||||
// #docregion to-promise
|
||||
.toPromise()
|
||||
// #enddocregion to-promise
|
||||
// #docregion to-data
|
||||
.then(response => response.json().data as Hero[])
|
||||
// #enddocregion to-data
|
||||
// #docregion catch
|
||||
.catch(this.handleError);
|
||||
// #enddocregion catch
|
||||
/** GET heroes from the server */
|
||||
getHeroes (): Observable<Hero[]> {
|
||||
return this.http.get<Hero[]>(this.heroesUrl)
|
||||
.pipe(
|
||||
tap(heroes => this.log(`fetched heroes`)),
|
||||
catchError(this.handleError('getHeroes', []))
|
||||
);
|
||||
}
|
||||
|
||||
// #enddocregion getHeroes
|
||||
/** GET hero by id. Return `undefined` when id not found */
|
||||
getHeroNo404<Data>(id: number): Observable<Hero> {
|
||||
const url = `${this.heroesUrl}/?id=${id}`;
|
||||
return this.http.get<Hero[]>(url)
|
||||
.pipe(
|
||||
map(heroes => heroes[0]), // returns a {0|1} element array
|
||||
tap(h => {
|
||||
const outcome = h ? `fetched` : `did not find`;
|
||||
this.log(`${outcome} hero id=${id}`);
|
||||
}),
|
||||
catchError(this.handleError<Hero>(`getHero id=${id}`))
|
||||
);
|
||||
}
|
||||
|
||||
// #docregion getHero
|
||||
getHero(id: number): Promise<Hero> {
|
||||
/** GET hero by id. Will 404 if id not found */
|
||||
getHero(id: number): Observable<Hero> {
|
||||
const url = `${this.heroesUrl}/${id}`;
|
||||
return this.http.get(url)
|
||||
.toPromise()
|
||||
.then(response => response.json().data as Hero)
|
||||
.catch(this.handleError);
|
||||
return this.http.get<Hero>(url).pipe(
|
||||
tap(_ => this.log(`fetched hero id=${id}`)),
|
||||
catchError(this.handleError<Hero>(`getHero id=${id}`))
|
||||
);
|
||||
}
|
||||
// #enddocregion getHero
|
||||
|
||||
// #docregion delete
|
||||
delete(id: number): Promise<void> {
|
||||
/* GET heroes whose name contains search term */
|
||||
searchHeroes(term: string): Observable<Hero[]> {
|
||||
if (!term.trim()) {
|
||||
// if not search term, return empty hero array.
|
||||
return of([]);
|
||||
}
|
||||
return this.http.get<Hero[]>(`api/heroes/?name=${term}`).pipe(
|
||||
tap(_ => this.log(`found heroes matching "${term}"`)),
|
||||
catchError(this.handleError<Hero[]>('searchHeroes', []))
|
||||
);
|
||||
}
|
||||
|
||||
//////// Save methods //////////
|
||||
|
||||
/** POST: add a new hero to the server */
|
||||
addHero (name: string): Observable<Hero> {
|
||||
const hero = { name };
|
||||
|
||||
return this.http.post<Hero>(this.heroesUrl, hero, httpOptions).pipe(
|
||||
tap((hero: Hero) => this.log(`added hero w/ id=${hero.id}`)),
|
||||
catchError(this.handleError<Hero>('addHero'))
|
||||
);
|
||||
}
|
||||
|
||||
/** DELETE: delete the hero from the server */
|
||||
deleteHero (hero: Hero | number): Observable<Hero> {
|
||||
const id = typeof hero === 'number' ? hero : hero.id;
|
||||
const url = `${this.heroesUrl}/${id}`;
|
||||
return this.http.delete(url, {headers: this.headers})
|
||||
.toPromise()
|
||||
.then(() => null)
|
||||
.catch(this.handleError);
|
||||
}
|
||||
// #enddocregion delete
|
||||
|
||||
// #docregion create
|
||||
create(name: string): Promise<Hero> {
|
||||
return this.http
|
||||
.post(this.heroesUrl, JSON.stringify({name: name}), {headers: this.headers})
|
||||
.toPromise()
|
||||
.then(res => res.json().data)
|
||||
.catch(this.handleError);
|
||||
return this.http.delete<Hero>(url, httpOptions).pipe(
|
||||
tap(_ => this.log(`deleted hero id=${id}`)),
|
||||
catchError(this.handleError<Hero>('deleteHero'))
|
||||
);
|
||||
}
|
||||
// #enddocregion create
|
||||
// #docregion update
|
||||
|
||||
update(hero: Hero): Promise<Hero> {
|
||||
const url = `${this.heroesUrl}/${hero.id}`;
|
||||
return this.http
|
||||
.put(url, JSON.stringify(hero), {headers: this.headers})
|
||||
.toPromise()
|
||||
.then(() => hero)
|
||||
.catch(this.handleError);
|
||||
/** PUT: update the hero on the server */
|
||||
updateHero (hero: Hero): Observable<any> {
|
||||
return this.http.put(this.heroesUrl, hero, httpOptions).pipe(
|
||||
tap(_ => this.log(`updated hero id=${hero.id}`)),
|
||||
catchError(this.handleError<any>('updateHero'))
|
||||
);
|
||||
}
|
||||
// #enddocregion update
|
||||
|
||||
// #docregion getHeroes, handleError
|
||||
private handleError(error: any): Promise<any> {
|
||||
console.error('An error occurred', error); // for demo purposes only
|
||||
return Promise.reject(error.message || error);
|
||||
/**
|
||||
* Handle Http operation that failed.
|
||||
* Let the app continue.
|
||||
* @param operation - name of the operation that failed
|
||||
* @param result - optional value to return as the observable result
|
||||
*/
|
||||
private handleError<T> (operation = 'operation', result?: T) {
|
||||
return (error: any): Observable<T> => {
|
||||
|
||||
// TODO: send the error to remote logging infrastructure
|
||||
console.error(error); // log to console instead
|
||||
|
||||
// TODO: better job of transforming error for user consumption
|
||||
this.log(`${operation} failed: ${error.message}`);
|
||||
|
||||
// Let the app keep running by returning an empty result.
|
||||
return of(result as T);
|
||||
};
|
||||
}
|
||||
|
||||
/** Log a HeroService message with the MessageService */
|
||||
private log(message: string) {
|
||||
this.messageService.add('HeroService: ' + message);
|
||||
}
|
||||
// #enddocregion getHeroes, handleError
|
||||
}
|
||||
|
||||
|
@ -1,29 +0,0 @@
|
||||
<!-- #docregion -->
|
||||
<h2>My Heroes</h2>
|
||||
<!-- #docregion add -->
|
||||
<div>
|
||||
<label>Hero name:</label> <input #heroName />
|
||||
<button (click)="add(heroName.value); heroName.value=''">
|
||||
Add
|
||||
</button>
|
||||
</div>
|
||||
<!-- #enddocregion add -->
|
||||
<ul class="heroes">
|
||||
<!-- #docregion li-element -->
|
||||
<li *ngFor="let hero of heroes" (click)="onSelect(hero)"
|
||||
[class.selected]="hero === selectedHero">
|
||||
<span class="badge">{{hero.id}}</span>
|
||||
<span>{{hero.name}}</span>
|
||||
<!-- #docregion delete -->
|
||||
<button class="delete"
|
||||
(click)="delete(hero); $event.stopPropagation()">x</button>
|
||||
<!-- #enddocregion delete -->
|
||||
</li>
|
||||
<!-- #enddocregion li-element -->
|
||||
</ul>
|
||||
<div *ngIf="selectedHero">
|
||||
<h2>
|
||||
{{selectedHero.name | uppercase}} is my hero
|
||||
</h2>
|
||||
<button (click)="gotoDetail()">View Details</button>
|
||||
</div>
|
@ -1,61 +0,0 @@
|
||||
// #docregion
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
import { Hero } from './hero';
|
||||
import { HeroService } from './hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'my-heroes',
|
||||
templateUrl: './heroes.component.html',
|
||||
styleUrls: [ './heroes.component.css' ]
|
||||
})
|
||||
export class HeroesComponent implements OnInit {
|
||||
heroes: Hero[];
|
||||
selectedHero: Hero;
|
||||
|
||||
constructor(
|
||||
private heroService: HeroService,
|
||||
private router: Router) { }
|
||||
|
||||
getHeroes(): void {
|
||||
this.heroService
|
||||
.getHeroes()
|
||||
.then(heroes => this.heroes = heroes);
|
||||
}
|
||||
|
||||
// #docregion add
|
||||
add(name: string): void {
|
||||
name = name.trim();
|
||||
if (!name) { return; }
|
||||
this.heroService.create(name)
|
||||
.then(hero => {
|
||||
this.heroes.push(hero);
|
||||
this.selectedHero = null;
|
||||
});
|
||||
}
|
||||
// #enddocregion add
|
||||
|
||||
// #docregion delete
|
||||
delete(hero: Hero): void {
|
||||
this.heroService
|
||||
.delete(hero.id)
|
||||
.then(() => {
|
||||
this.heroes = this.heroes.filter(h => h !== hero);
|
||||
if (this.selectedHero === hero) { this.selectedHero = null; }
|
||||
});
|
||||
}
|
||||
// #enddocregion delete
|
||||
|
||||
ngOnInit(): void {
|
||||
this.getHeroes();
|
||||
}
|
||||
|
||||
onSelect(hero: Hero): void {
|
||||
this.selectedHero = hero;
|
||||
}
|
||||
|
||||
gotoDetail(): void {
|
||||
this.router.navigate(['/detail', this.selectedHero.id]);
|
||||
}
|
||||
}
|
@ -1,8 +1,4 @@
|
||||
/* #docregion */
|
||||
.selected {
|
||||
background-color: #CFD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
/* HeroesComponent's private CSS styles */
|
||||
.heroes {
|
||||
margin: 0 0 2em 0;
|
||||
list-style-type: none;
|
||||
@ -10,28 +6,33 @@
|
||||
width: 15em;
|
||||
}
|
||||
.heroes li {
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
left: 0;
|
||||
cursor: pointer;
|
||||
background-color: #EEE;
|
||||
margin: .5em;
|
||||
padding: .3em 0;
|
||||
height: 1.6em;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.heroes li:hover {
|
||||
color: #607D8B;
|
||||
background-color: #DDD;
|
||||
left: .1em;
|
||||
}
|
||||
.heroes li.selected:hover {
|
||||
background-color: #BBD8DC !important;
|
||||
color: white;
|
||||
}
|
||||
.heroes .text {
|
||||
|
||||
.heroes a {
|
||||
color: #888;
|
||||
text-decoration: none;
|
||||
position: relative;
|
||||
top: -3px;
|
||||
display: block;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.heroes a:hover {
|
||||
color:#607D8B;
|
||||
}
|
||||
|
||||
.heroes .badge {
|
||||
display: inline-block;
|
||||
font-size: small;
|
||||
@ -43,26 +44,31 @@
|
||||
left: -1px;
|
||||
top: -4px;
|
||||
height: 1.8em;
|
||||
min-width: 16px;
|
||||
text-align: right;
|
||||
margin-right: .8em;
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
button {
|
||||
font-family: Arial;
|
||||
|
||||
.button {
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
font-family: Arial;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
}
|
||||
/* #docregion additions */
|
||||
|
||||
button.delete {
|
||||
float:right;
|
||||
margin-top: 2px;
|
||||
margin-right: .8em;
|
||||
position: relative;
|
||||
left: 194px;
|
||||
top: -32px;
|
||||
background-color: gray !important;
|
||||
color:white;
|
||||
color: white;
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
<h2>My Heroes</h2>
|
||||
|
||||
<div>
|
||||
<label>Hero name:
|
||||
<input #heroName />
|
||||
</label>
|
||||
<!-- (click) passes input value to add() and then clears the input -->
|
||||
<button (click)="add(heroName.value); heroName.value=''">
|
||||
add
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<ul class="heroes">
|
||||
<li *ngFor="let hero of heroes">
|
||||
<a routerLink="/detail/{{hero.id}}">
|
||||
<span class="badge">{{hero.id}}</span> {{hero.name}}
|
||||
</a>
|
||||
<button class="delete" title="delete hero"
|
||||
(click)="delete(hero);$event.stopPropagation()">x</button>
|
||||
</li>
|
||||
</ul>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { HeroesComponent } from './heroes.component';
|
||||
|
||||
describe('HeroesComponent', () => {
|
||||
let component: HeroesComponent;
|
||||
let fixture: ComponentFixture<HeroesComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ HeroesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(HeroesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,41 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
import { Hero } from '../hero';
|
||||
import { HeroService } from '../hero.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-heroes',
|
||||
templateUrl: './heroes.component.html',
|
||||
styleUrls: ['./heroes.component.css']
|
||||
})
|
||||
export class HeroesComponent implements OnInit {
|
||||
heroes: Hero[];
|
||||
|
||||
constructor(private heroService: HeroService) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.getHeroes();
|
||||
}
|
||||
|
||||
getHeroes(): void {
|
||||
this.heroService.getHeroes()
|
||||
.subscribe(heroes => this.heroes = heroes);
|
||||
}
|
||||
|
||||
add(name: string): void {
|
||||
name = name.trim();
|
||||
if (!name) { return; }
|
||||
this.heroService.addHero(name)
|
||||
.subscribe(hero => {
|
||||
this.heroes.push(hero);
|
||||
});
|
||||
}
|
||||
|
||||
delete(hero: Hero): void {
|
||||
this.heroService.deleteHero(hero)
|
||||
.subscribe(() => {
|
||||
this.heroes = this.heroes.filter(h => h !== hero);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1,18 +1,18 @@
|
||||
// #docregion , init
|
||||
import { InMemoryDbService } from 'angular-in-memory-web-api';
|
||||
|
||||
export class InMemoryDataService implements InMemoryDbService {
|
||||
createDb() {
|
||||
let heroes = [
|
||||
{id: 11, name: 'Mr. Nice'},
|
||||
{id: 12, name: 'Narco'},
|
||||
{id: 13, name: 'Bombasto'},
|
||||
{id: 14, name: 'Celeritas'},
|
||||
{id: 15, name: 'Magneta'},
|
||||
{id: 16, name: 'RubberMan'},
|
||||
{id: 17, name: 'Dynama'},
|
||||
{id: 18, name: 'Dr IQ'},
|
||||
{id: 19, name: 'Magma'},
|
||||
{id: 20, name: 'Tornado'}
|
||||
const heroes = [
|
||||
{ id: 11, name: 'Mr. Nice' },
|
||||
{ id: 12, name: 'Narco' },
|
||||
{ id: 13, name: 'Bombasto' },
|
||||
{ id: 14, name: 'Celeritas' },
|
||||
{ id: 15, name: 'Magneta' },
|
||||
{ id: 16, name: 'RubberMan' },
|
||||
{ id: 17, name: 'Dynama' },
|
||||
{ id: 18, name: 'Dr IQ' },
|
||||
{ id: 19, name: 'Magma' },
|
||||
{ id: 20, name: 'Tornado' }
|
||||
];
|
||||
return {heroes};
|
||||
}
|
||||
|
@ -0,0 +1,15 @@
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { MessageService } from './message.service';
|
||||
|
||||
describe('MessageService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [MessageService]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([MessageService], (service: MessageService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
14
aio/content/examples/universal/src/app/message.service.ts
Normal file
14
aio/content/examples/universal/src/app/message.service.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
@Injectable()
|
||||
export class MessageService {
|
||||
messages: string[] = [];
|
||||
|
||||
add(message: string) {
|
||||
this.messages.push(message);
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.messages.length = 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/* MessagesComponent's private CSS styles */
|
||||
h2 {
|
||||
color: red;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-weight: lighter;
|
||||
}
|
||||
body {
|
||||
margin: 2em;
|
||||
}
|
||||
body, input[text], button {
|
||||
color: crimson;
|
||||
font-family: Cambria, Georgia;
|
||||
}
|
||||
|
||||
button.clear {
|
||||
font-family: Arial;
|
||||
background-color: #eee;
|
||||
border: none;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
cursor: hand;
|
||||
}
|
||||
button:hover {
|
||||
background-color: #cfd8dc;
|
||||
}
|
||||
button:disabled {
|
||||
background-color: #eee;
|
||||
color: #aaa;
|
||||
cursor: auto;
|
||||
}
|
||||
button.clear {
|
||||
color: #888;
|
||||
margin-bottom: 12px;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<div *ngIf="messageService.messages.length">
|
||||
|
||||
<h2>Messages</h2>
|
||||
<button class="clear"
|
||||
(click)="messageService.clear()">clear</button>
|
||||
<div *ngFor='let message of messageService.messages'> {{message}} </div>
|
||||
|
||||
</div>
|
@ -0,0 +1,25 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MessagesComponent } from './messages.component';
|
||||
|
||||
describe('MessagesComponent', () => {
|
||||
let component: MessagesComponent;
|
||||
let fixture: ComponentFixture<MessagesComponent>;
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ MessagesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(MessagesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,16 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { MessageService } from '../message.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-messages',
|
||||
templateUrl: './messages.component.html',
|
||||
styleUrls: ['./messages.component.css']
|
||||
})
|
||||
export class MessagesComponent implements OnInit {
|
||||
|
||||
constructor(public messageService: MessageService) {}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
}
|
14
aio/content/examples/universal/src/app/mock-heroes.ts
Normal file
14
aio/content/examples/universal/src/app/mock-heroes.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { Hero } from './hero';
|
||||
|
||||
export const HEROES: Hero[] = [
|
||||
{ id: 11, name: 'Mr. Nice' },
|
||||
{ id: 12, name: 'Narco' },
|
||||
{ id: 13, name: 'Bombasto' },
|
||||
{ id: 14, name: 'Celeritas' },
|
||||
{ id: 15, name: 'Magneta' },
|
||||
{ id: 16, name: 'RubberMan' },
|
||||
{ id: 17, name: 'Dynama' },
|
||||
{ id: 18, name: 'Dr IQ' },
|
||||
{ id: 19, name: 'Magma' },
|
||||
{ id: 20, name: 'Tornado' }
|
||||
];
|
@ -1,18 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>Angular Tour of Heroes</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
<script src="node_modules/zone.js/dist/zone.min.js"></script>
|
||||
<script src="dist/build.js"></script>
|
||||
</html>
|
@ -1,26 +1,14 @@
|
||||
<!-- #docregion -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<base href="/">
|
||||
<title>Angular Universal Tour of Heroes</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Tour of Heroes</title>
|
||||
<base href="/">
|
||||
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
|
||||
<!-- Polyfills -->
|
||||
<script src="node_modules/core-js/client/shim.min.js"></script>
|
||||
|
||||
<script src="node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="node_modules/systemjs/dist/system.src.js"></script>
|
||||
|
||||
<script src="systemjs.config.js"></script>
|
||||
<script>
|
||||
System.import('main.js').catch(function(err){ console.error(err); });
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<my-app>Loading...</my-app>
|
||||
</body>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +0,0 @@
|
||||
// #docregion
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
import { AppModuleNgFactory } from '../aot/src/app/app.module.ngfactory';
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
1
aio/content/examples/universal/src/main.server.ts
Normal file
1
aio/content/examples/universal/src/main.server.ts
Normal file
@ -0,0 +1 @@
|
||||
export { AppServerModule } from './app/app.server.module';
|
@ -1,6 +1,11 @@
|
||||
// #docregion
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppModule } from './app/app.module';
|
||||
import { environment } from './environments/environment';
|
||||
|
||||
if (environment.production) {
|
||||
enableProdMode();
|
||||
}
|
||||
|
||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
||||
|
16
aio/content/examples/universal/src/tsconfig.server.json
Normal file
16
aio/content/examples/universal/src/tsconfig.server.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "../out-tsc/app",
|
||||
"baseUrl": "./",
|
||||
"module": "commonjs",
|
||||
"types": []
|
||||
},
|
||||
"exclude": [
|
||||
"test.ts",
|
||||
"**/*.spec.ts"
|
||||
],
|
||||
"angularCompilerOptions": {
|
||||
"entryModule": "app/app.server.module#AppServerModule"
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { ServerModule } from '@angular/platform-server';
|
||||
import { AppComponent } from '../app/app.component';
|
||||
import { AppModule } from '../app/app.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
ServerModule,
|
||||
AppModule
|
||||
],
|
||||
bootstrap: [
|
||||
AppComponent
|
||||
],
|
||||
providers: [
|
||||
{provide: APP_BASE_HREF, useValue: '/'}
|
||||
// { provide: NgModuleFactoryLoader, useClass: ServerRouterLoader }
|
||||
]
|
||||
})
|
||||
export class AppServerModule {
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
import 'zone.js/dist/zone-node';
|
||||
import { enableProdMode } from '@angular/core';
|
||||
// import { AppServerModule } from './app.server';
|
||||
import { AppServerModuleNgFactory } from '../../aot/src/uni/app.server.ngfactory';
|
||||
import * as express from 'express';
|
||||
import { ngUniversalEngine } from './universal-engine';
|
||||
|
||||
enableProdMode();
|
||||
|
||||
const server = express();
|
||||
|
||||
// set our angular engine as the handler for html files, so it will be used to render them.
|
||||
server.engine('html', ngUniversalEngine({
|
||||
bootstrap: [AppServerModuleNgFactory]
|
||||
}));
|
||||
|
||||
// set default view directory
|
||||
server.set('views', 'src');
|
||||
|
||||
// handle requests for routes in the app. ngExpressEngine does the rendering.
|
||||
server.get(['/', '/dashboard', '/heroes', '/detail/:id'], (req, res) => {
|
||||
res.render('index-aot.html', {req});
|
||||
});
|
||||
|
||||
// handle requests for static files
|
||||
server.get(['/*.js', '/*.css'], (req, res, next) => {
|
||||
let fileName: string = req.originalUrl;
|
||||
console.log(fileName);
|
||||
let root = fileName.startsWith('/node_modules/') ? '.' : 'src';
|
||||
res.sendFile(fileName, { root: root }, function (err) {
|
||||
if (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// start the server
|
||||
server.listen(3200, () => {
|
||||
console.log('listening on port 3200...');
|
||||
});
|
@ -1,38 +0,0 @@
|
||||
/**
|
||||
* Express/Connect middleware for rendering pages using Angular Universal
|
||||
*/
|
||||
import * as fs from 'fs';
|
||||
import { renderModuleFactory } from '@angular/platform-server';
|
||||
|
||||
const templateCache = {}; // cache for page templates
|
||||
const outputCache = {}; // cache for rendered pages
|
||||
|
||||
export function ngUniversalEngine(setupOptions: any) {
|
||||
|
||||
return function (filePath: string, options: { req: Request }, callback: (err: Error, html: string) => void) {
|
||||
let url: string = options.req.url;
|
||||
let html: string = outputCache[url];
|
||||
if (html) {
|
||||
// return already-built page for this url
|
||||
console.log('from cache: ' + url);
|
||||
callback(null, html);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('building: ' + url);
|
||||
if (!templateCache[filePath]) {
|
||||
let file = fs.readFileSync(filePath);
|
||||
templateCache[filePath] = file.toString();
|
||||
}
|
||||
|
||||
// render the page via angular platform-server
|
||||
let appModuleFactory = setupOptions.bootstrap[0];
|
||||
renderModuleFactory(appModuleFactory, {
|
||||
document: templateCache[filePath],
|
||||
url: url
|
||||
}).then(str => {
|
||||
outputCache[url] = str;
|
||||
callback(null, str);
|
||||
});
|
||||
};
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2015", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types/"
|
||||
]
|
||||
},
|
||||
|
||||
"files": [
|
||||
"src/app/app.module.ts",
|
||||
"src/main-aot.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "aot",
|
||||
"entryModule": "./src/app/app.module#AppModule",
|
||||
"skipMetadataEmit" : true
|
||||
}
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"sourceMap": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es2015", "dom"],
|
||||
"noImplicitAny": true,
|
||||
"suppressImplicitAnyIndexErrors": true,
|
||||
"typeRoots": [
|
||||
"../../node_modules/@types/"
|
||||
]
|
||||
},
|
||||
|
||||
"files": [
|
||||
"src/uni/app.server.ts",
|
||||
"src/uni/server-aot.ts"
|
||||
],
|
||||
|
||||
"angularCompilerOptions": {
|
||||
"genDir": "aot",
|
||||
"entryModule": "./src/app/app.module#AppModule",
|
||||
"skipMetadataEmit" : true
|
||||
}
|
||||
}
|
31
aio/content/examples/universal/webpack.server.config.js
Normal file
31
aio/content/examples/universal/webpack.server.config.js
Normal file
@ -0,0 +1,31 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
|
||||
module.exports = {
|
||||
entry: { server: './server.ts' },
|
||||
resolve: { extensions: ['.js', '.ts'] },
|
||||
target: 'node',
|
||||
// this makes sure we include node_modules and other 3rd party libraries
|
||||
externals: [/(node_modules|main\..*\.js)/],
|
||||
output: {
|
||||
path: path.join(__dirname, 'dist'),
|
||||
filename: '[name].js'
|
||||
},
|
||||
module: {
|
||||
rules: [{ test: /\.ts$/, loader: 'ts-loader' }]
|
||||
},
|
||||
plugins: [
|
||||
// Temporary Fix for issue: https://github.com/angular/angular/issues/11580
|
||||
// for 'WARNING Critical dependency: the request of a dependency is an expression'
|
||||
new webpack.ContextReplacementPlugin(
|
||||
/(.+)?angular(\\|\/)core(.+)?/,
|
||||
path.join(__dirname, 'src'), // location of your src
|
||||
{} // a map of your routes
|
||||
),
|
||||
new webpack.ContextReplacementPlugin(
|
||||
/(.+)?express(\\|\/)(.+)?/,
|
||||
path.join(__dirname, 'src'),
|
||||
{}
|
||||
)
|
||||
]
|
||||
};
|
9
aio/content/examples/universal/zipper.json
Normal file
9
aio/content/examples/universal/zipper.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"files":[
|
||||
"!dist/",
|
||||
"!**/*.d.ts",
|
||||
"!**/src/**/*.js"
|
||||
],
|
||||
"removeSystemJsConfig": false,
|
||||
"type": "universal"
|
||||
}
|
@ -7,3 +7,4 @@
|
||||
<p>We're sorry. The page you are looking for cannot be found.</p>
|
||||
</div>
|
||||
</div>
|
||||
<aio-file-not-found-search></aio-file-not-found-search>
|
||||
|
@ -380,7 +380,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
### ng-href
|
||||
<code-example hideCopy format="">
|
||||
<a ng-href="angularDocsUrl">Angular Docs</a>
|
||||
<a ng-href="{{ angularDocsUrl }}">Angular Docs</a>
|
||||
</code-example>
|
||||
|
||||
|
||||
@ -390,7 +390,7 @@ The following are some of the key AngularJS built-in directives and their equiva
|
||||
|
||||
In AngularJS, the `ng-href` is often used to activate a route as part of navigation.
|
||||
<code-example hideCopy format="">
|
||||
<a ng-href="#movies">Movies</a>
|
||||
<a ng-href="#{{ moviesHash }}">Movies</a>
|
||||
</code-example>
|
||||
|
||||
|
||||
|
@ -16,9 +16,7 @@ animation logic with the rest of your application code, for ease of control.
|
||||
Angular animations are built on top of the standard [Web Animations API](https://w3c.github.io/web-animations/)
|
||||
and run natively on [browsers that support it](http://caniuse.com/#feat=web-animation).
|
||||
|
||||
For other browsers, a polyfill is required. Grab
|
||||
[`web-animations.min.js` from GitHub](https://github.com/web-animations/web-animations-js) and
|
||||
add it to your page.
|
||||
For other browsers, a polyfill is required. Uncomment the `web-animations-js` polyfill from the `polyfills.ts` file.
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -171,7 +171,7 @@ element that hosts an attribute directive, the `<p>` in this case.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
Of course you could reach into the DOM with standard JavaScript and and attach event listeners manually.
|
||||
Of course you could reach into the DOM with standard JavaScript and attach event listeners manually.
|
||||
There are at least three problems with _that_ approach:
|
||||
|
||||
1. You have to write the listeners correctly.
|
||||
@ -353,7 +353,7 @@ You can also experience and download the <live-example title="Attribute Directiv
|
||||
|
||||
### Appendix: Why add _@Input_?
|
||||
|
||||
In this demo, the `hightlightColor` property is an ***input*** property of
|
||||
In this demo, the `highlightColor` property is an ***input*** property of
|
||||
the `HighlightDirective`. You've seen it applied without an alias:
|
||||
|
||||
<code-example path="attribute-directives/src/app/highlight.directive.2.ts" linenums="false" title="src/app/highlight.directive.ts (color)" region="color"></code-example>
|
||||
|
@ -290,7 +290,7 @@ If a library exports something that the application doesn't import, a tree shaki
|
||||
Tree shaking was popularized by
|
||||
<a href="http://rollupjs.org/" title="Rollup">Rollup</a>, a popular tool with an ecosystem of
|
||||
plugins for bundling, minification, and uglification.
|
||||
Learn more about tree shaking and dead code elmination in
|
||||
Learn more about tree shaking and dead code elimination in
|
||||
<a href="https://medium.com/@Rich_Harris/tree-shaking-versus-dead-code-elimination-d3765df85c80#.15ih9cyvl" title="Tree-shaking and Dead Code Elimination">
|
||||
this post</a> by rollup-creator, Rich Harris.
|
||||
|
||||
|
@ -451,7 +451,7 @@ Here's the markup for an "avoid" example in the
|
||||
{@a code-tabs}
|
||||
### Code Tabs
|
||||
|
||||
Code tabs display code much like _code examples_ do. The added advantage is that they can display mutiple code samples within a tabbed interface. Each tab is displayed using _code pane_.
|
||||
Code tabs display code much like _code examples_ do. The added advantage is that they can display multiple code samples within a tabbed interface. Each tab is displayed using _code pane_.
|
||||
|
||||
#### Code-tabs attributes
|
||||
|
||||
|
@ -4502,8 +4502,8 @@ helps instantly identify which members of the component serve which purpose.
|
||||
|
||||
**Why?** The property associated with `@HostBinding` or the method associated with `@HostListener`
|
||||
can be modified only in a single place—in the directive's class.
|
||||
If you use the `host` metadata property, you must modify both the property declaration inside the controller,
|
||||
and the metadata associated with the directive.
|
||||
If you use the `host` metadata property, you must modify both the property/method declaration in the
|
||||
directive's class and the metadata in the decorator associated with the directive.
|
||||
|
||||
|
||||
</div>
|
||||
|
527
aio/content/guide/universal.md
Normal file
527
aio/content/guide/universal.md
Normal file
@ -0,0 +1,527 @@
|
||||
# Angular Universal: server-side rendering
|
||||
|
||||
This guide describes **Angular Universal**, a technology that runs your Angular application on the server.
|
||||
|
||||
A normal Angular application executes in the _browser_, rendering pages in the DOM in response to user actions.
|
||||
|
||||
**Angular Universal** generates _static_ application pages on the _server_
|
||||
through a process called **server-side rendering (SSR)**.
|
||||
|
||||
It can generate and serve those pages in response to requests from browsers.
|
||||
It can also pre-generate pages as HTML files that you serve later.
|
||||
|
||||
This guide describes a Universal sample application that launches quickly as a server-rendered page.
|
||||
Meanwhile, the browser downloads the full client version and switches to it automatically after the code loads.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
[Download the finished sample code](generated/zips/universal/universal.zip),
|
||||
which runs in a [node express](https://expressjs.com/) server.
|
||||
|
||||
</div>
|
||||
|
||||
{@a why-do-it}
|
||||
|
||||
### Why Universal
|
||||
|
||||
There are three main reasons to create a Universal version of your app.
|
||||
|
||||
1. Facilitate web crawlers (SEO)
|
||||
1. Improve performance on mobile and low-powered devices
|
||||
1. Show the first page quickly
|
||||
|
||||
{@a seo}
|
||||
{@a web-crawlers}
|
||||
#### Facilitate web crawlers
|
||||
|
||||
Google, Bing, Facebook, Twitter and other social media sites rely on web crawlers to index your application content and make that content searchable on the web.
|
||||
|
||||
These web crawlers may be unable to navigate and index your highly-interactive, Angular application as a human user could do.
|
||||
|
||||
Angular Universal can generate a static version of your app that is easily searchable, linkable, and navigable without JavaScript.
|
||||
It also makes a site preview available since each URL returns a fully-rendered page.
|
||||
|
||||
Enabling web crawlers is often referred to as
|
||||
[Search Engine Optimization (SEO)](https://static.googleusercontent.com/media/www.google.com/en//webmasters/docs/search-engine-optimization-starter-guide.pdf).
|
||||
|
||||
{@a no-javascript}
|
||||
|
||||
#### Performance on mobile and low performance devices
|
||||
|
||||
Some devices don't support JavaScript or execute JavaScript so poorly that the user experience is unacceptable.
|
||||
For these cases, you may require a server-rendered, no-JavaScript version of the app.
|
||||
This version, however limited, may be the only practical alternative for
|
||||
people who otherwise would not be able to use the app at all.
|
||||
|
||||
{@a startup-performance}
|
||||
|
||||
#### Show the first page quickly
|
||||
|
||||
Displaying the first page quickly can be critical for user engagement.
|
||||
|
||||
[53% of mobile site visits are abandoned](https://www.doubleclickbygoogle.com/articles/mobile-speed-matters/) if pages take longer than 3 seconds to load.
|
||||
Your app may have to launch faster to engage these users before they decide to do something else.
|
||||
|
||||
With Angular Universal, you can generate landing pages for the app that look like the complete app.
|
||||
The pages are pure HTML, and can display even if JavaScript is disabled.
|
||||
The pages do not handle browser events, but they _do_ support navigation through the site using [routerLink](guide/router.html#router-link).
|
||||
|
||||
In practice, you'll serve a static version of the landing page to hold the user's attention.
|
||||
At the same time, you'll load the full Angular app behind it in the manner [explained below](#transition).
|
||||
The user perceives near-instant performance from the landing page
|
||||
and gets the full interactive experience after the full app loads.
|
||||
|
||||
{@a how-does-it-work}
|
||||
### How it works
|
||||
|
||||
To make a Universal app, you install the `platform-server` package.
|
||||
The `platform-server` package has server implementations of the DOM, `XMLHttpRequest`, and other low-level features that do not rely on a browser.
|
||||
|
||||
You compile the client application with the `platform-server` module instead of the `platform-browser` module.
|
||||
and run the resulting Universal app on a web server.
|
||||
|
||||
The server (a [Node Express](https://expressjs.com/) server in _this_ guide's example)
|
||||
passes client requests for application pages to Universal's `renderModuleFactory` function.
|
||||
|
||||
The `renderModuleFactory` function takes as inputs a *template* HTML page (usually `index.html`),
|
||||
an Angular *module* containing components,
|
||||
and a *route* that determines which components to display.
|
||||
|
||||
The route comes from the client's request to the server.
|
||||
Each request results in the appropriate view for the requested route.
|
||||
|
||||
The `renderModuleFactory` renders that view within the `<app>` tag of the template, creating a finished HTML page for the client.
|
||||
|
||||
Finally, the server returns the rendered page to the client.
|
||||
|
||||
### Working around the browser APIs
|
||||
|
||||
Because a Universal `platform-server` app doesn't execute in the browser, you may have to work around some of the browser APIs and capabilities that are missing on the server.
|
||||
|
||||
You won't be able reference browser-only native objects such as `window`, `document`, `navigator` or `location`.
|
||||
If you don't need them on the server-rendered page, side-step them with conditional logic.
|
||||
|
||||
Alternatively, look for an injectable Angular abstraction over the object you need such as `Location` or `Document`;
|
||||
it may substitute adequately for the specific API that you're calling.
|
||||
If Angular doesn't provide it, you may be able to write your own abstraction that delegates to the browser API while in the browser and to a satisfactory alternative implementation while on the server.
|
||||
|
||||
Without mouse or keyboard events, a universal app can't rely on a user clicking a button to show a component.
|
||||
A universal app should determine what to render based solely on the incoming client request.
|
||||
This is a good argument for making the app [routeable](guide/router).
|
||||
|
||||
Because the user of a server-rendered page can't do much more than click links,
|
||||
you should [swap in the real client app](#transition) as quickly as possible for a proper interactive experience.
|
||||
|
||||
{@a the-example}
|
||||
|
||||
## The example
|
||||
|
||||
The _Tour of Heroes_ tutorial is the foundation for the Universal sample described in this guide.
|
||||
|
||||
The core application files are mostly untouched, with a few exceptions described below.
|
||||
You'll add more files to support building and serving with Universal.
|
||||
|
||||
In this example, the Angular CLI compiles and bundles the Universal version of the app with the
|
||||
[AOT (Ahead-of-Time) compiler](guide/aot-compiler).
|
||||
A node/express web server turns client requests into the HTML pages rendered by Universal.
|
||||
|
||||
You will create:
|
||||
|
||||
* a server-side app module, `app.server.module.ts`
|
||||
* an entry point for the server-side, `main.server.ts`
|
||||
* an express web server to handle requests, `server.ts`
|
||||
* a TypeScript config file, `tsconfig.server.json`
|
||||
* a Webpack config file for the server, `webpack.server.config.js`
|
||||
|
||||
When you're done, the folder structure will look like this:
|
||||
|
||||
<code-example format="." language="none" linenums="false">
|
||||
src/
|
||||
index.html <i>app web page</i>
|
||||
main.ts <i>bootstrapper for client app</i>
|
||||
main.server.ts <i>* bootstrapper for server app</i>
|
||||
tsconfig.app.json <i>TypeScript client configuration</i>
|
||||
tsconfig.server.json <i>* TypeScript server configuration</i>
|
||||
tsconfig.spec.json <i>TypeScript spec configuration</i>
|
||||
style.css <i>styles for the app</i>
|
||||
app/ ... <i>application code</i>
|
||||
app.server.module.ts <i>* server-side application module</i>
|
||||
server.ts <i>* express web server</i>
|
||||
tsconfig.json <i>TypeScript client configuration</i>
|
||||
package.json <i>npm configuration</i>
|
||||
webpack.server.config.js <i>* Webpack server configuration</i>
|
||||
</code-example>
|
||||
|
||||
The files marked with `*` are new and not in the original tutorial sample.
|
||||
This guide covers them in the sections below.
|
||||
|
||||
{@a preparation}
|
||||
|
||||
## Preparation
|
||||
|
||||
Download the [Tour of Heroes](generated/zips/toh-pt6/toh-pt6.zip) project and install the dependencies from it.
|
||||
|
||||
{@a install-the-tools}
|
||||
|
||||
### Install the tools
|
||||
|
||||
To get started, install these packages.
|
||||
|
||||
* `@angular/platform-server` - Universal server-side components.
|
||||
* `@nguniversal/module-map-ngfactory-loader` - For handling lazy-loading in the context of a server-render.
|
||||
* `@nguniversal/express-engine` - An express engine for Universal applications.
|
||||
* `ts-loader` - To transpile the server application
|
||||
|
||||
Install them with the following commands:
|
||||
|
||||
<code-example format="." language="bash">
|
||||
npm install --save @angular/platform-server @nguniversal/module-map-ngfactory-loader ts-loader @nguniversal/express-engine
|
||||
</code-example>
|
||||
|
||||
{@a transition}
|
||||
|
||||
### Modify the client app
|
||||
|
||||
A Universal app can act as a dynamic, content-rich "splash screen" that engages the user.
|
||||
It gives the appearance of a near-instant application.
|
||||
|
||||
Meanwhile, the browser downloads the client app scripts in background.
|
||||
Once loaded, Angular transitions from the static server-rendered page to the dynamically rendered views of the interactive client app.
|
||||
|
||||
You must make a few changes to your application code to support both server-side rendering and the transition to the client app.
|
||||
|
||||
#### The root `AppModule`
|
||||
|
||||
Open file `src/app/app.module.ts` and find the `BrowserModule` import in the `NgModule` metadata.
|
||||
Replace that import with this one:
|
||||
|
||||
<code-example path="universal/src/app/app.module.ts" region="browsermodule" title="src/app/app.module.ts (withServerTransition)">
|
||||
</code-example>
|
||||
|
||||
Angular adds the `appId` value (which can be _any_ string) to the style-names of the server-rendered pages,
|
||||
so that they can be identified and removed when the client app starts.
|
||||
|
||||
You can get runtime information about the current platform and the `appId` by injection.
|
||||
|
||||
<code-example path="universal/src/app/app.module.ts" region="platform-detection" title="src/app/app.module.ts (platform detection)">
|
||||
</code-example>
|
||||
|
||||
{@a http-urls}
|
||||
|
||||
#### Absolute HTTP URLs
|
||||
|
||||
The tutorial's `HeroService` and `HeroSearchService` delegate to the Angular `Http` module to fetch application data.
|
||||
These services send requests to _relative_ URLs such as `api/heroes`.
|
||||
|
||||
In a Universal app, `Http` URLs must be _absolute_ (e.g., `https://my-server.com/api/heroes`)
|
||||
even when the Universal web server is capable of handling those requests.
|
||||
|
||||
You'll have to change the services to make requests with absolute URLs when running on the server
|
||||
and with relative URLs when running in the browser.
|
||||
|
||||
One solution is to provide the server's runtime origin under the Angular [`APP_BASE_REF` token](api/common/APP_BASE_HREF),
|
||||
inject it into the service, and prepend the origin to the request URL.
|
||||
|
||||
Start by changing the `HeroService` constructor to take a second `origin` parameter that is optionally injected via the `APP_BASE_HREF` token.
|
||||
|
||||
<code-example path="universal/src/app/hero.service.ts" region="ctor" title="src/app/hero.service.ts (constructor with optional origin)">
|
||||
</code-example>
|
||||
|
||||
Note how the constructor prepends the origin (if it exists) to the `heroesUrl`.
|
||||
|
||||
You don't provide `APP_BASE_HREF` in the browser version, so the `heroesUrl` remains relative.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
You can ignore `APP_BASE_HREF` in the browser if you've specified `<base href="/">` in the `index.html`
|
||||
to satisfy the router's need for a base address, as the tutorial sample does.
|
||||
|
||||
</div>
|
||||
|
||||
{@a server-code}
|
||||
|
||||
## Server code
|
||||
|
||||
To run an Angular Universal application, you'll need a server that accepts client requests and returns rendered pages.
|
||||
|
||||
{@a app-server-module}
|
||||
|
||||
### App server module
|
||||
|
||||
The app server module class (conventionally named `AppServerModule`) is an Angular module that wraps the application's root module (`AppModule`) so that Universal can mediate between your application and the server.
|
||||
`AppServerModule` also tells Angular how to bootstrap your application when running as a Universal app.
|
||||
|
||||
Create an `app.server.module.ts` file in the `src/app/` directory with the following `AppServerModule` code:
|
||||
|
||||
<code-example path="universal/src/app/app.server.module.ts" title="src/app/app.server.module.ts">
|
||||
</code-example>
|
||||
|
||||
Notice that it imports first the client app's `AppModule`, the Angular Universal's `ServerModule` and the `ModuleMapLoaderModule`.
|
||||
|
||||
The `ModuleMapLoaderModule` is a server-side module that allows lazy-loading of routes.
|
||||
|
||||
This is also the place to register providers that are specific to running your app under Universal.
|
||||
|
||||
{@a web-server}
|
||||
|
||||
### Universal web server
|
||||
|
||||
A _Universal_ web server responds to application _page_ requests with static HTML rendered by the [Universal template engine](#universal-engine).
|
||||
|
||||
It receives and responds to HTTP requests from clients (usually browsers).
|
||||
It serves static assets such as scripts, css, and images.
|
||||
It may respond to data requests, perhaps directly or as a proxy to a separate data server.
|
||||
|
||||
The sample web server for _this_ guide is based on the popular [Express](https://expressjs.com/) framework.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
_Any_ web server technology can serve a Universal app as long as it can call Universal's `renderModuleFactory`.
|
||||
The principles and decision points discussed below apply to any web server technology that you chose.
|
||||
|
||||
</div>
|
||||
|
||||
Create a `server.ts` file in the root directory and add the following code:
|
||||
|
||||
<code-example path="universal/server.ts" title="server.ts">
|
||||
</code-example>
|
||||
|
||||
<div class="alert is-critical">
|
||||
|
||||
**This sample server is not secure!**
|
||||
Be sure to add middleware to authenticate and authorize users
|
||||
just as you would for a normal Angular application server.
|
||||
|
||||
</div>
|
||||
|
||||
{@a universal-engine}
|
||||
#### Universal template engine
|
||||
|
||||
The important bit in this file is the `ngExpressEngine` function:
|
||||
|
||||
<code-example path="universal/server.ts" title="server.ts" region="ngExpressEngine">
|
||||
</code-example>
|
||||
|
||||
The `ngExpressEngine` is a wrapper around the universal's `renderModuleFactory` function that turns a client's requests into server-rendered HTML pages.
|
||||
You'll call that function within a _template engine_ that's appropriate for your server stack.
|
||||
|
||||
The first parameter is the `AppServerModule` that you wrote [earlier](#app-server-module).
|
||||
It's the bridge between the Universal server-side renderer and your application.
|
||||
|
||||
The second parameter is the `extraProviders`. It is an optional Angular dependency injection providers, applicable when running on this server.
|
||||
|
||||
{@a provide-origin}
|
||||
|
||||
You supply `extraProviders` when your app needs information that can only be determined by the currently running server instance.
|
||||
|
||||
The required information in this case is the running server's origin, provided under the `APP_BASE_HREF` token, so that the app can [calculate absolute HTTP URLs](#http-urls).
|
||||
|
||||
The `ngExpressEngine` function returns a _promise_ that resolves to the rendered page.
|
||||
|
||||
It's up to your engine to decide what to do with that page.
|
||||
_This engine's_ promise callback returns the rendered page to the [web server](#web-server),
|
||||
which then forwards it to the client in the HTTP response.
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
This wrappers are very useful to hide the complexity of the `renderModuleFactory`. There are more wrappers for different backend technologies
|
||||
at the [Universal repository](https://github.com/angular/universal).
|
||||
|
||||
</div>
|
||||
|
||||
#### Filter request URLs
|
||||
|
||||
The web server must distinguish _app page requests_ from other kinds of requests.
|
||||
|
||||
It's not as simple as intercepting a request to the root address `/`.
|
||||
The browser could ask for one of the application routes such as `/dashboard`, `/heroes`, or `/detail:12`.
|
||||
In fact, if the app were _only_ rendered by the server, _every_ app link clicked would arrive at the server
|
||||
as a navigation URL intended for the router.
|
||||
|
||||
Fortunately, application routes have something in common: their URLs lack file extensions.
|
||||
|
||||
Data requests also lack extensions but they're easy to recognize because they always begin with `/api`.
|
||||
|
||||
All static asset requests have a file extension (e.g., `main.js` or `/node_modules/zone.js/dist/zone.js`).
|
||||
|
||||
So we can easily recognize the three types of requests and handle them differently.
|
||||
|
||||
1. data request - request URL that begins `/api`
|
||||
2. app navigation - request URL with no file extension
|
||||
3. static asset - all other requests.
|
||||
|
||||
An Express server is a pipeline of middleware that filters and processes URL requests one after the other.
|
||||
|
||||
You configure the Express server pipeline with calls to `app.get()` like this one for data requests.
|
||||
|
||||
<code-example path="universal/server.ts" title="server.ts (data URL)" region="data-request" linenums="false">
|
||||
</code-example>
|
||||
|
||||
<div class="l-sub-section">
|
||||
|
||||
This sample server doesn't handle data requests.
|
||||
|
||||
The tutorial's "in-memory web api" module, a demo and development tool, intercepts all HTTP calls and
|
||||
simulates the behavior of a remote data server.
|
||||
In practice, you would remove that module and register your web api middleware on the server here.
|
||||
|
||||
</div>
|
||||
|
||||
<div class="alert is-critical">
|
||||
|
||||
**Universal HTTP requests have different security requirements**
|
||||
|
||||
HTTP requests issued from a browser app are not the same as when issued by the universal app on the server.
|
||||
|
||||
When a browser makes an HTTP request, the server can make assumptions about cookies, XSRF headers, etc.
|
||||
|
||||
For example, the browser automatically sends auth cookies for the current user.
|
||||
Angular Universal cannot forward these credentials to a separate data server.
|
||||
If your server handles HTTP requests, you'll have to add your own security plumbing.
|
||||
|
||||
</div>
|
||||
|
||||
The following code filters for request URLs with no extensions and treats them as navigation requests.
|
||||
|
||||
<code-example path="universal/server.ts" title="server.ts (navigation)" region="navigation-request" linenums="false">
|
||||
</code-example>
|
||||
|
||||
#### Serve static files safely
|
||||
|
||||
A single `app.use()` treats all other URLs as requests for static assets
|
||||
such as JavaScript, image, and style files.
|
||||
|
||||
To ensure that clients can only download the files that they are _permitted_ to see, you will [put all client-facing asset files in the `/dist` folder](#universal-webpack-configuration)
|
||||
and will only honor requests for files from the `/dist` folder.
|
||||
|
||||
The following express code routes all remaining requests to `/dist`; it returns a `404 - NOT FOUND` if the file is not found.
|
||||
|
||||
<code-example path="universal/server.ts" title="server.ts (static files)" region="static" linenums="false">
|
||||
</code-example>
|
||||
|
||||
{@a universal-configuration}
|
||||
|
||||
## Configure for Universal
|
||||
|
||||
The server application requires its own build configuration.
|
||||
|
||||
{@a universal-typescript-configuration}
|
||||
|
||||
### Universal TypeScript configuration
|
||||
|
||||
Create a `tsconfig.server.json` file in the project root directory to configure TypeScript and AOT compilation of the universal app.
|
||||
|
||||
<code-example path="universal/src/tsconfig.server.json" title="src/tsconfig.server.json">
|
||||
</code-example>
|
||||
|
||||
This config extends from the root's `tsconfig.json` file. Certain settings are noteworthy for their differences.
|
||||
|
||||
* The `module` property must be **commonjs** which can be require()'d into our server application.
|
||||
|
||||
* The `angularCompilerOptions` section guides the AOT compiler:
|
||||
* `entryModule` - the root module of the server application, expressed as `path/to/file#ClassName`.
|
||||
|
||||
### Universal Webpack configuration
|
||||
|
||||
Universal applications doesn't need any extra Webpack configuration, the CLI takes care of that for you,
|
||||
but since the server is a typescript application, you will use Webpack to transpile it.
|
||||
|
||||
Create a `webpack.server.config.js` file in the project root directory with the following code.
|
||||
|
||||
<code-example path="universal/webpack.server.config.js" title="webpack.server.config.js">
|
||||
</code-example>
|
||||
|
||||
**Webpack configuration** is a rich topic beyond the scope of this guide.
|
||||
|
||||
## Build and run with universal
|
||||
|
||||
Now that you've created the TypeScript and Webpack config files, you can build and run the Universal application.
|
||||
|
||||
First add the _build_ and _serve_ commands to the `scripts` section of the `package.json`:
|
||||
|
||||
<code-example format="." language="ts">
|
||||
"scripts": {
|
||||
...
|
||||
"build:universal": "npm run build:client-and-server-bundles && npm run webpack:server",
|
||||
"serve:universal": "node dist/server.js",
|
||||
"build:client-and-server-bundles": "ng build --prod && ng build --prod --app 1 --output-hashing=false",
|
||||
"webpack:server": "webpack --config webpack.server.config.js --progress --colors"
|
||||
...
|
||||
}
|
||||
</code-example>
|
||||
|
||||
{@a build}
|
||||
|
||||
#### Build
|
||||
|
||||
From the command prompt, type
|
||||
|
||||
<code-example format="." language="bash">
|
||||
npm run build:universal
|
||||
</code-example>
|
||||
|
||||
The Angular CLI compiles and bundles the universal app into two different folders, `browser` and `server`.
|
||||
Webpack transpiles the `server.ts` file into Javascript.
|
||||
|
||||
{@a serve}
|
||||
|
||||
#### Serve
|
||||
After building the application, start the server.
|
||||
|
||||
<code-example format="." language="bash">
|
||||
npm run serve:universal
|
||||
</code-example>
|
||||
|
||||
The console window should say
|
||||
|
||||
<code-example format="." language="bash">
|
||||
Node server listening on http://localhost:4000
|
||||
</code-example>
|
||||
|
||||
## Universal in action
|
||||
|
||||
Open a browser to http://localhost:4000/.
|
||||
You should see the familiar Tour of Heroes dashboard page.
|
||||
|
||||
Navigation via `routerLinks` works correctly.
|
||||
You can go from the Dashboard to the Heroes page and back.
|
||||
You can click on a hero on the Dashboard page to display its Details page.
|
||||
|
||||
But clicks, mouse-moves, and keyboard entries are inert.
|
||||
|
||||
* Clicking a hero on the Heroes page does nothing.
|
||||
* You can't add or delete a hero.
|
||||
* The search box on the Dashboard page is ignored.
|
||||
* The _back_ and _save_ buttons on the Details page don't work.
|
||||
|
||||
User events other than `routerLink` clicks aren't supported.
|
||||
The user must wait for the full client app to arrive.
|
||||
|
||||
It will never arrive until you compile the client app
|
||||
and move the output into the `dist/` folder,
|
||||
a step you'll take in just a moment.
|
||||
|
||||
## Throttling
|
||||
|
||||
The transition from the server-rendered app to the client app happens quickly on a development machine.
|
||||
You can simulate a slower network to see the transition more clearly and
|
||||
better appreciate the launch-speed advantage of a universal app running on a low powered, poorly connected device.
|
||||
|
||||
Open the Chrome Dev Tools and go to the Network tab.
|
||||
Find the [Network Throttling](https://developers.google.com/web/tools/chrome-devtools/network-performance/reference#throttling) dropdown on the far right of the menu bar.
|
||||
|
||||
Try one of the "3G" speeds.
|
||||
The server-rendered app still launches quickly but the full client app may take seconds to load.
|
||||
|
||||
{@a summary}
|
||||
## Summary
|
||||
|
||||
This guide showed you how to take an existing Angular application and make it into a Universal app that does server-side rendering.
|
||||
It also explained some of the key reasons for doing so.
|
||||
|
||||
- Facilitate web crawlers (SEO)
|
||||
- Support low-bandwidth or low-power devices
|
||||
- Fast first page load
|
||||
|
||||
Angular Universal can greatly improve the perceived startup performance of your app.
|
||||
The slower the network, the more advantageous it becomes to have Universal display the first page to the user.
|
BIN
aio/content/images/marketing/home/angular-connect.png
Normal file
BIN
aio/content/images/marketing/home/angular-connect.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.8 KiB |
@ -577,7 +577,7 @@
|
||||
|
||||
"amcdnl": {
|
||||
"name": "Austin McDaniel",
|
||||
"picture": "amcdnl.jpg",
|
||||
"picture": "amcdnl.jpeg",
|
||||
"twitter": "amcdnl",
|
||||
"website": "https://amcdnl.com",
|
||||
"bio": "Austin is an software architect with a passion for JavaScript and Angular. Austin loves to share his experiences with other like-minded developers by giving talks, blogging, podcasting and open-sourcing.",
|
||||
|
@ -13,18 +13,6 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- ngJapan -->
|
||||
<tr>
|
||||
<th><a href="http://ngjapan.org/" title="ng-Japan">ng-Japan</a></th>
|
||||
<td>Tokyo, Japan</td>
|
||||
<td>June 17, 2017</td>
|
||||
</tr>
|
||||
<!-- AngularMix -->
|
||||
<tr>
|
||||
<th><a href="https://angularmix.com/" title="AngularMix">AngularMix</a></th>
|
||||
<td>Universal Studios, Orlando, Florida</td>
|
||||
<td>October 8, 2017</td>
|
||||
</tr>
|
||||
<!-- ReactiveConf -->
|
||||
<tr>
|
||||
<th><a href="https://reactiveconf.com/" title="ReactiveConf">ReactiveConf</a></th>
|
||||
@ -35,7 +23,7 @@
|
||||
<tr>
|
||||
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
|
||||
<td>London, United Kingdom</td>
|
||||
<td>November 07, 2017</td>
|
||||
<td>November 7-8, 2017</td>
|
||||
</tr>
|
||||
<!-- ngAtlanta-->
|
||||
<tr>
|
||||
@ -43,11 +31,30 @@
|
||||
<td>Atlanta, Georgia</td>
|
||||
<td>January 30, 2018</td>
|
||||
</tr>
|
||||
<!-- ngVikings-->
|
||||
<tr>
|
||||
<th><a href="https://ngvikings.org/" title="ngVikings">ngVikings</a></th>
|
||||
<td>Helsinki, Finland</td>
|
||||
<td>March 1-2, 2018</td>
|
||||
</tr>
|
||||
<!-- ngconf 2018-->
|
||||
<tr>
|
||||
<th><a href="https://www.ng-conf.org/" title="ng-conf">ng-conf</a></th>
|
||||
<td>Salt Lake City, UT</td>
|
||||
<td>April 18-20, 2018</td>
|
||||
</tr>
|
||||
<!-- WeRDevs-->
|
||||
<tr>
|
||||
<th><a href="https://www.wearedevelopers.com/" title="WeAreDevs">WeAreDevelopers</a></th>
|
||||
<td>Vienna</td>
|
||||
<td>May 16-18, 2018</td>
|
||||
</tr>
|
||||
<!-- AngularConnect-->
|
||||
<tr>
|
||||
<th><a href="http://angularconnect.com" title="AngularConnect">AngularConnect</a></th>
|
||||
<td>London, United Kingdom</td>
|
||||
<td>November 5-7, 2018</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</article>
|
||||
|
@ -32,9 +32,9 @@
|
||||
<!-- Announcement Bar -->
|
||||
<div class="homepage-container">
|
||||
<div class="announcement-bar">
|
||||
<img src="generated/images/marketing/home/angular-mix.png" height="40" width="151">
|
||||
<p>Join us at our newest event, October 2017</p>
|
||||
<a class="button" href="https://angularmix.com/">Learn More</a>
|
||||
<img src="generated/images/marketing/home/angular-connect.png">
|
||||
<p>Join us in London for AngularConnect<br>November 7-8, 2017</p>
|
||||
<a class="button" href="https://angularconnect.com/">Learn More</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -317,7 +317,7 @@
|
||||
"desc": "Material Design components for Angular",
|
||||
"logo": "",
|
||||
"rev": true,
|
||||
"title": "Angular Material 2",
|
||||
"title": "Angular Material",
|
||||
"url": "https://github.com/angular/material2"
|
||||
},
|
||||
"aggrid": {
|
||||
|
@ -362,7 +362,11 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
{
|
||||
"url": "guide/universal",
|
||||
"title": "Server-side Rendering",
|
||||
"tooltip": "Render HTML server-side with Angular Universal."
|
||||
},
|
||||
{
|
||||
"url": "guide/visual-studio-2015",
|
||||
"title": "Visual Studio 2015 QuickStart",
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { browser, element, by, promise } from 'protractor';
|
||||
import { element, by } from 'protractor';
|
||||
import { SitePage } from './app.po';
|
||||
|
||||
describe('site App', function() {
|
||||
@ -41,20 +41,20 @@ describe('site App', function() {
|
||||
|
||||
describe('scrolling to the top', () => {
|
||||
it('should scroll to the top when navigating to another page', () => {
|
||||
page.navigateTo('guide/docs');
|
||||
page.navigateTo('guide/security');
|
||||
page.scrollToBottom();
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
|
||||
|
||||
page.navigateTo('guide/api');
|
||||
page.navigateTo('api');
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
|
||||
});
|
||||
|
||||
it('should scroll to the top when navigating to the same page', () => {
|
||||
page.navigateTo('guide/docs');
|
||||
page.navigateTo('guide/security');
|
||||
page.scrollToBottom();
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBeGreaterThan(0));
|
||||
|
||||
page.navigateTo('guide/docs');
|
||||
page.navigateTo('guide/security');
|
||||
page.getScrollTop().then(scrollTop => expect(scrollTop).toBe(0));
|
||||
});
|
||||
});
|
||||
@ -66,30 +66,35 @@ describe('site App', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(https://github.com/angular/angular/issues/19785): Activate this again
|
||||
// once it is no more flaky.
|
||||
describe('google analytics', () => {
|
||||
beforeEach(done => page.gaReady.then(done));
|
||||
|
||||
it('should call ga', done => {
|
||||
page.ga()
|
||||
.then(calls => {
|
||||
expect(calls.length).toBeGreaterThan(2, 'ga calls');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should call ga with initial URL', done => {
|
||||
let path: string;
|
||||
|
||||
page.navigateTo('api');
|
||||
page.locationPath()
|
||||
.then(p => path = p)
|
||||
.then(() => page.ga().then(calls => {
|
||||
expect(calls.length).toBeGreaterThan(2, 'ga calls');
|
||||
expect(calls[1]).toEqual(['set', 'page', path]);
|
||||
// The last call (length-1) will be the `send` command
|
||||
// The second to last call (length-2) will be the command to `set` the page url
|
||||
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
|
||||
// Todo: add test to confirm tracking URL when navigate.
|
||||
it('should call ga with new URL on navigation', done => {
|
||||
let path: string;
|
||||
page.getLink('features').click();
|
||||
page.locationPath()
|
||||
.then(p => path = p)
|
||||
.then(() => page.ga().then(calls => {
|
||||
// The last call (length-1) will be the `send` command
|
||||
// The second to last call (length-2) will be the command to `set` the page url
|
||||
expect(calls[calls.length - 2]).toEqual(['set', 'page', path]);
|
||||
done();
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe('search', () => {
|
||||
@ -100,4 +105,12 @@ describe('site App', function() {
|
||||
expect(page.getSearchResults().map(link => link.getText())).toContain('ControlValueAccessor');
|
||||
});
|
||||
});
|
||||
|
||||
describe('404 page', () => {
|
||||
it('should search the index for words found in the url', () => {
|
||||
page.navigateTo('http/router');
|
||||
expect(page.getSearchResults().map(link => link.getText())).toContain('Http');
|
||||
expect(page.getSearchResults().map(link => link.getText())).toContain('Router');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -12,7 +12,6 @@ export class SitePage {
|
||||
.all(by.css('a'))
|
||||
.filter((a: ElementFinder) => a.getAttribute('href').then(href => githubRegex.test(href)))
|
||||
.first();
|
||||
gaReady: promise.Promise<any>;
|
||||
|
||||
static setWindowWidth(newWidth: number) {
|
||||
const win = browser.driver.manage().window();
|
||||
@ -25,11 +24,14 @@ export class SitePage {
|
||||
.first();
|
||||
}
|
||||
getLink(path) { return element(by.css(`a[href="${path}"]`)); }
|
||||
ga() { return browser.executeScript('return window["gaCalls"]') as promise.Promise<any[][]>; }
|
||||
ga() { return browser.executeScript('return window["ga"].q') as promise.Promise<any[][]>; }
|
||||
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
|
||||
|
||||
navigateTo(pageUrl = '') {
|
||||
return browser.get('/' + pageUrl).then(_ => this.replaceGa(_));
|
||||
return browser.get('/' + pageUrl)
|
||||
// We need to tell the index.html not to load the real analytics library
|
||||
// See the GA snippet in index.html
|
||||
.then(() => browser.driver.executeScript('sessionStorage.setItem("__e2e__", true);'));
|
||||
}
|
||||
|
||||
getDocViewerText() {
|
||||
@ -61,31 +63,4 @@ export class SitePage {
|
||||
browser.wait(ExpectedConditions.presenceOf(results.first()), 8000);
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the ambient Google Analytics tracker with homebrew spy
|
||||
* don't send commands to GA during e2e testing!
|
||||
* @param _ - forward's anything passed in
|
||||
*/
|
||||
private replaceGa(_: any) {
|
||||
|
||||
this.gaReady = browser.driver.executeScript(() => {
|
||||
// Give ga() a "ready" callback:
|
||||
// https://developers.google.com/analytics/devguides/collection/analyticsjs/command-queue-reference
|
||||
window['ga'](() => {
|
||||
window['gaCalls'] = [];
|
||||
window['ga'] = function() { window['gaCalls'].push(arguments); };
|
||||
});
|
||||
|
||||
})
|
||||
.then(() => {
|
||||
// wait for GaService to start using window.ga after analytics lib loads.
|
||||
const d = promise.defer();
|
||||
setTimeout(() => d.fulfill(), 1000); // GaService.initializeDelay
|
||||
return d.promise;
|
||||
});
|
||||
|
||||
return _;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +44,17 @@
|
||||
"source": "**/!(*.*)",
|
||||
"destination": "/index.html"
|
||||
}
|
||||
],
|
||||
"headers": [
|
||||
{
|
||||
"source": "/",
|
||||
"headers": [
|
||||
{
|
||||
"key": "Link",
|
||||
"value": "</generated/navigation.json>;rel=preload;as=fetch,</generated/docs/index.json>;rel=preload;as=fetch"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,9 @@
|
||||
"author": "Angular",
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"aio-use-local": "node tools/ng-packages-installer overwrite . --debug --ignore-packages @angular/service-worker",
|
||||
"aio-use-npm": "node tools/ng-packages-installer restore .",
|
||||
"aio-check-local": "node tools/ng-packages-installer check .",
|
||||
"ng": "yarn check-env && ng",
|
||||
"start": "yarn check-env && ng serve",
|
||||
"prebuild": "yarn setup",
|
||||
@ -16,29 +19,31 @@
|
||||
"test": "yarn check-env && ng test",
|
||||
"pree2e": "yarn check-env && yarn ~~update-webdriver",
|
||||
"e2e": "ng e2e --no-webdriver-update",
|
||||
"presetup": "yarn ~~check-env && yarn install && yarn boilerplate:remove",
|
||||
"setup": "node tools/ng-packages-installer restore . && yarn boilerplate:add",
|
||||
"postsetup": "yarn build-ie-polyfills && yarn generate-plunkers && yarn generate-zips && yarn docs",
|
||||
"presetup": "yarn install --frozen-lockfile && yarn ~~check-env && yarn boilerplate:remove",
|
||||
"setup": "yarn aio-use-npm && yarn example-use-npm",
|
||||
"postsetup": "yarn boilerplate:add && yarn build-ie-polyfills && yarn generate-plunkers && yarn generate-zips && yarn docs",
|
||||
"presetup-local": "yarn presetup",
|
||||
"setup-local": "node tools/ng-packages-installer overwrite . && yarn boilerplate:add --local",
|
||||
"setup-local": "yarn aio-use-local && yarn example-use-local",
|
||||
"postsetup-local": "yarn postsetup",
|
||||
"pretest-pwa-score-localhost": "yarn build",
|
||||
"test-pwa-score-localhost": "concurrently --kill-others --success first \"http-server dist -p 4200 --silent\" \"yarn test-pwa-score http://localhost:4200 90\"",
|
||||
"test-pwa-score": "node scripts/test-pwa-score",
|
||||
"example-e2e": "yarn check-ng-packages tools/examples/shared && node ./tools/examples/run-example-e2e",
|
||||
"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-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",
|
||||
"deploy-production": "scripts/deploy-to-firebase.sh",
|
||||
"check-ng-packages": "node tools/ng-packages-installer check",
|
||||
"check-env": "yarn ~~check-env",
|
||||
"postcheck-env": "yarn check-ng-packages .",
|
||||
"postcheck-env": "yarn aio-check-local",
|
||||
"payload-size": "scripts/payload.sh",
|
||||
"predocs": "rimraf src/generated/{docs,*.json}",
|
||||
"docs": "dgeni ./tools/transforms/angular.io-package",
|
||||
"docs-watch": "node tools/transforms/authors-package/watchr.js",
|
||||
"docs-lint": "eslint --ignore-path=\"tools/transforms/.eslintignore\" tools/transforms",
|
||||
"docs-test": "node tools/transforms/test.js",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer.spec.js",
|
||||
"tools-test": "./scripts/deploy-to-firebase.test.sh && yarn docs-test && yarn boilerplate:test && jasmine tools/ng-packages-installer/index.spec.js",
|
||||
"serve-and-sync": "concurrently --kill-others \"yarn docs-watch\" \"yarn start\"",
|
||||
"boilerplate:add": "node ./tools/examples/example-boilerplate add",
|
||||
"boilerplate:remove": "node ./tools/examples/example-boilerplate remove",
|
||||
@ -47,7 +52,7 @@
|
||||
"generate-zips": "node ./tools/example-zipper/generateZips",
|
||||
"sw-manifest": "ngu-sw-manifest --dist dist --in ngsw-manifest.json --out dist/ngsw-manifest.json",
|
||||
"sw-copy": "cp node_modules/@angular/service-worker/bundles/worker-basic.min.js dist/",
|
||||
"postinstall": "uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
|
||||
"postinstall": "node tools/cli-patches/patch.js && uglifyjs node_modules/lunr/lunr.js -c -m -o src/assets/js/lunr.min.js --source-map",
|
||||
"build-ie-polyfills": "node node_modules/webpack/bin/webpack.js -p src/ie-polyfills.js src/generated/ie-polyfills.min.js",
|
||||
"~~check-env": "node scripts/check-environment",
|
||||
"~~build": "ng build --target=production --environment=stable -sm --build-optimizer",
|
||||
@ -60,31 +65,31 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^5.0.0-beta.3",
|
||||
"@angular/cdk": "^2.0.0-beta.8",
|
||||
"@angular/common": "^5.0.0-beta.3",
|
||||
"@angular/compiler": "^5.0.0-beta.3",
|
||||
"@angular/core": "^5.0.0-beta.3",
|
||||
"@angular/forms": "^5.0.0-beta.3",
|
||||
"@angular/http": "^5.0.0-beta.3",
|
||||
"@angular/material": "^2.0.0-beta.8",
|
||||
"@angular/platform-browser": "^5.0.0-beta.3",
|
||||
"@angular/platform-browser-dynamic": "^5.0.0-beta.3",
|
||||
"@angular/platform-server": "^5.0.0-beta.3",
|
||||
"@angular/router": "^5.0.0-beta.3",
|
||||
"@angular/animations": "^5.0.0-rc.9",
|
||||
"@angular/cdk": "^2.0.0-beta.12",
|
||||
"@angular/common": "^5.0.0-rc.9",
|
||||
"@angular/compiler": "^5.0.0-rc.9",
|
||||
"@angular/core": "^5.0.0-rc.9",
|
||||
"@angular/forms": "^5.0.0-rc.9",
|
||||
"@angular/http": "^5.0.0-rc.9",
|
||||
"@angular/material": "^2.0.0-beta.12",
|
||||
"@angular/platform-browser": "^5.0.0-rc.9",
|
||||
"@angular/platform-browser-dynamic": "^5.0.0-rc.9",
|
||||
"@angular/platform-server": "^5.0.0-rc.9",
|
||||
"@angular/router": "^5.0.0-rc.9",
|
||||
"@angular/service-worker": "^1.0.0-beta.16",
|
||||
"classlist.js": "^1.1.20150312",
|
||||
"core-js": "^2.4.1",
|
||||
"jasmine": "^2.6.0",
|
||||
"ng-pwa-tools": "^0.0.10",
|
||||
"rxjs": "^5.2.0",
|
||||
"rxjs": "^5.5.0",
|
||||
"tslib": "^1.7.1",
|
||||
"web-animations-js": "^2.2.5",
|
||||
"zone.js": "^0.8.16"
|
||||
"zone.js": "0.8.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "^1.3.0",
|
||||
"@angular/compiler-cli": "^5.0.0-beta.3",
|
||||
"@angular/cli": "^1.5.0-rc.8",
|
||||
"@angular/compiler-cli": "^5.0.0-rc.9",
|
||||
"@types/jasmine": "^2.5.52",
|
||||
"@types/node": "~6.0.60",
|
||||
"archiver": "^1.3.0",
|
||||
@ -94,7 +99,7 @@
|
||||
"concurrently": "^3.4.0",
|
||||
"cross-spawn": "^5.1.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "^0.21.4",
|
||||
"dgeni-packages": "0.22.0",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
@ -129,9 +134,9 @@
|
||||
"semver": "^5.3.0",
|
||||
"shelljs": "^0.7.7",
|
||||
"tree-kill": "^1.1.0",
|
||||
"ts-node": "~2.0.0",
|
||||
"ts-node": "^3.3.0",
|
||||
"tslint": "~4.5.0",
|
||||
"typescript": "2.3.2",
|
||||
"typescript": "^2.5.3",
|
||||
"uglify-js": "^3.0.15",
|
||||
"unist-util-filter": "^0.2.1",
|
||||
"unist-util-source": "^1.0.1",
|
||||
|
@ -4,11 +4,11 @@ set -u -e -o pipefail
|
||||
|
||||
declare -A payloadLimits
|
||||
payloadLimits["aio", "uncompressed", "inline"]=1600
|
||||
payloadLimits["aio", "uncompressed", "main"]=525000
|
||||
payloadLimits["aio", "uncompressed", "main"]=487000
|
||||
payloadLimits["aio", "uncompressed", "polyfills"]=38000
|
||||
payloadLimits["aio", "gzip7", "inline"]=1000
|
||||
payloadLimits["aio", "gzip7", "main"]=127000
|
||||
payloadLimits["aio", "gzip7", "polyfills"]=12500
|
||||
payloadLimits["aio", "gzip7", "main"]=120000
|
||||
payloadLimits["aio", "gzip7", "polyfills"]=11900
|
||||
payloadLimits["aio", "gzip9", "inline"]=1000
|
||||
payloadLimits["aio", "gzip9", "main"]=127000
|
||||
payloadLimits["aio", "gzip9", "polyfills"]=12500
|
||||
payloadLimits["aio", "gzip9", "main"]=120000
|
||||
payloadLimits["aio", "gzip9", "polyfills"]=11900
|
||||
|
@ -19,6 +19,11 @@ const config = require('lighthouse/lighthouse-core/config/default.js');
|
||||
// Constants
|
||||
const VIEWER_URL = 'https://googlechrome.github.io/lighthouse/viewer/';
|
||||
|
||||
// Specify the path to Chrome on Travis
|
||||
if (process.env.TRAVIS) {
|
||||
process.env.LIGHTHOUSE_CHROMIUM_PATH = process.env.CHROME_BIN;
|
||||
}
|
||||
|
||||
// Run
|
||||
_main(process.argv.slice(2));
|
||||
|
||||
|
@ -1,30 +1,30 @@
|
||||
<div id="top-of-page"></div>
|
||||
|
||||
<div *ngIf="isFetching" class="progress-bar-container">
|
||||
<md-progress-bar mode="indeterminate" color="warn"></md-progress-bar>
|
||||
<mat-progress-bar mode="indeterminate" color="warn"></mat-progress-bar>
|
||||
</div>
|
||||
|
||||
<md-toolbar color="primary" class="app-toolbar">
|
||||
<button class="hamburger" [class.starting]="isStarting" md-button
|
||||
<mat-toolbar color="primary" class="app-toolbar">
|
||||
<button class="hamburger" [class.starting]="isStarting" mat-button
|
||||
(click)="sidenav.toggle()" title="Docs menu">
|
||||
<md-icon [ngClass]="{'sidenav-open': !isSideBySide }" svgIcon="menu"></md-icon>
|
||||
<mat-icon [ngClass]="{'sidenav-open': !isSideBySide }" svgIcon="menu"></mat-icon>
|
||||
</button>
|
||||
<a class="nav-link home" href="/"><img src="{{ homeImageUrl }}" title="Home" alt="Home"></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>
|
||||
</md-toolbar>
|
||||
<aio-search-results #searchResults *ngIf="showSearchResults" (resultSelected)="hideSearchResults()"></aio-search-results>
|
||||
</mat-toolbar>
|
||||
<aio-search-results #searchResultsView *ngIf="showSearchResults" [searchResults]="searchResults | async" (resultSelected)="hideSearchResults()"></aio-search-results>
|
||||
|
||||
<md-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
|
||||
<mat-sidenav-container class="sidenav-container" [class.starting]="isStarting" [class.has-floating-toc]="hasFloatingToc" role="main">
|
||||
|
||||
<md-sidenav [ngClass]="{'collapsed': !isSideBySide }" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode" (open)="updateHostClasses()" (close)="updateHostClasses()">
|
||||
<mat-sidenav [ngClass]="{'collapsed': !isSideBySide }" #sidenav class="sidenav" [opened]="isOpened" [mode]="mode" (open)="updateHostClasses()" (close)="updateHostClasses()">
|
||||
<aio-nav-menu *ngIf="!isSideBySide" [nodes]="topMenuNarrowNodes" [currentNode]="currentNodes?.TopBarNarrow" [isWide]="false"></aio-nav-menu>
|
||||
<aio-nav-menu [nodes]="sideNavNodes" [currentNode]="currentNodes?.SideNav" [isWide]="isSideBySide"></aio-nav-menu>
|
||||
|
||||
<div class="doc-version">
|
||||
<aio-select (change)="onDocVersionChange($event.index)" [options]="docVersions" [selected]="currentDocVersion"></aio-select>
|
||||
</div>
|
||||
</md-sidenav>
|
||||
</mat-sidenav>
|
||||
|
||||
<section class="sidenav-content" [id]="pageId" role="content">
|
||||
<aio-mode-banner [mode]="deployment.mode" [version]="versionInfo"></aio-mode-banner>
|
||||
@ -32,7 +32,7 @@
|
||||
<aio-dt [on]="dtOn" [(doc)]="currentDocument"></aio-dt>
|
||||
</section>
|
||||
|
||||
</md-sidenav-container>
|
||||
</mat-sidenav-container>
|
||||
|
||||
<div *ngIf="hasFloatingToc" class="toc-container" [style.max-height.px]="tocMaxHeight" (mousewheel)="restrainScrolling($event)">
|
||||
<aio-toc></aio-toc>
|
||||
|
@ -1,12 +1,11 @@
|
||||
import { NO_ERRORS_SCHEMA, DebugElement } from '@angular/core';
|
||||
import { async, inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { inject, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
|
||||
import { Title } from '@angular/platform-browser';
|
||||
import { APP_BASE_HREF } from '@angular/common';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { MdProgressBar, MdSidenav } from '@angular/material';
|
||||
import { MatProgressBar, MatSidenav } from '@angular/material';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { of } from 'rxjs/observable/of';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
@ -22,9 +21,9 @@ import { MockSearchService } from 'testing/search.service';
|
||||
import { NavigationNode } from 'app/navigation/navigation.service';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
|
||||
import { SearchResultsComponent } from 'app/shared/search-results/search-results.component';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { SelectComponent, Option } from 'app/shared/select/select.component';
|
||||
import { SelectComponent } from 'app/shared/select/select.component';
|
||||
import { TocComponent } from 'app/embedded/toc/toc.component';
|
||||
import { TocItem, TocService } from 'app/shared/toc.service';
|
||||
|
||||
@ -38,7 +37,7 @@ describe('AppComponent', () => {
|
||||
let docViewer: HTMLElement;
|
||||
let hamburger: HTMLButtonElement;
|
||||
let locationService: MockLocationService;
|
||||
let sidenav: HTMLElement;
|
||||
let sidenav: MatSidenav;
|
||||
let tocService: TocService;
|
||||
|
||||
const initializeTest = () => {
|
||||
@ -52,7 +51,7 @@ describe('AppComponent', () => {
|
||||
docViewer = de.query(By.css('aio-doc-viewer')).nativeElement;
|
||||
hamburger = de.query(By.css('.hamburger')).nativeElement;
|
||||
locationService = de.injector.get(LocationService) as any as MockLocationService;
|
||||
sidenav = de.query(By.css('md-sidenav')).nativeElement;
|
||||
sidenav = de.query(By.directive(MatSidenav)).componentInstance;
|
||||
tocService = de.injector.get(TocService);
|
||||
};
|
||||
|
||||
@ -156,19 +155,19 @@ describe('AppComponent', () => {
|
||||
it('should open when nav to a guide page (guide/pipes)', () => {
|
||||
locationService.go('guide/pipes');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-open/);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
});
|
||||
|
||||
it('should open when nav to an api page', () => {
|
||||
locationService.go('api/a/b/c/d');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-open/);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
});
|
||||
|
||||
it('should be closed when nav to a marketing page (features)', () => {
|
||||
locationService.go('features');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
describe('when manually closed', () => {
|
||||
@ -181,19 +180,19 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should be closed', () => {
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should stay closed when nav from one guide page to another', () => {
|
||||
locationService.go('guide/bags');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should stay closed when nav from a guide page to api page', () => {
|
||||
locationService.go('api');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should reopen when nav to market page and back to guide page', () => {
|
||||
@ -201,7 +200,7 @@ describe('AppComponent', () => {
|
||||
fixture.detectChanges();
|
||||
locationService.go('guide/bags');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-open/);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -215,19 +214,19 @@ describe('AppComponent', () => {
|
||||
it('should be closed when nav to a guide page (guide/pipes)', () => {
|
||||
locationService.go('guide/pipes');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should be closed when nav to an api page', () => {
|
||||
locationService.go('api/a/b/c/d');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should be closed when nav to a marketing page (features)', () => {
|
||||
locationService.go('features');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
describe('when manually opened', () => {
|
||||
@ -240,32 +239,32 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should be open', () => {
|
||||
expect(sidenav.className).toMatch(/sidenav-open/);
|
||||
expect(sidenav.opened).toBe(true);
|
||||
});
|
||||
|
||||
it('should close when click in gray content area overlay', () => {
|
||||
const sidenavBackdrop = fixture.debugElement.query(By.css('.mat-sidenav-backdrop')).nativeElement;
|
||||
const sidenavBackdrop = fixture.debugElement.query(By.css('.mat-drawer-backdrop')).nativeElement;
|
||||
sidenavBackdrop.click();
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should close when nav to another guide page', () => {
|
||||
locationService.go('guide/bags');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should close when nav to api page', () => {
|
||||
locationService.go('api');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
it('should close again when nav to market page', () => {
|
||||
locationService.go('features');
|
||||
fixture.detectChanges();
|
||||
expect(sidenav.className).toMatch(/sidenav-clos/);
|
||||
expect(sidenav.opened).toBe(false);
|
||||
});
|
||||
|
||||
});
|
||||
@ -377,19 +376,17 @@ describe('AppComponent', () => {
|
||||
});
|
||||
|
||||
it('should set the css class of the host container based on the open/closed state of the side nav', () => {
|
||||
const sideNav = fixture.debugElement.query(By.directive(MdSidenav));
|
||||
|
||||
locationService.go('guide/pipes');
|
||||
fixture.detectChanges();
|
||||
checkHostClass('sidenav', 'open');
|
||||
|
||||
sideNav.componentInstance.opened = false;
|
||||
sideNav.triggerEventHandler('close', {});
|
||||
sidenav.close();
|
||||
sidenav.onClose.next();
|
||||
fixture.detectChanges();
|
||||
checkHostClass('sidenav', 'closed');
|
||||
|
||||
sideNav.componentInstance.opened = true;
|
||||
sideNav.triggerEventHandler('open', {});
|
||||
sidenav.open();
|
||||
sidenav.onOpen.next();
|
||||
fixture.detectChanges();
|
||||
checkHostClass('sidenav', 'open');
|
||||
});
|
||||
@ -883,7 +880,7 @@ describe('AppComponent', () => {
|
||||
|
||||
describe('initial rendering', () => {
|
||||
it('should initially add the starting class until the first document is rendered', fakeAsync(() => {
|
||||
const getSidenavContainer = () => fixture.debugElement.query(By.css('md-sidenav-container'));
|
||||
const getSidenavContainer = () => fixture.debugElement.query(By.css('mat-sidenav-container'));
|
||||
|
||||
initializeTest();
|
||||
|
||||
@ -910,7 +907,7 @@ describe('AppComponent', () => {
|
||||
describe('progress bar', () => {
|
||||
const SHOW_DELAY = 200;
|
||||
const HIDE_DELAY = 500;
|
||||
const getProgressBar = () => fixture.debugElement.query(By.directive(MdProgressBar));
|
||||
const getProgressBar = () => fixture.debugElement.query(By.directive(MatProgressBar));
|
||||
const initializeAndCompleteNavigation = () => {
|
||||
initializeTest();
|
||||
triggerDocRendered();
|
||||
@ -1054,11 +1051,6 @@ class TestGaService {
|
||||
locationChanged = jasmine.createSpy('locationChanged');
|
||||
}
|
||||
|
||||
class TestSearchService {
|
||||
initWorker = jasmine.createSpy('initWorker');
|
||||
loadIndex = jasmine.createSpy('loadIndex');
|
||||
}
|
||||
|
||||
class TestHttpClient {
|
||||
|
||||
static versionInfo = {
|
||||
|
@ -1,19 +1,19 @@
|
||||
import { Component, ElementRef, HostBinding, HostListener, OnInit,
|
||||
QueryList, ViewChild, ViewChildren } from '@angular/core';
|
||||
import { MdSidenav } from '@angular/material';
|
||||
import { MatSidenav } from '@angular/material';
|
||||
|
||||
import { CurrentNodes, NavigationService, NavigationViews, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import { CurrentNodes, NavigationService, NavigationNode, VersionInfo } from 'app/navigation/navigation.service';
|
||||
import { DocumentService, DocumentContents } from 'app/documents/document.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { SearchResultsComponent } from 'app/search/search-results/search-results.component';
|
||||
import { SearchBoxComponent } from 'app/search/search-box/search-box.component';
|
||||
import { SearchResults } from 'app/search/interfaces';
|
||||
import { SearchService } from 'app/search/search.service';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
import { combineLatest } from 'rxjs/observable/combineLatest';
|
||||
|
||||
@ -89,15 +89,14 @@ export class AppComponent implements OnInit {
|
||||
|
||||
// Search related properties
|
||||
showSearchResults = false;
|
||||
@ViewChildren('searchBox, searchResults', { read: ElementRef })
|
||||
searchResults: Observable<SearchResults>;
|
||||
@ViewChildren('searchBox, searchResultsView', { read: ElementRef })
|
||||
searchElements: QueryList<ElementRef>;
|
||||
@ViewChild(SearchResultsComponent)
|
||||
searchResults: SearchResultsComponent;
|
||||
@ViewChild(SearchBoxComponent)
|
||||
searchBox: SearchBoxComponent;
|
||||
|
||||
@ViewChild(MdSidenav)
|
||||
sidenav: MdSidenav;
|
||||
@ViewChild(MatSidenav)
|
||||
sidenav: MatSidenav;
|
||||
|
||||
constructor(
|
||||
public deployment: Deployment,
|
||||
@ -288,11 +287,11 @@ export class AppComponent implements OnInit {
|
||||
@HostListener('window:scroll')
|
||||
onScroll() {
|
||||
if (!this.tocMaxHeightOffset) {
|
||||
// Must wait until now for md-toolbar to be measurable.
|
||||
// Must wait until now for mat-toolbar to be measurable.
|
||||
const el = this.hostElement.nativeElement as Element;
|
||||
this.tocMaxHeightOffset =
|
||||
el.querySelector('footer').clientHeight +
|
||||
el.querySelector('md-toolbar.app-toolbar').clientHeight +
|
||||
el.querySelector('mat-toolbar.app-toolbar').clientHeight +
|
||||
24; // fudge margin
|
||||
}
|
||||
|
||||
@ -332,7 +331,7 @@ export class AppComponent implements OnInit {
|
||||
}
|
||||
|
||||
doSearch(query) {
|
||||
this.searchService.search(query);
|
||||
this.searchResults = this.searchService.search(query);
|
||||
this.showSearchResults = !!query;
|
||||
}
|
||||
|
||||
|
@ -6,18 +6,22 @@ import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { Location, LocationStrategy, PathLocationStrategy } from '@angular/common';
|
||||
|
||||
import {
|
||||
MdButtonModule,
|
||||
MdIconModule,
|
||||
MdIconRegistry,
|
||||
MdInputModule,
|
||||
MdProgressBarModule,
|
||||
MdSidenavModule,
|
||||
MdTabsModule,
|
||||
MdToolbarModule,
|
||||
Platform
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatIconRegistry,
|
||||
MatInputModule,
|
||||
MatProgressBarModule,
|
||||
MatSidenavModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule
|
||||
} from '@angular/material';
|
||||
|
||||
// Temporary fix for MdSidenavModule issue:
|
||||
import {
|
||||
Platform
|
||||
} from '@angular/cdk/platform';
|
||||
|
||||
|
||||
// Temporary fix for MatSidenavModule issue:
|
||||
// crashes with "missing first" operator when SideNav.mode is "over"
|
||||
import 'rxjs/add/operator/first';
|
||||
|
||||
@ -25,7 +29,7 @@ import { SwUpdatesModule } from 'app/sw-updates/sw-updates.module';
|
||||
|
||||
import { AppComponent } from 'app/app.component';
|
||||
import { ApiService } from 'app/embedded/api/api.service';
|
||||
import { CustomMdIconRegistry, SVG_ICONS } from 'app/shared/custom-md-icon-registry';
|
||||
import { CustomIconRegistry, SVG_ICONS } from 'app/shared/custom-icon-registry';
|
||||
import { Deployment } from 'app/shared/deployment.service';
|
||||
import { DocViewerComponent } from 'app/layout/doc-viewer/doc-viewer.component';
|
||||
import { DtComponent } from 'app/layout/doc-viewer/dt.component';
|
||||
@ -43,13 +47,13 @@ import { NavMenuComponent } from 'app/layout/nav-menu/nav-menu.component';
|
||||
import { NavItemComponent } from 'app/layout/nav-item/nav-item.component';
|
||||
import { ScrollService } from 'app/shared/scroll.service';
|
||||
import { ScrollSpyService } from 'app/shared/scroll-spy.service';
|
||||
import { SearchResultsComponent } from './search/search-results/search-results.component';
|
||||
import { SearchBoxComponent } from './search/search-box/search-box.component';
|
||||
import { TocService } from 'app/shared/toc.service';
|
||||
import { WindowToken, windowProvider } from 'app/shared/window';
|
||||
|
||||
import { SharedModule } from 'app/shared/shared.module';
|
||||
|
||||
// These are the hardcoded inline svg sources to be used by the `<md-icon>` component
|
||||
// These are the hardcoded inline svg sources to be used by the `<mat-icon>` component
|
||||
export const svgIconProviders = [
|
||||
{
|
||||
provide: SVG_ICONS,
|
||||
@ -77,13 +81,13 @@ export const svgIconProviders = [
|
||||
EmbeddedModule,
|
||||
HttpClientModule,
|
||||
BrowserAnimationsModule,
|
||||
MdButtonModule,
|
||||
MdIconModule,
|
||||
MdInputModule,
|
||||
MdProgressBarModule,
|
||||
MdSidenavModule,
|
||||
MdTabsModule,
|
||||
MdToolbarModule,
|
||||
MatButtonModule,
|
||||
MatIconModule,
|
||||
MatInputModule,
|
||||
MatProgressBarModule,
|
||||
MatSidenavModule,
|
||||
MatTabsModule,
|
||||
MatToolbarModule,
|
||||
SwUpdatesModule,
|
||||
SharedModule
|
||||
],
|
||||
@ -95,7 +99,6 @@ export const svgIconProviders = [
|
||||
ModeBannerComponent,
|
||||
NavMenuComponent,
|
||||
NavItemComponent,
|
||||
SearchResultsComponent,
|
||||
SearchBoxComponent,
|
||||
TopMenuComponent,
|
||||
],
|
||||
@ -108,14 +111,15 @@ export const svgIconProviders = [
|
||||
Location,
|
||||
{ provide: LocationStrategy, useClass: PathLocationStrategy },
|
||||
LocationService,
|
||||
{ provide: MdIconRegistry, useClass: CustomMdIconRegistry },
|
||||
{ provide: MatIconRegistry, useClass: CustomIconRegistry },
|
||||
NavigationService,
|
||||
Platform,
|
||||
ScrollService,
|
||||
ScrollSpyService,
|
||||
SearchService,
|
||||
svgIconProviders,
|
||||
TocService
|
||||
TocService,
|
||||
{ provide: WindowToken, useFactory: windowProvider },
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
})
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { Injector } from '@angular/core';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { Observable } from 'rxjs/Observable';
|
||||
import { Subscription } from 'rxjs/Subscription';
|
||||
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
|
||||
|
||||
import { LocationService } from 'app/shared/location.service';
|
||||
import { MockLocationService } from 'testing/location.service';
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user