Compare commits
229 Commits
5.1.0-beta
...
5.0.5
Author | SHA1 | Date | |
---|---|---|---|
9dc310eb50 | |||
b26bc9096e | |||
56c98f7c23 | |||
6bf07b4e60 | |||
a2ff4abddc | |||
ee37d4b26d | |||
f99335bc47 | |||
445d833b5d | |||
cbd93fe0d0 | |||
e1dfd9b17a | |||
e95b5d77bc | |||
9e69d97b77 | |||
b450c83091 | |||
38be44df9f | |||
e4ce695b66 | |||
4da184c29b | |||
647ca64216 | |||
53aff1fb83 | |||
71a6f1768e | |||
108b6e77dd | |||
ead759670b | |||
bdaee508cf | |||
e099911dd0 | |||
66fd1f8ce6 | |||
0e8f0288cb | |||
c7b211ca3c | |||
22bbd6e045 | |||
2a5b6194bb | |||
264260f997 | |||
c70cd258d5 | |||
2b0c896d78 | |||
c0bf1b0545 | |||
65a40e659b | |||
215832d8c6 | |||
686c4efe6d | |||
efce39605b | |||
b567f9cc21 | |||
4ff60c3562 | |||
bc904b19a4 | |||
135cf226bf | |||
0550383fc9 | |||
07699cbaec | |||
e4bb077d27 | |||
27e7439c3b | |||
66d160215e | |||
0feba49c4b | |||
9ca6ee9eed | |||
a5a82296b7 | |||
f9f2c20a57 | |||
662422a67e | |||
b53ead4c45 | |||
d0abfa3acc | |||
3bf36e9492 | |||
82aace63ea | |||
15795d09cf | |||
8ddbed8f7b | |||
81f1d42328 | |||
3df1542c87 | |||
8e1e7faef4 | |||
27f8c69d8a | |||
5d0dd97402 | |||
814f06289b | |||
b1f8eb14c8 | |||
f983d1cc50 | |||
12857922c5 | |||
dbdf9f76be | |||
d5eaf4de3c | |||
2d6126ec1b | |||
259f6bf638 | |||
03d549be05 | |||
bc4b4b5b55 | |||
00f2055c59 | |||
91efc7f70b | |||
8b1a6b1f24 | |||
b732fb935b | |||
0e2d962acb | |||
7acbc19b94 | |||
c69eda8f60 | |||
210ff78f4d | |||
96766f7336 | |||
52b50c4b6c | |||
398690d47c | |||
dc01fb167b | |||
7c26c06495 | |||
82dc7fa628 | |||
3101e89cf5 | |||
79c3e1b968 | |||
c96967b235 | |||
422cd0b665 | |||
eea2039bce | |||
612f508dff | |||
aa3d75ba83 | |||
4cc6abbbf8 | |||
222758bed3 | |||
516107fd04 | |||
bd70aece52 | |||
2ca6bdd110 | |||
2aefac841f | |||
ec496c2fda | |||
346dbcf24a | |||
48843a9f47 | |||
8f2fb02048 | |||
89e4262188 | |||
424a323316 | |||
28985cb2de | |||
5d1cd57787 | |||
9de45fa650 | |||
953a0e07d6 | |||
511e56d633 | |||
1f1741c5ad | |||
408ffb634e | |||
73d857cb38 | |||
3ab0963309 | |||
da22c48ef1 | |||
f80d41f3e5 | |||
2b7ca4e9f8 | |||
4292540939 | |||
708379128f | |||
131368c774 | |||
94e0ef77ea | |||
e4cd3b0564 | |||
22dc3ad94c | |||
7077a61614 | |||
9811bef278 | |||
3298c4a79f | |||
5a6efa7a3f | |||
a8c786c8c9 | |||
799cbb932c | |||
e6f16a7629 | |||
1bb0333c23 | |||
9fa0ffd8a5 | |||
678d1cfae1 | |||
a897d6842f | |||
25843fed53 | |||
762da3b154 | |||
37a740b5b2 | |||
067e926c08 | |||
bc52e97eba | |||
9b579d85be | |||
895d78c0ed | |||
803b0f0fb3 | |||
86399958dd | |||
d550b6872a | |||
92e4c34d01 | |||
e84695389b | |||
4e48bdff22 | |||
2185e466db | |||
6f05dab2fc | |||
18197bd56d | |||
c75740e585 | |||
16d72584cb | |||
9c9867e840 | |||
55092ace87 | |||
70edca3cec | |||
120ebe8225 | |||
7695ad570b | |||
7a708ad387 | |||
71f1ed5795 | |||
ced5186ca4 | |||
e48dc270b5 | |||
aa574710c9 | |||
d62ef132d2 | |||
428f80e8ae | |||
260c9b763c | |||
5f6d71d39a | |||
8133a8d335 | |||
3e05d62a99 | |||
db7f2a9ca7 | |||
59a2fbe74f | |||
3274e1a79a | |||
6979a29d1f | |||
9da5ca7ae6 | |||
e51ac3671f | |||
3393009b69 | |||
12e4b5667e | |||
0f8b83200a | |||
253e89dfac | |||
c4772ee002 | |||
35ec4af2b1 | |||
19b48429ef | |||
b796419e7e | |||
a1ec65b1dd | |||
851e1cfcfd | |||
a363be6b5c | |||
e1fb65cac4 | |||
b497e9e6ee | |||
706ba38498 | |||
78052e4984 | |||
bf861f5539 | |||
d99a5ec494 | |||
d651fc0d6a | |||
5775376bcf | |||
896b853519 | |||
5225fdbc0e | |||
f5b7f2b9a5 | |||
509f392ab0 | |||
cf5fce8d5e | |||
f1248b69e6 | |||
4498dddbe3 | |||
812786f44e | |||
de24d54517 | |||
c295aeeca2 | |||
a8add78fe1 | |||
e3a16ed02d | |||
fd37f3fbab | |||
85e95cc32b | |||
de71ba74bb | |||
a01c877534 | |||
2d508a3ef0 | |||
4285b6c3e3 | |||
5542517b9c | |||
fef3539608 | |||
f4d5729cb3 | |||
d343bf7885 | |||
9ce7f0e538 | |||
4a23df3909 | |||
14016c781f | |||
47caebfe86 | |||
5cfd9c6020 | |||
47bc6f105d | |||
40fa2593a9 | |||
680bcf7b8a | |||
ef08330341 | |||
6cc042e2ba | |||
9b26455740 | |||
18bce5987c | |||
f1108fea76 | |||
64b3e3e41a | |||
a82f863e24 |
18
.bazelrc
Normal file
18
.bazelrc
Normal file
@ -0,0 +1,18 @@
|
||||
# Make compilation fast, by keeping a few copies of the compilers
|
||||
# running as daemons, and cache SourceFile AST's to reduce parse time.
|
||||
build --strategy=TypeScriptCompile=worker
|
||||
build --strategy=AngularTemplateCompile=worker
|
||||
|
||||
# Don't create bazel-* symlinks in the WORKSPACE directory.
|
||||
# These require .gitignore and may scare users.
|
||||
# Also, it's a workaround for https://github.com/bazelbuild/rules_typescript/issues/12
|
||||
# which affects the common case of having `tsconfig.json` in the WORKSPACE directory.
|
||||
#
|
||||
# Instead, you should run `bazel info bazel-bin` to find out where the outputs went.
|
||||
build --symlink_prefix=/
|
||||
|
||||
# Performance: avoid stat'ing input files
|
||||
build --watchfs
|
||||
|
||||
# Don't print all the .d.ts output locations after builds
|
||||
build --show_result=0
|
@ -279,7 +279,7 @@ groups:
|
||||
files:
|
||||
- "packages/benchpress/*"
|
||||
users:
|
||||
# needs primary
|
||||
- alxhub #primary
|
||||
# needs secondary
|
||||
- IgorMinar #fallback
|
||||
- mhevery #fallback
|
||||
|
73
CHANGELOG.md
73
CHANGELOG.md
@ -1,24 +1,31 @@
|
||||
<a name="5.1.0-beta.2"></a>
|
||||
# [5.1.0-beta.2](https://github.com/angular/angular/compare/5.1.0-beta.1...5.1.0-beta.2) (2017-11-22)
|
||||
<a name="5.0.5"></a>
|
||||
## [5.0.5](https://github.com/angular/angular/compare/5.0.4...5.0.5) (2017-12-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always fire inner trigger callbacks even if blocked by parent animations ([#19753](https://github.com/angular/angular/issues/19753)) ([0e012c9](https://github.com/angular/angular/commit/0e012c9)), closes [#19100](https://github.com/angular/angular/issues/19100)
|
||||
* **animations:** always fire start and done callbacks in order for noop animations ([#20570](https://github.com/angular/angular/issues/20570)) ([ffb6dbe](https://github.com/angular/angular/commit/ffb6dbe))
|
||||
* **animations:** validate against trigger() names that use @ symbols ([#20326](https://github.com/angular/angular/issues/20326)) ([1861e41](https://github.com/angular/angular/commit/1861e41))
|
||||
* **benchpress:** Allow ignoring navigationStart events in perflog metric. ([#20312](https://github.com/angular/angular/issues/20312)) ([717ac5a](https://github.com/angular/angular/commit/717ac5a))
|
||||
* **common:** return ISubscription from Location.subscribe() ([#20429](https://github.com/angular/angular/issues/20429)) ([437a044](https://github.com/angular/angular/commit/437a044)), closes [#20406](https://github.com/angular/angular/issues/20406)
|
||||
* **compiler:** emit correct type-check-blocks with TemplateRef's ([#20463](https://github.com/angular/angular/issues/20463)) ([68b53c0](https://github.com/angular/angular/commit/68b53c0))
|
||||
* **compiler:** support event bindings in `fullTemplateTypeCheck` ([#20490](https://github.com/angular/angular/issues/20490)) ([4ed0439](https://github.com/angular/angular/commit/4ed0439))
|
||||
* **core:** fix [#20532](https://github.com/angular/angular/issues/20532), should be able to cancel listener from mixed zone ([#20538](https://github.com/angular/angular/issues/20538)) ([a740e4f](https://github.com/angular/angular/commit/a740e4f))
|
||||
* **core:** should support event.stopImmediatePropagation ([#20469](https://github.com/angular/angular/issues/20469)) ([997336b](https://github.com/angular/angular/commit/997336b))
|
||||
* **forms:** updateOn should check if change occurred ([#20358](https://github.com/angular/angular/issues/20358)) ([69c53c3](https://github.com/angular/angular/commit/69c53c3)), closes [#20259](https://github.com/angular/angular/issues/20259)
|
||||
* **compiler-cli:** propagate ts.SourceFile moduleName into metadata ([a2ff4ab](https://github.com/angular/angular/commit/a2ff4ab))
|
||||
* **service-worker:** allow disabling SW while still using services ([f99335b](https://github.com/angular/angular/commit/f99335b))
|
||||
* **service-worker:** don't crash if SW not supported ([ee37d4b](https://github.com/angular/angular/commit/ee37d4b))
|
||||
* **service-worker:** send initialization signal from the application ([6bf07b4](https://github.com/angular/angular/commit/6bf07b4))
|
||||
* **service-worker:** use relative path for ngsw.json ([56c98f7](https://github.com/angular/angular/commit/56c98f7))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **platform-browser-dynamic:** export `JitCompilerFactory` ([#20478](https://github.com/angular/angular/issues/20478)) ([d7a727c](https://github.com/angular/angular/commit/d7a727c)), closes [#20125](https://github.com/angular/angular/issues/20125)
|
||||
<a name="5.0.4"></a>
|
||||
## [5.0.4](https://github.com/angular/angular/compare/5.0.3...5.0.4) (2017-12-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** ensure multi-level enter animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([22bbd6e](https://github.com/angular/angular/commit/22bbd6e))
|
||||
* **animations:** ensure multi-level leave animations work ([#19455](https://github.com/angular/angular/issues/19455)) ([c7b211c](https://github.com/angular/angular/commit/c7b211c))
|
||||
* **common:** accept falsy values as HTTP bodies ([#19958](https://github.com/angular/angular/issues/19958)) ([66fd1f8](https://github.com/angular/angular/commit/66fd1f8)), closes [#19825](https://github.com/angular/angular/issues/19825) [#19195](https://github.com/angular/angular/issues/19195)
|
||||
* **common:** don't strip XSSI prefix for if error isn't JSON ([#19958](https://github.com/angular/angular/issues/19958)) ([ead7596](https://github.com/angular/angular/commit/ead7596))
|
||||
* **common:** remove useless guard in HttpClient ([#19958](https://github.com/angular/angular/issues/19958)) ([e099911](https://github.com/angular/angular/commit/e099911)), closes [#19223](https://github.com/angular/angular/issues/19223)
|
||||
* **common:** treat an empty body as null when parsing JSON in HttpClient ([#19958](https://github.com/angular/angular/issues/19958)) ([bdaee50](https://github.com/angular/angular/commit/bdaee50)), closes [#18680](https://github.com/angular/angular/issues/18680) [#19413](https://github.com/angular/angular/issues/19413) [#19502](https://github.com/angular/angular/issues/19502) [#19555](https://github.com/angular/angular/issues/19555)
|
||||
* **compiler-cli:** fix memory leak in program creation ([#20692](https://github.com/angular/angular/issues/20692)) ([38be44d](https://github.com/angular/angular/commit/38be44d)), closes [#20691](https://github.com/angular/angular/issues/20691)
|
||||
* **compiler-cli:** normalize sourcepaths for i18n extracted files ([#20417](https://github.com/angular/angular/issues/20417)) ([2b0c896](https://github.com/angular/angular/commit/2b0c896)), closes [#20416](https://github.com/angular/angular/issues/20416)
|
||||
|
||||
|
||||
|
||||
@ -39,28 +46,6 @@
|
||||
* **forms:** updateOn should check if change occurred ([#20358](https://github.com/angular/angular/issues/20358)) ([f9f2c20](https://github.com/angular/angular/commit/f9f2c20)), closes [#20259](https://github.com/angular/angular/issues/20259)
|
||||
|
||||
|
||||
<a name="5.1.0-beta.1"></a>
|
||||
# [5.1.0-beta.1](https://github.com/angular/angular/compare/5.1.0-beta.0...5.1.0-beta.1) (2017-11-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **animations:** always fire inner trigger callbacks even if blocked by parent animations ([#19753](https://github.com/angular/angular/issues/19753)) ([d47b2a6](https://github.com/angular/angular/commit/d47b2a6)), closes [#19100](https://github.com/angular/angular/issues/19100)
|
||||
* **animations:** ensure final state() styles are applied within @.disabled animations ([#20267](https://github.com/angular/angular/issues/20267)) ([20aafff](https://github.com/angular/angular/commit/20aafff)), closes [#20266](https://github.com/angular/angular/issues/20266)
|
||||
* **bazel:** adjust mock of tsconfig for ng_module rule unit test ([#20175](https://github.com/angular/angular/issues/20175)) ([c2a24b4](https://github.com/angular/angular/commit/c2a24b4))
|
||||
* **compiler:** fix corner cases in shadow CSS ([c32f5fd](https://github.com/angular/angular/commit/c32f5fd))
|
||||
* **compiler:** recognize @NgModule with a redundant @Injectable ([#20320](https://github.com/angular/angular/issues/20320)) ([c33a576](https://github.com/angular/angular/commit/c33a576))
|
||||
* **compiler:** show explanatory text in template errors ([#20313](https://github.com/angular/angular/issues/20313)) ([3257fcd](https://github.com/angular/angular/commit/3257fcd))
|
||||
* **core:** ensure init lifecycle events are called ([#20258](https://github.com/angular/angular/issues/20258)) ([24cf8b3](https://github.com/angular/angular/commit/24cf8b3))
|
||||
* **language-service:** pass compilerOptions.paths to ReflectorHost ([#20222](https://github.com/angular/angular/issues/20222)) ([eb8013e](https://github.com/angular/angular/commit/eb8013e))
|
||||
* **router:** 'merge' queryParamHandling strategy should be able to remove query params ([#19733](https://github.com/angular/angular/issues/19733)) ([a622e19](https://github.com/angular/angular/commit/a622e19)), closes [#18463](https://github.com/angular/angular/issues/18463) [#17202](https://github.com/angular/angular/issues/17202)
|
||||
* Update test code to type-check under TS 2.5 ([#20175](https://github.com/angular/angular/issues/20175)) ([5ec1717](https://github.com/angular/angular/commit/5ec1717))
|
||||
|
||||
### Features
|
||||
|
||||
* **typescript:** support TypeScript 2.5 ([a9f3e2b](https://github.com/angular/angular/commit/a9f3e2b)), closes [#20175](https://github.com/angular/angular/issues/20175)
|
||||
|
||||
> Note, if you do `Injector.get(Token)` where `Token` has static members, you'll run into https://github.com/Microsoft/TypeScript/issues/20102 where the returned type is `{}` rather than `Token`. Use `Injector.get<Token>(Token)` to work around.
|
||||
|
||||
<a name="5.0.2"></a>
|
||||
## [5.0.2](https://github.com/angular/angular/compare/5.0.1...5.0.2) (2017-11-16)
|
||||
@ -75,22 +60,6 @@
|
||||
* **router:** 'merge' queryParamHandling strategy should be able to remove query params ([#19733](https://github.com/angular/angular/issues/19733)) ([b732fb9](https://github.com/angular/angular/commit/b732fb9)), closes [#18463](https://github.com/angular/angular/issues/18463) [#17202](https://github.com/angular/angular/issues/17202)
|
||||
|
||||
|
||||
<a name="5.1.0-beta.0"></a>
|
||||
# [5.1.0-beta.0](https://github.com/angular/angular/compare/5.0.0-rc.4...5.1.0-beta.0) (2017-11-08)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** don't overwrite missingTranslation's value in JIT ([#19952](https://github.com/angular/angular/issues/19952)) ([799cbb9](https://github.com/angular/angular/commit/799cbb9))
|
||||
* **compiler:** report a reasonable error with invalid metadata ([#20062](https://github.com/angular/angular/issues/20062)) ([da22c48](https://github.com/angular/angular/commit/da22c48))
|
||||
* **compiler-cli:** don't report emit diagnostics when `--noEmitOnError` is off ([#20063](https://github.com/angular/angular/issues/20063)) ([8639995](https://github.com/angular/angular/commit/8639995))
|
||||
* **core:** `__symbol__` should return `__zone_symbol__` without zone.js loaded ([#19541](https://github.com/angular/angular/issues/19541)) ([678d1cf](https://github.com/angular/angular/commit/678d1cf))
|
||||
* **core:** should support event.stopImmediatePropagation ([#19222](https://github.com/angular/angular/issues/19222)) ([7083791](https://github.com/angular/angular/commit/7083791))
|
||||
* **platform-browser:** support Symbols in custom `jasmineToString()` method ([#19794](https://github.com/angular/angular/issues/19794)) ([5a6efa7](https://github.com/angular/angular/commit/5a6efa7))
|
||||
|
||||
### Features
|
||||
|
||||
* **compiler:** introduce `TestBed.overrideTemplateUsingTestingModule` ([a460066](https://github.com/angular/angular/commit/a460066)), closes [#19815](https://github.com/angular/angular/issues/19815)
|
||||
|
||||
|
||||
<a name="5.0.1"></a>
|
||||
## [5.0.1](https://github.com/angular/angular/compare/5.0.0...5.0.1) (2017-11-08)
|
||||
|
@ -69,37 +69,36 @@ You can file new issues by filling out our [new issue form](https://github.com/a
|
||||
### <a name="submit-pr"></a> Submitting a Pull Request (PR)
|
||||
Before you submit your Pull Request (PR) consider the following guidelines:
|
||||
|
||||
1. Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||
* Search [GitHub](https://github.com/angular/angular/pulls) for an open or closed PR
|
||||
that relates to your submission. You don't want to duplicate effort.
|
||||
1. Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
* Please sign our [Contributor License Agreement (CLA)](#cla) before sending PRs.
|
||||
We cannot accept code without this.
|
||||
1. Fork the angular/angular repo.
|
||||
1. Make your changes in a new git branch:
|
||||
* Make your changes in a new git branch:
|
||||
|
||||
```shell
|
||||
git checkout -b my-fix-branch master
|
||||
```
|
||||
|
||||
1. Create your patch, **including appropriate test cases**.
|
||||
1. Follow our [Coding Rules](#rules).
|
||||
1. Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||
* Create your patch, **including appropriate test cases**.
|
||||
* Follow our [Coding Rules](#rules).
|
||||
* Run the full Angular test suite, as described in the [developer documentation][dev-doc],
|
||||
and ensure that all tests pass.
|
||||
1. Commit your changes using a descriptive commit message that follows our
|
||||
* Commit your changes using a descriptive commit message that follows our
|
||||
[commit message conventions](#commit). Adherence to these conventions
|
||||
is necessary because release notes are automatically generated from these messages.
|
||||
|
||||
```shell
|
||||
git commit -a
|
||||
```
|
||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||
Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
|
||||
|
||||
1. Push your branch to GitHub:
|
||||
* Push your branch to GitHub:
|
||||
|
||||
```shell
|
||||
git push origin my-fix-branch
|
||||
```
|
||||
|
||||
1. In GitHub, send a pull request to `angular:master`.
|
||||
* In GitHub, send a pull request to `angular:master`.
|
||||
* If we suggest changes then:
|
||||
* Make the required updates.
|
||||
* Re-run the Angular test suites to ensure tests are still passing.
|
||||
|
@ -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",
|
||||
commit = "0.2.1",
|
||||
# TODO(alexeagle): use the correct tag here.
|
||||
commit = "2c6243df53fd33fdab283ebdd01582e4eb815db8",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<blockquote>
|
||||
<!-- #docregion built-in, asterisk, ngif -->
|
||||
<div *ngIf="hero" >{{hero.name}}</div>
|
||||
<div *ngIf="hero" class="name">{{hero.name}}</div>
|
||||
<!-- #enddocregion built-in, asterisk, ngif -->
|
||||
</blockquote>
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
<p><ng-template> element</p>
|
||||
<!-- #docregion ngif-template -->
|
||||
<ng-template [ngIf]="hero">
|
||||
<div>{{hero.name}}</div>
|
||||
<div class="name">{{hero.name}}</div>
|
||||
</ng-template>
|
||||
<!-- #enddocregion ngif-template -->
|
||||
|
||||
|
@ -74,8 +74,8 @@ Generate a new project and skeleton application by running the following command
|
||||
|
||||
|
||||
|
||||
Patience please.
|
||||
It takes time to set up a new project, most of it spent installing npm packages.
|
||||
Patience, please.
|
||||
It takes time to set up a new project; most of it is spent installing npm packages.
|
||||
|
||||
|
||||
</div>
|
||||
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 85 KiB After Width: | Height: | Size: 79 KiB |
@ -241,6 +241,12 @@
|
||||
"UI Components": {
|
||||
"order": 4,
|
||||
"resources": {
|
||||
"IgniteUIforAngular": {
|
||||
"desc": "Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps.",
|
||||
"rev": true,
|
||||
"title": "Ignite UI for Angular",
|
||||
"url": "https://www.infragistics.com/products/ignite-ui-angular"
|
||||
},
|
||||
"DevExtreme": {
|
||||
"desc": "50+ UI components including data grid, pivot grid, scheduler, charts, editors, maps and other multi-purpose controls for creating highly responsive web applications for touch devices and traditional desktops.",
|
||||
"rev": true,
|
||||
|
@ -3,7 +3,7 @@
|
||||
The Tour of Heroes `HeroesComponent` is currently getting and displaying fake data.
|
||||
|
||||
After the refactoring in this tutorial, `HeroesComponent` will be lean and focused on supporting the view.
|
||||
It will also be easier to unit-test with a mock services.
|
||||
It will also be easier to unit-test with a mock service.
|
||||
|
||||
## Why services
|
||||
|
||||
|
@ -28,7 +28,7 @@ describe('Api pages', function() {
|
||||
|
||||
it('should show readonly properties as getters', () => {
|
||||
const page = new ApiPage('api/common/http/HttpRequest');
|
||||
expect(page.getOverview('class').getText()).toContain('get body: T|null');
|
||||
expect(page.getOverview('class').getText()).toContain('get body: T | null');
|
||||
});
|
||||
|
||||
it('should not show parenthesis for getters', () => {
|
||||
|
@ -80,8 +80,6 @@ describe('site App', function() {
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(https://github.com/angular/angular/issues/19785): Activate this again
|
||||
// once it is no more flaky.
|
||||
describe('google analytics', () => {
|
||||
|
||||
it('should call ga with initial URL', done => {
|
||||
|
@ -29,10 +29,7 @@ export class SitePage {
|
||||
locationPath() { return browser.executeScript('return document.location.pathname') as promise.Promise<string>; }
|
||||
|
||||
navigateTo(pageUrl = '') {
|
||||
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.executeScript('sessionStorage.setItem("__e2e__", true);'));
|
||||
return browser.get('/' + pageUrl);
|
||||
}
|
||||
|
||||
getDocViewerText() {
|
||||
|
@ -100,7 +100,7 @@
|
||||
"cross-spawn": "^5.1.0",
|
||||
"css-selector-parser": "^1.3.0",
|
||||
"dgeni": "^0.4.7",
|
||||
"dgeni-packages": "0.22.0",
|
||||
"dgeni-packages": "0.22.1",
|
||||
"entities": "^1.1.1",
|
||||
"eslint": "^3.19.0",
|
||||
"eslint-plugin-jasmine": "^2.2.0",
|
||||
|
@ -34,11 +34,13 @@
|
||||
|
||||
<!-- Google Analytics -->
|
||||
<script>
|
||||
// Note this is a customised version of the GA tracking snippet to aid e2e testing
|
||||
// See the bit between /**/.../**/
|
||||
// Note this is a customised version of the GA tracking snippet
|
||||
// See the comments below for more info
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;/**/i.sessionStorage.__e2e__||/**/m.parentNode.insertBefore(a,m)
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;
|
||||
~i.name.indexOf('NG_DEFER_BOOTSTRAP')|| // only load library if not running e2e tests
|
||||
m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
</script>
|
||||
<!-- End Google Analytics -->
|
||||
|
@ -62,7 +62,7 @@ class ExampleZipper {
|
||||
let exampleZipName;
|
||||
const exampleType = this._getExampleType(path.join(sourceDirName, relativeDirName));
|
||||
if (relativeDirName.indexOf('/') !== -1) { // Special example
|
||||
exampleZipName = relativeDirName.split('/')[0];
|
||||
exampleZipName = relativeDirName.split('/').join('-');
|
||||
} else {
|
||||
exampleZipName = jsonFileName.replace(/(plnkr|zipper).json/, relativeDirName);
|
||||
}
|
||||
|
@ -26,13 +26,13 @@
|
||||
"zone.js": "^0.8.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cli": "1.5.0",
|
||||
"@angular/cli": "1.5.4",
|
||||
"@angular/compiler-cli": "^5.0.0",
|
||||
"@angular/language-service": "^5.0.0",
|
||||
"@types/jasmine": "~2.5.53",
|
||||
"@types/jasminewd2": "~2.0.2",
|
||||
"@types/node": "~6.0.60",
|
||||
"codelyzer": "~3.2.0",
|
||||
"codelyzer": "^4.0.1",
|
||||
"jasmine-core": "~2.6.2",
|
||||
"jasmine-spec-reporter": "~4.1.0",
|
||||
"karma": "~1.7.0",
|
||||
|
@ -2287,9 +2287,9 @@ devtools-timeline-model@1.1.6:
|
||||
chrome-devtools-frontend "1.0.401423"
|
||||
resolve "1.1.7"
|
||||
|
||||
dgeni-packages@0.22.0:
|
||||
version "0.22.0"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.22.0.tgz#7ed07af9074f6547847256c1a65b488a5a17ad03"
|
||||
dgeni-packages@0.22.1:
|
||||
version "0.22.1"
|
||||
resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.22.1.tgz#c4587a765689c4c9d48ed661517ed2249403bfb2"
|
||||
dependencies:
|
||||
canonical-path "0.0.2"
|
||||
catharsis "^0.8.1"
|
||||
|
@ -3,7 +3,7 @@ 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.2.1",
|
||||
tag = "0.0.2",
|
||||
)
|
||||
|
||||
load("@build_bazel_rules_nodejs//:defs.bzl", "node_repositories")
|
||||
|
@ -1,19 +0,0 @@
|
||||
import { AppPage } from './app.po';
|
||||
|
||||
describe('dynamic-compiler App', () => {
|
||||
let page: AppPage;
|
||||
|
||||
beforeEach(() => {
|
||||
page = new AppPage();
|
||||
});
|
||||
|
||||
it('should display welcome message', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getParagraphText()).toEqual('Hello world!');
|
||||
});
|
||||
|
||||
it('should display lazy-loaded component', () => {
|
||||
page.navigateTo();
|
||||
expect(page.getLazyLoadedText()).toEqual('Lazy-loaded component!');
|
||||
});
|
||||
});
|
@ -1,16 +0,0 @@
|
||||
import {browser, by, element, protractor} from 'protractor';
|
||||
|
||||
export class AppPage {
|
||||
navigateTo() {
|
||||
return browser.get('/');
|
||||
}
|
||||
|
||||
getParagraphText() {
|
||||
return element(by.css('app-root h1')).getText();
|
||||
}
|
||||
|
||||
getLazyLoadedText() {
|
||||
const el = element(by.css('app-root lazy-component'));
|
||||
return browser.wait(protractor.ExpectedConditions.presenceOf(el)).then(() => el.getText(), err => el.getText());
|
||||
}
|
||||
}
|
@ -1,15 +0,0 @@
|
||||
{
|
||||
"open": false,
|
||||
"logLevel": "silent",
|
||||
"port": 8080,
|
||||
"server": {
|
||||
"baseDir": ".",
|
||||
"routes": {
|
||||
"/dist": "dist",
|
||||
"/node_modules": "node_modules"
|
||||
},
|
||||
"middleware": {
|
||||
"0": null
|
||||
}
|
||||
}
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
exports.config = {
|
||||
specs: [
|
||||
'../dist/e2e/*.e2e-spec.js'
|
||||
],
|
||||
capabilities: {
|
||||
browserName: 'chrome',
|
||||
chromeOptions: {
|
||||
args: ['--no-sandbox'],
|
||||
binary: process.env.CHROME_BIN,
|
||||
}
|
||||
},
|
||||
directConnect: true,
|
||||
baseUrl: 'http://localhost:8080/',
|
||||
framework: 'jasmine',
|
||||
useAllAngular2AppRoots: true
|
||||
};
|
@ -1,7 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/e2e",
|
||||
"types": ["jasmine"],
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title></title>
|
||||
</head>
|
||||
<body>
|
||||
<app-root></app-root>
|
||||
<script src="/node_modules/zone.js/dist/zone.js"></script>
|
||||
<script src="/node_modules/reflect-metadata/Reflect.js"></script>
|
||||
<script src="/node_modules/systemjs/dist/system.js"></script>
|
||||
<script src="/dist/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
@ -1,44 +0,0 @@
|
||||
{
|
||||
"name": "dynamic-compiler",
|
||||
"version": "0.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "npm run clean && npm run ngc && npm run rollup && npm run rollup:lazy && npm run es5 && npm run es5:lazy",
|
||||
"clean": "rm -rf dist",
|
||||
"es5": "tsc --target es5 --skipLibCheck --allowJs dist/bundle.es2015.js --out dist/bundle.js",
|
||||
"es5:lazy": "tsc --target es5 --skipLibCheck --allowJs dist/lazy.bundle.es2015.js --out dist/lazy.bundle.js",
|
||||
"ngc": "ngc -p tsconfig.json",
|
||||
"rollup": "rollup -f iife -c rollup.config.js -o dist/bundle.es2015.js",
|
||||
"rollup:lazy": "rollup -f cjs -c rollup.lazy.config.js -o dist/lazy.bundle.es2015.js",
|
||||
"postinstall": "webdriver-manager update --gecko false",
|
||||
"preprotractor": "tsc -p e2e",
|
||||
"protractor": "protractor e2e/protractor.config.js",
|
||||
"serve": "lite-server -c e2e/browser.config.json",
|
||||
"test": "npm run build && concurrently \"yarn run serve\" \"yarn run protractor\" --kill-others --success first"
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jasmine": "file:../../node_modules/@types/jasmine",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"concurrently": "3.4.0",
|
||||
"lite-server": "2.2.2",
|
||||
"protractor": "file:../../node_modules/protractor",
|
||||
"rollup": "file:../../node_modules/rollup",
|
||||
"rollup-plugin-commonjs": "file:../../node_modules/rollup-plugin-commonjs",
|
||||
"rollup-plugin-node-resolve": "file:../../node_modules/rollup-plugin-node-resolve",
|
||||
"typescript": "file:../../node_modules/typescript"
|
||||
},
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
||||
"@angular/common": "file:../../dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"core-js": "file:../../node_modules/core-js",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
"systemjs": "file:../../node_modules/systemjs",
|
||||
"zone.js": "file:../../node_modules/zone.js"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
|
||||
export default {
|
||||
entry: 'dist/src/main.js',
|
||||
sourceMap: true,
|
||||
treeshake: true,
|
||||
moduleName: 'main',
|
||||
plugins: [
|
||||
commonjs({
|
||||
include: 'node_modules/**'
|
||||
}),
|
||||
nodeResolve({
|
||||
jsnext: true, main: true, module: true
|
||||
})
|
||||
]
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
import commonjs from 'rollup-plugin-commonjs';
|
||||
|
||||
// a real app should make a common bundle for libraries instead of bundling them
|
||||
// in both the main module & the lazy module, but we don't care about size here
|
||||
export default {
|
||||
entry: 'dist/src/lazy.module.js',
|
||||
sourceMap: true,
|
||||
treeshake: true,
|
||||
moduleName: 'lazy',
|
||||
plugins: [
|
||||
commonjs({
|
||||
include: 'node_modules/**'
|
||||
}),
|
||||
nodeResolve({
|
||||
jsnext: true, main: true, module: true
|
||||
})
|
||||
]
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
import {AfterViewInit, Compiler, Component, ViewChild, ViewContainerRef} from '@angular/core';
|
||||
|
||||
declare var System: any;
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
template: `
|
||||
<h1>Hello world!</h1>
|
||||
<div #vc></div>
|
||||
`,
|
||||
})
|
||||
export class AppComponent implements AfterViewInit {
|
||||
@ViewChild('vc', {read: ViewContainerRef}) container: ViewContainerRef;
|
||||
|
||||
constructor(private compiler: Compiler) {
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
System.import('./dist/lazy.bundle.js').then((module: any) => {
|
||||
this.compiler.compileModuleAndAllComponentsAsync(module.LazyModule)
|
||||
.then((compiled) => {
|
||||
const factory = compiled.componentFactories[0];
|
||||
this.container.createComponent(factory);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
import {Compiler, COMPILER_OPTIONS, CompilerFactory, NgModule} from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import {JitCompilerFactory} from '@angular/platform-browser-dynamic';
|
||||
|
||||
import { AppComponent } from './app.component';
|
||||
|
||||
export function createCompiler(compilerFactory: CompilerFactory) {
|
||||
return compilerFactory.createCompiler();
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
imports: [BrowserModule],
|
||||
bootstrap: [AppComponent],
|
||||
declarations: [AppComponent],
|
||||
providers: [
|
||||
{provide: COMPILER_OPTIONS, useValue: {}, multi: true},
|
||||
{provide: CompilerFactory, useClass: JitCompilerFactory, deps: [COMPILER_OPTIONS]},
|
||||
{provide: Compiler, useFactory: createCompiler, deps: [CompilerFactory]}
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
@ -1,17 +0,0 @@
|
||||
import {NgModule} from "@angular/core";
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'lazy-component',
|
||||
template: 'Lazy-loaded component!'
|
||||
})
|
||||
export class LazyComponent {
|
||||
constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [LazyComponent]
|
||||
})
|
||||
export class LazyModule {
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
import { enableProdMode } from '@angular/core';
|
||||
import { platformBrowser } from '@angular/platform-browser';
|
||||
|
||||
import { AppModuleNgFactory } from './app.module.ngfactory';
|
||||
|
||||
enableProdMode();
|
||||
|
||||
platformBrowser().bootstrapModuleFactory(AppModuleNgFactory);
|
@ -1,28 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es2015",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"declaration": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"lib": ["es6", "es2015", "dom"],
|
||||
"sourceMap": true,
|
||||
"pretty": true,
|
||||
"allowUnreachableCode": false,
|
||||
"allowUnusedLabels": false,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noImplicitUseStrict": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"outDir": "./dist",
|
||||
"types": [
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/main.ts",
|
||||
"src/lazy.module.ts"
|
||||
]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-integration",
|
||||
"description": "Assert that users with TypeScript 2.4 can type-check an Angular application",
|
||||
"description": "Assert that users with TypeScript 2.2 can type-check an Angular application",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@ -18,7 +18,7 @@
|
||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
"typescript": "2.4.x",
|
||||
"typescript": "file:../../node_modules/typescript",
|
||||
"zone.js": "file:../../node_modules/zone.js"
|
||||
},
|
||||
"scripts": {
|
||||
|
@ -1,41 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright Google Inc. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import * as compiler from '@angular/compiler';
|
||||
import * as compilerTesting from '@angular/compiler/testing';
|
||||
import * as core from '@angular/core';
|
||||
import * as coreTesting from '@angular/core/testing';
|
||||
import * as forms from '@angular/forms';
|
||||
import * as http from '@angular/http';
|
||||
import * as httpTesting from '@angular/http/testing';
|
||||
import * as platformBrowserDynamic from '@angular/platform-browser-dynamic';
|
||||
import * as platformBrowser from '@angular/platform-browser';
|
||||
import * as platformBrowserTesting from '@angular/platform-browser/testing';
|
||||
import * as platformServer from '@angular/platform-server';
|
||||
import * as platformServerTesting from '@angular/platform-server/testing';
|
||||
import * as router from '@angular/router';
|
||||
import * as routerTesting from '@angular/router/testing';
|
||||
import * as upgrade from '@angular/upgrade';
|
||||
|
||||
export default {
|
||||
compiler,
|
||||
compilerTesting,
|
||||
core,
|
||||
coreTesting,
|
||||
forms,
|
||||
http,
|
||||
httpTesting,
|
||||
platformBrowser,
|
||||
platformBrowserTesting,
|
||||
platformBrowserDynamic,
|
||||
platformServer,
|
||||
platformServerTesting,
|
||||
router,
|
||||
routerTesting,
|
||||
upgrade
|
||||
};
|
@ -1,27 +0,0 @@
|
||||
{
|
||||
"name": "angular-integration",
|
||||
"description": "Assert that users with TypeScript 2.5 can type-check an Angular application",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@angular/animations": "file:../../dist/packages-dist/animations",
|
||||
"@angular/common": "file:../../dist/packages-dist/common",
|
||||
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||
"@angular/core": "file:../../dist/packages-dist/core",
|
||||
"@angular/forms": "file:../../dist/packages-dist/forms",
|
||||
"@angular/http": "file:../../dist/packages-dist/http",
|
||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||
"@angular/router": "file:../../dist/packages-dist/router",
|
||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||
"@types/jasmine": "2.5.41",
|
||||
"rxjs": "file:../../node_modules/rxjs",
|
||||
"typescript": "2.5.x",
|
||||
"zone.js": "file:../../node_modules/zone.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tsc"
|
||||
}
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node",
|
||||
"outDir": "../../dist/typings_test_ts25/",
|
||||
"rootDir": ".",
|
||||
"target": "es5",
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection",
|
||||
"es2015.iterable",
|
||||
"es2015.promise"
|
||||
],
|
||||
"types": [],
|
||||
"strictNullChecks": true
|
||||
},
|
||||
"files": [
|
||||
"include-all.ts",
|
||||
"node_modules/@types/jasmine/index.d.ts"
|
||||
]
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "5.1.0-beta.2",
|
||||
"version": "5.0.5",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular - a web framework for modern web apps",
|
||||
@ -32,7 +32,7 @@
|
||||
"fsevents": "1.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@bazel/typescript": "0.3.1",
|
||||
"@bazel/typescript": "0.2.x",
|
||||
"@types/angularjs": "1.5.14-alpha",
|
||||
"@types/base64-js": "1.2.5",
|
||||
"@types/chokidar": "1.7.3",
|
||||
@ -95,10 +95,10 @@
|
||||
"source-map-support": "0.4.18",
|
||||
"systemjs": "0.18.10",
|
||||
"ts-api-guardian": "0.2.2",
|
||||
"tsickle": "0.25.5",
|
||||
"tsickle": "0.24.x",
|
||||
"tslint": "5.7.0",
|
||||
"tslint-eslint-rules": "4.1.1",
|
||||
"typescript": "2.5.x",
|
||||
"typescript": "2.4.2",
|
||||
"uglify-js": "2.8.29",
|
||||
"universal-analytics": "0.4.15",
|
||||
"vlq": "0.2.2",
|
||||
|
@ -8,7 +8,7 @@
|
||||
import {AnimationMetadata, AnimationMetadataType, AnimationOptions, ɵStyleData} from '@angular/animations';
|
||||
|
||||
import {AnimationDriver} from '../render/animation_driver';
|
||||
import {normalizeStyles} from '../util';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME, normalizeStyles} from '../util';
|
||||
|
||||
import {Ast} from './animation_ast';
|
||||
import {buildAnimationAst} from './animation_ast_builder';
|
||||
@ -39,7 +39,8 @@ export class Animation {
|
||||
const errors: any = [];
|
||||
subInstructions = subInstructions || new ElementInstructionMap();
|
||||
const result = buildAnimationTimelines(
|
||||
this._driver, element, this._animationAst, start, dest, options, subInstructions, errors);
|
||||
this._driver, element, this._animationAst, ENTER_CLASSNAME, LEAVE_CLASSNAME, start, dest,
|
||||
options, subInstructions, errors);
|
||||
if (errors.length) {
|
||||
const errorMessage = `animation building failed:\n${errors.join("\n")}`;
|
||||
throw new Error(errorMessage);
|
||||
|
@ -60,10 +60,6 @@ export function buildAnimationAst(
|
||||
return new AnimationAstBuilderVisitor(driver).build(metadata, errors);
|
||||
}
|
||||
|
||||
const LEAVE_TOKEN = ':leave';
|
||||
const LEAVE_TOKEN_REGEX = new RegExp(LEAVE_TOKEN, 'g');
|
||||
const ENTER_TOKEN = ':enter';
|
||||
const ENTER_TOKEN_REGEX = new RegExp(ENTER_TOKEN, 'g');
|
||||
const ROOT_SELECTOR = '';
|
||||
|
||||
export class AnimationAstBuilderVisitor implements AnimationDslVisitor {
|
||||
@ -478,9 +474,8 @@ function normalizeSelector(selector: string): [string, boolean] {
|
||||
selector = selector.replace(SELF_TOKEN_REGEX, '');
|
||||
}
|
||||
|
||||
selector = selector.replace(ENTER_TOKEN_REGEX, ENTER_SELECTOR)
|
||||
.replace(LEAVE_TOKEN_REGEX, LEAVE_SELECTOR)
|
||||
.replace(/@\*/g, NG_TRIGGER_SELECTOR)
|
||||
// the :enter and :leave selectors are filled in at runtime during timeline building
|
||||
selector = selector.replace(/@\*/g, NG_TRIGGER_SELECTOR)
|
||||
.replace(/@\w+/g, match => NG_TRIGGER_SELECTOR + '-' + match.substr(1))
|
||||
.replace(/:animating/g, NG_ANIMATING_SELECTOR);
|
||||
|
||||
|
@ -15,6 +15,10 @@ import {AnimationTimelineInstruction, createTimelineInstruction} from './animati
|
||||
import {ElementInstructionMap} from './element_instruction_map';
|
||||
|
||||
const ONE_FRAME_IN_MILLISECONDS = 1;
|
||||
const ENTER_TOKEN = ':enter';
|
||||
const ENTER_TOKEN_REGEX = new RegExp(ENTER_TOKEN, 'g');
|
||||
const LEAVE_TOKEN = ':leave';
|
||||
const LEAVE_TOKEN_REGEX = new RegExp(LEAVE_TOKEN, 'g');
|
||||
|
||||
/*
|
||||
* The code within this file aims to generate web-animations-compatible keyframes from Angular's
|
||||
@ -102,19 +106,23 @@ const ONE_FRAME_IN_MILLISECONDS = 1;
|
||||
*/
|
||||
export function buildAnimationTimelines(
|
||||
driver: AnimationDriver, rootElement: any, ast: Ast<AnimationMetadataType>,
|
||||
startingStyles: ɵStyleData = {}, finalStyles: ɵStyleData = {}, options: AnimationOptions,
|
||||
enterClassName: string, leaveClassName: string, startingStyles: ɵStyleData = {},
|
||||
finalStyles: ɵStyleData = {}, options: AnimationOptions,
|
||||
subInstructions?: ElementInstructionMap, errors: any[] = []): AnimationTimelineInstruction[] {
|
||||
return new AnimationTimelineBuilderVisitor().buildKeyframes(
|
||||
driver, rootElement, ast, startingStyles, finalStyles, options, subInstructions, errors);
|
||||
driver, rootElement, ast, enterClassName, leaveClassName, startingStyles, finalStyles,
|
||||
options, subInstructions, errors);
|
||||
}
|
||||
|
||||
export class AnimationTimelineBuilderVisitor implements AstVisitor {
|
||||
buildKeyframes(
|
||||
driver: AnimationDriver, rootElement: any, ast: Ast<AnimationMetadataType>,
|
||||
startingStyles: ɵStyleData, finalStyles: ɵStyleData, options: AnimationOptions,
|
||||
subInstructions?: ElementInstructionMap, errors: any[] = []): AnimationTimelineInstruction[] {
|
||||
enterClassName: string, leaveClassName: string, startingStyles: ɵStyleData,
|
||||
finalStyles: ɵStyleData, options: AnimationOptions, subInstructions?: ElementInstructionMap,
|
||||
errors: any[] = []): AnimationTimelineInstruction[] {
|
||||
subInstructions = subInstructions || new ElementInstructionMap();
|
||||
const context = new AnimationTimelineContext(driver, rootElement, subInstructions, errors, []);
|
||||
const context = new AnimationTimelineContext(
|
||||
driver, rootElement, subInstructions, enterClassName, leaveClassName, errors, []);
|
||||
context.options = options;
|
||||
context.currentTimeline.setStyles([startingStyles], null, context.errors, options);
|
||||
|
||||
@ -445,8 +453,9 @@ export class AnimationTimelineContext {
|
||||
|
||||
constructor(
|
||||
private _driver: AnimationDriver, public element: any,
|
||||
public subInstructions: ElementInstructionMap, public errors: any[],
|
||||
public timelines: TimelineBuilder[], initialTimeline?: TimelineBuilder) {
|
||||
public subInstructions: ElementInstructionMap, private _enterClassName: string,
|
||||
private _leaveClassName: string, public errors: any[], public timelines: TimelineBuilder[],
|
||||
initialTimeline?: TimelineBuilder) {
|
||||
this.currentTimeline = initialTimeline || new TimelineBuilder(this._driver, element, 0);
|
||||
timelines.push(this.currentTimeline);
|
||||
}
|
||||
@ -499,8 +508,8 @@ export class AnimationTimelineContext {
|
||||
AnimationTimelineContext {
|
||||
const target = element || this.element;
|
||||
const context = new AnimationTimelineContext(
|
||||
this._driver, target, this.subInstructions, this.errors, this.timelines,
|
||||
this.currentTimeline.fork(target, newTime || 0));
|
||||
this._driver, target, this.subInstructions, this._enterClassName, this._leaveClassName,
|
||||
this.errors, this.timelines, this.currentTimeline.fork(target, newTime || 0));
|
||||
context.previousNode = this.previousNode;
|
||||
context.currentAnimateTimings = this.currentAnimateTimings;
|
||||
|
||||
@ -555,6 +564,8 @@ export class AnimationTimelineContext {
|
||||
results.push(this.element);
|
||||
}
|
||||
if (selector.length > 0) { // if :self is only used then the selector is empty
|
||||
selector = selector.replace(ENTER_TOKEN_REGEX, '.' + this._enterClassName);
|
||||
selector = selector.replace(LEAVE_TOKEN_REGEX, '.' + this._leaveClassName);
|
||||
const multi = limit != 1;
|
||||
let elements = this._driver.query(this.element, selector, multi);
|
||||
if (limit !== 0) {
|
||||
|
@ -37,7 +37,8 @@ export class AnimationTransitionFactory {
|
||||
|
||||
build(
|
||||
driver: AnimationDriver, element: any, currentState: any, nextState: any,
|
||||
currentOptions?: AnimationOptions, nextOptions?: AnimationOptions,
|
||||
enterClassName: string, leaveClassName: string, currentOptions?: AnimationOptions,
|
||||
nextOptions?: AnimationOptions,
|
||||
subInstructions?: ElementInstructionMap): AnimationTransitionInstruction {
|
||||
const errors: any[] = [];
|
||||
|
||||
@ -55,8 +56,8 @@ export class AnimationTransitionFactory {
|
||||
const animationOptions = {params: {...transitionAnimationParams, ...nextAnimationParams}};
|
||||
|
||||
const timelines = buildAnimationTimelines(
|
||||
driver, element, this.ast.animation, currentStateStyles, nextStateStyles, animationOptions,
|
||||
subInstructions, errors);
|
||||
driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles,
|
||||
nextStateStyles, animationOptions, subInstructions, errors);
|
||||
|
||||
if (errors.length) {
|
||||
return createTransitionInstruction(
|
||||
|
@ -13,6 +13,7 @@ import {buildAnimationTimelines} from '../dsl/animation_timeline_builder';
|
||||
import {AnimationTimelineInstruction} from '../dsl/animation_timeline_instruction';
|
||||
import {ElementInstructionMap} from '../dsl/element_instruction_map';
|
||||
import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '../util';
|
||||
|
||||
import {AnimationDriver} from './animation_driver';
|
||||
import {getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared';
|
||||
@ -55,7 +56,8 @@ export class TimelineAnimationEngine {
|
||||
|
||||
if (ast) {
|
||||
instructions = buildAnimationTimelines(
|
||||
this._driver, element, ast, {}, {}, options, EMPTY_INSTRUCTION_MAP, errors);
|
||||
this._driver, element, ast, ENTER_CLASSNAME, LEAVE_CLASSNAME, {}, {}, options,
|
||||
EMPTY_INSTRUCTION_MAP, errors);
|
||||
instructions.forEach(inst => {
|
||||
const styles = getOrSetAsInMap(autoStylesMap, inst.element, {});
|
||||
inst.postStyleProps.forEach(prop => styles[prop] = null);
|
||||
|
@ -13,7 +13,7 @@ import {AnimationTransitionInstruction} from '../dsl/animation_transition_instru
|
||||
import {AnimationTrigger} from '../dsl/animation_trigger';
|
||||
import {ElementInstructionMap} from '../dsl/element_instruction_map';
|
||||
import {AnimationStyleNormalizer} from '../dsl/style_normalization/animation_style_normalizer';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, copyObj, eraseStyles, setStyles} from '../util';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME, NG_ANIMATING_CLASSNAME, NG_ANIMATING_SELECTOR, NG_TRIGGER_CLASSNAME, NG_TRIGGER_SELECTOR, copyObj, eraseStyles, iteratorToArray, setStyles} from '../util';
|
||||
|
||||
import {AnimationDriver} from './animation_driver';
|
||||
import {getBodyNode, getOrSetAsInMap, listenOnPlayer, makeAnimationEvent, normalizeKeyframes, optimizeGroupPlayer} from './shared';
|
||||
@ -22,6 +22,8 @@ const QUEUED_CLASSNAME = 'ng-animate-queued';
|
||||
const QUEUED_SELECTOR = '.ng-animate-queued';
|
||||
const DISABLED_CLASSNAME = 'ng-animate-disabled';
|
||||
const DISABLED_SELECTOR = '.ng-animate-disabled';
|
||||
const STAR_CLASSNAME = 'ng-star-inserted';
|
||||
const STAR_SELECTOR = '.ng-star-inserted';
|
||||
|
||||
const EMPTY_PLAYER_ARRAY: TransitionAnimationPlayer[] = [];
|
||||
const NULL_REMOVAL_STATE: ElementAnimationState = {
|
||||
@ -714,10 +716,12 @@ export class TransitionAnimationEngine {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
private _buildInstruction(entry: QueueInstruction, subTimelines: ElementInstructionMap) {
|
||||
private _buildInstruction(
|
||||
entry: QueueInstruction, subTimelines: ElementInstructionMap, enterClassName: string,
|
||||
leaveClassName: string) {
|
||||
return entry.transition.build(
|
||||
this.driver, entry.element, entry.fromState.value, entry.toState.value,
|
||||
entry.fromState.options, entry.toState.options, subTimelines);
|
||||
this.driver, entry.element, entry.fromState.value, entry.toState.value, enterClassName,
|
||||
leaveClassName, entry.fromState.options, entry.toState.options, subTimelines);
|
||||
}
|
||||
|
||||
destroyInnerAnimations(containerElement: any) {
|
||||
@ -798,6 +802,13 @@ export class TransitionAnimationEngine {
|
||||
this.newHostElements.clear();
|
||||
}
|
||||
|
||||
if (this.totalAnimations && this.collectedEnterElements.length) {
|
||||
for (let i = 0; i < this.collectedEnterElements.length; i++) {
|
||||
const elm = this.collectedEnterElements[i];
|
||||
addClass(elm, STAR_CLASSNAME);
|
||||
}
|
||||
}
|
||||
|
||||
if (this._namespaceList.length &&
|
||||
(this.totalQueuedPlayers || this.collectedLeaveElements.length)) {
|
||||
const cleanupFns: Function[] = [];
|
||||
@ -862,37 +873,57 @@ export class TransitionAnimationEngine {
|
||||
});
|
||||
|
||||
const bodyNode = getBodyNode();
|
||||
const allEnterNodes: any[] = this.collectedEnterElements.length ?
|
||||
this.collectedEnterElements.filter(createIsRootFilterFn(this.collectedEnterElements)) :
|
||||
[];
|
||||
const allTriggerElements = Array.from(this.statesByElement.keys());
|
||||
const enterNodeMap = buildRootMap(allTriggerElements, this.collectedEnterElements);
|
||||
|
||||
// this must occur before the instructions are built below such that
|
||||
// the :enter queries match the elements (since the timeline queries
|
||||
// are fired during instruction building).
|
||||
for (let i = 0; i < allEnterNodes.length; i++) {
|
||||
addClass(allEnterNodes[i], ENTER_CLASSNAME);
|
||||
}
|
||||
const enterNodeMapIds = new Map<any, string>();
|
||||
let i = 0;
|
||||
enterNodeMap.forEach((nodes, root) => {
|
||||
const className = ENTER_CLASSNAME + i++;
|
||||
enterNodeMapIds.set(root, className);
|
||||
nodes.forEach(node => addClass(node, className));
|
||||
});
|
||||
|
||||
const allLeaveNodes: any[] = [];
|
||||
const mergedLeaveNodes = new Set<any>();
|
||||
const leaveNodesWithoutAnimations = new Set<any>();
|
||||
for (let i = 0; i < this.collectedLeaveElements.length; i++) {
|
||||
const element = this.collectedLeaveElements[i];
|
||||
const details = element[REMOVAL_FLAG] as ElementAnimationState;
|
||||
if (details && details.setForRemoval) {
|
||||
addClass(element, LEAVE_CLASSNAME);
|
||||
allLeaveNodes.push(element);
|
||||
if (!details.hasAnimation) {
|
||||
mergedLeaveNodes.add(element);
|
||||
if (details.hasAnimation) {
|
||||
this.driver.query(element, STAR_SELECTOR, true).forEach(elm => mergedLeaveNodes.add(elm));
|
||||
} else {
|
||||
leaveNodesWithoutAnimations.add(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const leaveNodeMapIds = new Map<any, string>();
|
||||
const leaveNodeMap = buildRootMap(allTriggerElements, Array.from(mergedLeaveNodes));
|
||||
leaveNodeMap.forEach((nodes, root) => {
|
||||
const className = LEAVE_CLASSNAME + i++;
|
||||
leaveNodeMapIds.set(root, className);
|
||||
nodes.forEach(node => addClass(node, className));
|
||||
});
|
||||
|
||||
cleanupFns.push(() => {
|
||||
allEnterNodes.forEach(element => removeClass(element, ENTER_CLASSNAME));
|
||||
allLeaveNodes.forEach(element => {
|
||||
removeClass(element, LEAVE_CLASSNAME);
|
||||
this.processLeaveNode(element);
|
||||
enterNodeMap.forEach((nodes, root) => {
|
||||
const className = enterNodeMapIds.get(root) !;
|
||||
nodes.forEach(node => removeClass(node, className));
|
||||
});
|
||||
|
||||
leaveNodeMap.forEach((nodes, root) => {
|
||||
const className = leaveNodeMapIds.get(root) !;
|
||||
nodes.forEach(node => removeClass(node, className));
|
||||
});
|
||||
|
||||
allLeaveNodes.forEach(element => { this.processLeaveNode(element); });
|
||||
});
|
||||
|
||||
const allPlayers: TransitionAnimationPlayer[] = [];
|
||||
@ -909,7 +940,10 @@ export class TransitionAnimationEngine {
|
||||
return;
|
||||
}
|
||||
|
||||
const instruction = this._buildInstruction(entry, subTimelines) !;
|
||||
const leaveClassName = leaveNodeMapIds.get(element) !;
|
||||
const enterClassName = enterNodeMapIds.get(element) !;
|
||||
const instruction =
|
||||
this._buildInstruction(entry, subTimelines, enterClassName, leaveClassName) !;
|
||||
if (instruction.errors && instruction.errors.length) {
|
||||
erroneousTransitions.push(instruction);
|
||||
return;
|
||||
@ -973,18 +1007,6 @@ export class TransitionAnimationEngine {
|
||||
this.reportError(errors);
|
||||
}
|
||||
|
||||
// these can only be detected here since we have a map of all the elements
|
||||
// that have animations attached to them... We use a set here in the event
|
||||
// multiple enter captures on the same element were caught in different
|
||||
// renderer namespaces (e.g. when a @trigger was on a host binding that had *ngIf)
|
||||
const enterNodesWithoutAnimations = new Set<any>();
|
||||
for (let i = 0; i < allEnterNodes.length; i++) {
|
||||
const element = allEnterNodes[i];
|
||||
if (!subTimelines.has(element)) {
|
||||
enterNodesWithoutAnimations.add(element);
|
||||
}
|
||||
}
|
||||
|
||||
const allPreviousPlayersMap = new Map<any, TransitionAnimationPlayer[]>();
|
||||
// this map works to tell which element in the DOM tree is contained by
|
||||
// which animation. Further down below this map will get populated once
|
||||
@ -1022,8 +1044,9 @@ export class TransitionAnimationEngine {
|
||||
});
|
||||
|
||||
// POST STAGE: fill the * styles
|
||||
const [postStylesMap, allLeaveQueriedNodes] = cloakAndComputeStyles(
|
||||
this.driver, leaveNodesWithoutAnimations, allPostStyleElements, AUTO_STYLE);
|
||||
const postStylesMap = new Map<any, ɵStyleData>();
|
||||
const allLeaveQueriedNodes = cloakAndComputeStyles(
|
||||
postStylesMap, this.driver, leaveNodesWithoutAnimations, allPostStyleElements, AUTO_STYLE);
|
||||
|
||||
allLeaveQueriedNodes.forEach(node => {
|
||||
if (replacePostStylesAsPre(node, allPreStyleElements, allPostStyleElements)) {
|
||||
@ -1032,10 +1055,11 @@ export class TransitionAnimationEngine {
|
||||
});
|
||||
|
||||
// PRE STAGE: fill the ! styles
|
||||
const [preStylesMap] = allPreStyleElements.size ?
|
||||
cloakAndComputeStyles(
|
||||
this.driver, enterNodesWithoutAnimations, allPreStyleElements, PRE_STYLE) :
|
||||
[new Map<any, ɵStyleData>()];
|
||||
const preStylesMap = new Map<any, ɵStyleData>();
|
||||
enterNodeMap.forEach((nodes, root) => {
|
||||
cloakAndComputeStyles(
|
||||
preStylesMap, this.driver, new Set(nodes), allPreStyleElements, PRE_STYLE);
|
||||
});
|
||||
|
||||
replaceNodes.forEach(node => {
|
||||
const post = postStylesMap.get(node);
|
||||
@ -1505,12 +1529,11 @@ function cloakElement(element: any, value?: string) {
|
||||
}
|
||||
|
||||
function cloakAndComputeStyles(
|
||||
driver: AnimationDriver, elements: Set<any>, elementPropsMap: Map<any, Set<string>>,
|
||||
defaultStyle: string): [Map<any, ɵStyleData>, any[]] {
|
||||
valuesMap: Map<any, ɵStyleData>, driver: AnimationDriver, elements: Set<any>,
|
||||
elementPropsMap: Map<any, Set<string>>, defaultStyle: string): any[] {
|
||||
const cloakVals: string[] = [];
|
||||
elements.forEach(element => cloakVals.push(cloakElement(element)));
|
||||
|
||||
const valuesMap = new Map<any, ɵStyleData>();
|
||||
const failedElements: any[] = [];
|
||||
|
||||
elementPropsMap.forEach((props: Set<string>, element: any) => {
|
||||
@ -1532,39 +1555,57 @@ function cloakAndComputeStyles(
|
||||
// an index value for the closure (but instead just the value)
|
||||
let i = 0;
|
||||
elements.forEach(element => cloakElement(element, cloakVals[i++]));
|
||||
return [valuesMap, failedElements];
|
||||
|
||||
return failedElements;
|
||||
}
|
||||
|
||||
/*
|
||||
Since the Angular renderer code will return a collection of inserted
|
||||
nodes in all areas of a DOM tree, it's up to this algorithm to figure
|
||||
out which nodes are roots.
|
||||
out which nodes are roots for each animation @trigger.
|
||||
|
||||
By placing all nodes into a set and traversing upwards to the edge,
|
||||
the recursive code can figure out if a clean path from the DOM node
|
||||
to the edge container is clear. If no other node is detected in the
|
||||
set then it is a root element.
|
||||
|
||||
This algorithm also keeps track of all nodes along the path so that
|
||||
if other sibling nodes are also tracked then the lookup process can
|
||||
skip a lot of steps in between and avoid traversing the entire tree
|
||||
multiple times to the edge.
|
||||
By placing each inserted node into a Set and traversing upwards, it
|
||||
is possible to find the @trigger elements and well any direct *star
|
||||
insertion nodes, if a @trigger root is found then the enter element
|
||||
is placed into the Map[@trigger] spot.
|
||||
*/
|
||||
function createIsRootFilterFn(nodes: any): (node: any) => boolean {
|
||||
function buildRootMap(roots: any[], nodes: any[]): Map<any, any[]> {
|
||||
const rootMap = new Map<any, any[]>();
|
||||
roots.forEach(root => rootMap.set(root, []));
|
||||
|
||||
if (nodes.length == 0) return rootMap;
|
||||
|
||||
const NULL_NODE = 1;
|
||||
const nodeSet = new Set(nodes);
|
||||
const knownRootContainer = new Set();
|
||||
let isRoot: (node: any) => boolean;
|
||||
isRoot = node => {
|
||||
if (!node) return true;
|
||||
if (nodeSet.has(node.parentNode)) return false;
|
||||
if (knownRootContainer.has(node.parentNode)) return true;
|
||||
if (isRoot(node.parentNode)) {
|
||||
knownRootContainer.add(node);
|
||||
return true;
|
||||
const localRootMap = new Map<any, any>();
|
||||
|
||||
function getRoot(node: any): any {
|
||||
if (!node) return NULL_NODE;
|
||||
|
||||
let root = localRootMap.get(node);
|
||||
if (root) return root;
|
||||
|
||||
const parent = node.parentNode;
|
||||
if (rootMap.has(parent)) { // ngIf inside @trigger
|
||||
root = parent;
|
||||
} else if (nodeSet.has(parent)) { // ngIf inside ngIf
|
||||
root = NULL_NODE;
|
||||
} else { // recurse upwards
|
||||
root = getRoot(parent);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return isRoot;
|
||||
|
||||
localRootMap.set(node, root);
|
||||
return root;
|
||||
}
|
||||
|
||||
nodes.forEach(node => {
|
||||
const root = getRoot(node);
|
||||
if (root !== NULL_NODE) {
|
||||
rootMap.get(root) !.push(node);
|
||||
}
|
||||
});
|
||||
|
||||
return rootMap;
|
||||
}
|
||||
|
||||
const CLASSES_CACHE_KEY = '$$classes';
|
||||
|
@ -10,6 +10,7 @@ import {AnimationOptions, animate, state, style, transition} from '@angular/anim
|
||||
import {AnimationTransitionInstruction} from '@angular/animations/browser/src/dsl/animation_transition_instruction';
|
||||
import {AnimationTrigger} from '@angular/animations/browser/src/dsl/animation_trigger';
|
||||
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '../../src/util';
|
||||
import {MockAnimationDriver} from '../../testing';
|
||||
import {makeTrigger} from '../shared';
|
||||
|
||||
@ -228,7 +229,9 @@ function buildTransition(
|
||||
const trans = trigger.matchTransition(fromState, toState) !;
|
||||
if (trans) {
|
||||
const driver = new MockAnimationDriver();
|
||||
return trans.build(driver, element, fromState, toState, fromOptions, toOptions) !;
|
||||
return trans.build(
|
||||
driver, element, fromState, toState, ENTER_CLASSNAME, LEAVE_CLASSNAME, fromOptions,
|
||||
toOptions) !;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -62,8 +62,8 @@ export class NoopAnimationPlayer implements AnimationPlayer {
|
||||
init(): void {}
|
||||
play(): void {
|
||||
if (!this.hasStarted()) {
|
||||
this._onStart();
|
||||
this.triggerMicrotask();
|
||||
this._onStart();
|
||||
}
|
||||
this._started = true;
|
||||
}
|
||||
|
@ -6,9 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {fakeAsync} from '@angular/core/testing';
|
||||
|
||||
import {flushMicrotasks} from '../../core/testing/src/fake_async';
|
||||
import {NoopAnimationPlayer} from '../src/players/animation_player';
|
||||
import {scheduleMicroTask} from '../src/util';
|
||||
|
||||
export function main() {
|
||||
describe('NoopAnimationPlayer', function() {
|
||||
@ -61,21 +61,6 @@ export function main() {
|
||||
player.finish();
|
||||
expect(log).toEqual(['started', 'done']);
|
||||
|
||||
flushMicrotasks();
|
||||
expect(log).toEqual(['started', 'done']);
|
||||
}));
|
||||
|
||||
it('should fire off start callbacks before triggering the finish callback', fakeAsync(() => {
|
||||
const log: string[] = [];
|
||||
|
||||
const player = new NoopAnimationPlayer();
|
||||
player.onStart(() => { scheduleMicroTask(() => log.push('started')); });
|
||||
player.onDone(() => log.push('done'));
|
||||
expect(log).toEqual([]);
|
||||
|
||||
player.play();
|
||||
expect(log).toEqual([]);
|
||||
|
||||
flushMicrotasks();
|
||||
expect(log).toEqual(['started', 'done']);
|
||||
}));
|
||||
|
@ -6,10 +6,10 @@
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@angular/compiler-cli": "0.0.0-PLACEHOLDER",
|
||||
"typescript": ">=2.4.2 <2.6"
|
||||
"typescript": ">=2.4.2 <2.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@bazel/typescript": "0.3.x",
|
||||
"@bazel/typescript": "0.2.x",
|
||||
"@types/node": "6.0.84"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -5,7 +5,8 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// TODO(tbosch): figure out why we need this as it breaks node code within ngc-wrapped
|
||||
/// <reference types="node" />
|
||||
import * as ng from '@angular/compiler-cli';
|
||||
import {BazelOptions, CachedFileLoader, CompilerHost, FileCache, FileLoader, UncachedFileLoader, constructManifest, debug, fixUmdModuleDeclarations, parseTsconfig, runAsWorker, runWorkerLoop} from '@bazel/typescript';
|
||||
import * as fs from 'fs';
|
||||
|
@ -64,10 +64,6 @@ export function createTsConfig(options: TsConfigOptions) {
|
||||
'es5Mode': true,
|
||||
'manifest': createManifestPath(options),
|
||||
'compilationTargetSrc': options.compilationTargetSrc,
|
||||
// Override this property from the real tsconfig we read
|
||||
// Because we ask for :empty_tsconfig.json, we get the ES6 version which
|
||||
// expects to write externs, yet that doesn't work under this fixture.
|
||||
'tsickleExternsPath': '',
|
||||
},
|
||||
'files': options.files,
|
||||
'angularCompilerOptions': {
|
||||
|
@ -81,9 +81,7 @@ export class Runner {
|
||||
{provide: WebDriverAdapter, useValue: adapter}
|
||||
]);
|
||||
|
||||
// TODO: With TypeScript 2.5 injector.get does not infer correctly the
|
||||
// return type. Remove 'any' and investigate the issue.
|
||||
const sampler = injector.get(Sampler) as any;
|
||||
const sampler = injector.get(Sampler);
|
||||
return sampler.sample();
|
||||
});
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export function main() {
|
||||
ids.map(id => ({provide: id, useValue: new MockMetric(id)})),
|
||||
MultiMetric.provideWith(ids)
|
||||
])
|
||||
.get<MultiMetric>(MultiMetric);
|
||||
.get(MultiMetric);
|
||||
return Promise.resolve(m);
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@ export function main() {
|
||||
}
|
||||
}
|
||||
];
|
||||
return Injector.create(providers).get<JsonFileReporter>(JsonFileReporter);
|
||||
return Injector.create(providers).get(JsonFileReporter);
|
||||
}
|
||||
|
||||
it('should write all data into a file',
|
||||
|
@ -17,7 +17,7 @@ export function main() {
|
||||
ids.map(id => ({provide: id, useValue: new MockReporter(id)})),
|
||||
MultiReporter.provideWith(ids)
|
||||
])
|
||||
.get<MultiReporter>(MultiReporter);
|
||||
.get(MultiReporter);
|
||||
return Promise.resolve(r);
|
||||
}
|
||||
|
||||
|
@ -352,12 +352,10 @@ export class HttpClient {
|
||||
|
||||
// Figure out the headers.
|
||||
let headers: HttpHeaders|undefined = undefined;
|
||||
if (!!options.headers !== undefined) {
|
||||
if (options.headers instanceof HttpHeaders) {
|
||||
headers = options.headers;
|
||||
} else {
|
||||
headers = new HttpHeaders(options.headers);
|
||||
}
|
||||
if (options.headers instanceof HttpHeaders) {
|
||||
headers = options.headers;
|
||||
} else {
|
||||
headers = new HttpHeaders(options.headers);
|
||||
}
|
||||
|
||||
// Sort out parameters.
|
||||
@ -371,7 +369,7 @@ export class HttpClient {
|
||||
}
|
||||
|
||||
// Construct the request.
|
||||
req = new HttpRequest(first, url !, options.body || null, {
|
||||
req = new HttpRequest(first, url !, (options.body !== undefined ? options.body : null), {
|
||||
headers,
|
||||
params,
|
||||
reportProgress: options.reportProgress,
|
||||
|
@ -171,7 +171,7 @@ export class HttpRequest<T> {
|
||||
// the body argument is to use a known no-body method like GET.
|
||||
if (mightHaveBody(this.method) || !!fourth) {
|
||||
// Body is the third argument, options are the fourth.
|
||||
this.body = third as T || null;
|
||||
this.body = (third !== undefined) ? third as T : null;
|
||||
options = fourth;
|
||||
} else {
|
||||
// No body required, options are the third argument. The body stays null.
|
||||
|
@ -262,7 +262,7 @@ export class HttpResponse<T> extends HttpResponseBase {
|
||||
body?: T | null, headers?: HttpHeaders; status?: number; statusText?: string; url?: string;
|
||||
} = {}) {
|
||||
super(init);
|
||||
this.body = init.body || null;
|
||||
this.body = init.body !== undefined ? init.body : null;
|
||||
}
|
||||
|
||||
readonly type: HttpEventType.Response = HttpEventType.Response;
|
||||
|
@ -180,24 +180,27 @@ export class HttpXhrBackend implements HttpBackend {
|
||||
|
||||
// Check whether the body needs to be parsed as JSON (in many cases the browser
|
||||
// will have done that already).
|
||||
if (ok && req.responseType === 'json' && typeof body === 'string') {
|
||||
// Attempt the parse. If it fails, a parse error should be delivered to the user.
|
||||
if (req.responseType === 'json' && typeof body === 'string') {
|
||||
// Save the original body, before attempting XSSI prefix stripping.
|
||||
const originalBody = body;
|
||||
body = body.replace(XSSI_PREFIX, '');
|
||||
try {
|
||||
body = JSON.parse(body);
|
||||
// Attempt the parse. If it fails, a parse error should be delivered to the user.
|
||||
body = body !== '' ? JSON.parse(body) : null;
|
||||
} catch (error) {
|
||||
// Even though the response status was 2xx, this is still an error.
|
||||
ok = false;
|
||||
// The parse error contains the text of the body that failed to parse.
|
||||
body = { error, text: body } as HttpJsonParseError;
|
||||
}
|
||||
} else if (!ok && req.responseType === 'json' && typeof body === 'string') {
|
||||
try {
|
||||
// Attempt to parse the body as JSON.
|
||||
body = JSON.parse(body);
|
||||
} catch (error) {
|
||||
// Cannot be certain that the body was meant to be parsed as JSON.
|
||||
// Leave the body as a string.
|
||||
// Since the JSON.parse failed, it's reasonable to assume this might not have been a
|
||||
// JSON response. Restore the original body (including any XSSI prefix) to deliver
|
||||
// a better error response.
|
||||
body = originalBody;
|
||||
|
||||
// If this was an error request to begin with, leave it as a string, it probably
|
||||
// just isn't JSON. Otherwise, deliver the parsing error to the user.
|
||||
if (ok) {
|
||||
// Even though the response status was 2xx, this is still an error.
|
||||
ok = false;
|
||||
// The parse error contains the text of the body that failed to parse.
|
||||
body = { error, text: body } as HttpJsonParseError;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +115,26 @@ export function main() {
|
||||
expect(testReq.request.body).toBe(body);
|
||||
testReq.flush('hello world');
|
||||
});
|
||||
it('with a json body of false', (done: DoneFn) => {
|
||||
client.post('/test', false, {observe: 'response', responseType: 'text'}).subscribe(res => {
|
||||
expect(res.ok).toBeTruthy();
|
||||
expect(res.status).toBe(200);
|
||||
done();
|
||||
});
|
||||
const testReq = backend.expectOne('/test');
|
||||
expect(testReq.request.body).toBe(false);
|
||||
testReq.flush('hello world');
|
||||
});
|
||||
it('with a json body of 0', (done: DoneFn) => {
|
||||
client.post('/test', 0, {observe: 'response', responseType: 'text'}).subscribe(res => {
|
||||
expect(res.ok).toBeTruthy();
|
||||
expect(res.status).toBe(200);
|
||||
done();
|
||||
});
|
||||
const testReq = backend.expectOne('/test');
|
||||
expect(testReq.request.body).toBe(0);
|
||||
testReq.flush('hello world');
|
||||
});
|
||||
it('with an arraybuffer', (done: DoneFn) => {
|
||||
const body = new ArrayBuffer(4);
|
||||
client.post('/test', body, {observe: 'response', responseType: 'text'}).subscribe(res => {
|
||||
|
@ -40,6 +40,10 @@ export function main() {
|
||||
expect(resp.ok).toBeTruthy();
|
||||
expect(resp.url).toBeNull();
|
||||
});
|
||||
it('accepts a falsy body', () => {
|
||||
expect(new HttpResponse({body: false}).body).toEqual(false);
|
||||
expect(new HttpResponse({body: 0}).body).toEqual(0);
|
||||
});
|
||||
});
|
||||
it('.ok is determined by status', () => {
|
||||
const good = new HttpResponse({status: 200});
|
||||
|
@ -25,6 +25,8 @@ const TEST_POST = new HttpRequest('POST', '/test', 'some body', {
|
||||
responseType: 'text',
|
||||
});
|
||||
|
||||
const XSSI_PREFIX = ')]}\'\n';
|
||||
|
||||
export function main() {
|
||||
describe('XhrBackend', () => {
|
||||
let factory: MockXhrFactory = null !;
|
||||
@ -92,6 +94,13 @@ export function main() {
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body !.data).toBe('some data');
|
||||
});
|
||||
it('handles a blank json response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
factory.mock.mockFlush(200, 'OK', '');
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body).toBeNull();
|
||||
});
|
||||
it('handles a json error response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
factory.mock.mockFlush(500, 'Error', JSON.stringify({data: 'some data'}));
|
||||
@ -99,6 +108,13 @@ export function main() {
|
||||
const res = events[1] as any as HttpErrorResponse;
|
||||
expect(res.error !.data).toBe('some data');
|
||||
});
|
||||
it('handles a json error response with XSSI prefix', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
factory.mock.mockFlush(500, 'Error', XSSI_PREFIX + JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as any as HttpErrorResponse;
|
||||
expect(res.error !.data).toBe('some data');
|
||||
});
|
||||
it('handles a json string response', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
expect(factory.mock.responseType).toEqual('text');
|
||||
@ -109,7 +125,7 @@ export function main() {
|
||||
});
|
||||
it('handles a json response with an XSSI prefix', () => {
|
||||
const events = trackEvents(backend.handle(TEST_POST.clone({responseType: 'json'})));
|
||||
factory.mock.mockFlush(200, 'OK', ')]}\'\n' + JSON.stringify({data: 'some data'}));
|
||||
factory.mock.mockFlush(200, 'OK', XSSI_PREFIX + JSON.stringify({data: 'some data'}));
|
||||
expect(events.length).toBe(2);
|
||||
const res = events[1] as HttpResponse<{data: string}>;
|
||||
expect(res.body !.data).toBe('some data');
|
||||
|
@ -42,9 +42,7 @@ export class SpyLocation implements Location {
|
||||
return currPath == givenPath + (query.length > 0 ? ('?' + query) : '');
|
||||
}
|
||||
|
||||
simulateUrlPop(pathname: string) {
|
||||
this._subject.emit({'url': pathname, 'pop': true, 'type': 'popstate'});
|
||||
}
|
||||
simulateUrlPop(pathname: string) { this._subject.emit({'url': pathname, 'pop': true}); }
|
||||
|
||||
simulateHashChange(pathname: string) {
|
||||
// Because we don't prevent the native event, the browser will independently update the path
|
||||
|
@ -11,11 +11,11 @@
|
||||
"dependencies": {
|
||||
"reflect-metadata": "^0.1.2",
|
||||
"minimist": "^1.2.0",
|
||||
"tsickle": "^0.25.5",
|
||||
"tsickle": "^0.24.0",
|
||||
"chokidar": "^1.4.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=2.4.2 <2.6",
|
||||
"typescript": ">=2.4.2 <2.5",
|
||||
"@angular/compiler": "0.0.0-PLACEHOLDER"
|
||||
},
|
||||
"repository": {
|
||||
|
@ -116,8 +116,8 @@ export class MetadataCollector {
|
||||
function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata {
|
||||
const result: ClassMetadata = {__symbolic: 'class'};
|
||||
|
||||
function getDecorators(decorators: ReadonlyArray<ts.Decorator>| undefined):
|
||||
MetadataSymbolicExpression[]|undefined {
|
||||
function getDecorators(decorators: ts.Decorator[] | undefined): MetadataSymbolicExpression[]|
|
||||
undefined {
|
||||
if (decorators && decorators.length)
|
||||
return decorators.map(decorator => objFromDecorator(decorator));
|
||||
return undefined;
|
||||
@ -551,6 +551,7 @@ export class MetadataCollector {
|
||||
__symbolic: 'module',
|
||||
version: this.options.version || METADATA_VERSION, metadata
|
||||
};
|
||||
if (sourceFile.moduleName) result.importAs = sourceFile.moduleName;
|
||||
if (exports) result.exports = exports;
|
||||
return result;
|
||||
}
|
||||
|
@ -41,13 +41,6 @@ export interface CodeGenerator {
|
||||
findGeneratedFileNames(fileName: string): string[];
|
||||
}
|
||||
|
||||
function assert<T>(condition: T | null | undefined) {
|
||||
if (!condition) {
|
||||
// TODO(chuckjaz): do the right thing
|
||||
}
|
||||
return condition !;
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements the following hosts based on an api.CompilerHost:
|
||||
* - ts.CompilerHost to be consumed by a ts.Program
|
||||
@ -120,7 +113,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
return sf ? this.metadataProvider.getMetadata(sf) : undefined;
|
||||
},
|
||||
fileExists: (filePath) => this.originalFileExists(filePath),
|
||||
readFile: (filePath) => assert(this.context.readFile(filePath)),
|
||||
readFile: (filePath) => this.context.readFile(filePath),
|
||||
};
|
||||
}
|
||||
|
||||
@ -428,7 +421,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
return summary.text;
|
||||
}
|
||||
if (this.originalFileExists(filePath)) {
|
||||
return assert(this.context.readFile(filePath));
|
||||
return this.context.readFile(filePath);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -479,7 +472,7 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
if (!this.originalFileExists(filePath)) {
|
||||
throw syntaxError(`Error: Resource file not found: ${filePath}`);
|
||||
}
|
||||
return assert(this.context.readFile(filePath));
|
||||
return this.context.readFile(filePath);
|
||||
}
|
||||
|
||||
private hasBundleIndex(filePath: string): boolean {
|
||||
@ -497,13 +490,13 @@ export class TsCompilerAotCompilerTypeCheckHostAdapter implements ts.CompilerHos
|
||||
if (this.originalFileExists(packageFile)) {
|
||||
// Once we see a package.json file, assume false until it we find the bundle index.
|
||||
result = false;
|
||||
const packageContent: any = JSON.parse(assert(this.context.readFile(packageFile)));
|
||||
const packageContent: any = JSON.parse(this.context.readFile(packageFile));
|
||||
if (packageContent.typings) {
|
||||
const typings = path.normalize(path.join(directory, packageContent.typings));
|
||||
if (DTS.test(typings)) {
|
||||
const metadataFile = typings.replace(DTS, '.metadata.json');
|
||||
if (this.originalFileExists(metadataFile)) {
|
||||
const metadata = JSON.parse(assert(this.context.readFile(metadataFile)));
|
||||
const metadata = JSON.parse(this.context.readFile(metadataFile));
|
||||
if (metadata.flatModuleIndexRedirect) {
|
||||
this.flatModuleIndexRedirectNames.add(typings);
|
||||
// Note: don't set result = true,
|
||||
|
@ -112,6 +112,7 @@ function upgradeMetadataWithDtsData(
|
||||
newMetadata.metadata[prop] = dtsMetadata.metadata[prop];
|
||||
}
|
||||
}
|
||||
if (dtsMetadata['importAs']) newMetadata['importAs'] = dtsMetadata['importAs'];
|
||||
|
||||
// Only copy exports from exports from metadata prior to version 3.
|
||||
// Starting with version 3 the collector began collecting exports and
|
||||
|
@ -62,7 +62,7 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
constructor(
|
||||
private rootNames: string[], private options: CompilerOptions, private host: CompilerHost,
|
||||
private oldProgram?: Program) {
|
||||
oldProgram?: Program) {
|
||||
const [major, minor] = ts.version.split('.');
|
||||
if (Number(major) < 2 || (Number(major) === 2 && Number(minor) < 4)) {
|
||||
throw new Error('The Angular Compiler requires TypeScript >= 2.4.');
|
||||
@ -606,8 +606,7 @@ class AngularCompilerProgram implements Program {
|
||||
|
||||
private writeFile(
|
||||
outFileName: string, outData: string, writeByteOrderMark: boolean,
|
||||
onError?: (message: string) => void, genFile?: GeneratedFile,
|
||||
sourceFiles?: ReadonlyArray<ts.SourceFile>) {
|
||||
onError?: (message: string) => void, genFile?: GeneratedFile, sourceFiles?: ts.SourceFile[]) {
|
||||
// collect emittedLibrarySummaries
|
||||
let baseFile: ts.SourceFile|undefined;
|
||||
if (genFile) {
|
||||
@ -654,8 +653,7 @@ class AngularCompilerProgram implements Program {
|
||||
if (baseFile) {
|
||||
sourceFiles = sourceFiles ? [...sourceFiles, baseFile] : [baseFile];
|
||||
}
|
||||
// TODO: remove any when TS 2.4 support is removed.
|
||||
this.host.writeFile(outFileName, outData, writeByteOrderMark, onError, sourceFiles as any);
|
||||
this.host.writeFile(outFileName, outData, writeByteOrderMark, onError, sourceFiles);
|
||||
}
|
||||
}
|
||||
|
||||
@ -803,9 +801,16 @@ export function i18nSerialize(
|
||||
default:
|
||||
serializer = new Xliff();
|
||||
}
|
||||
return bundle.write(
|
||||
serializer, (sourcePath: string) =>
|
||||
options.basePath ? path.relative(options.basePath, sourcePath) : sourcePath);
|
||||
|
||||
return bundle.write(serializer, getPathNormalizer(options.basePath));
|
||||
}
|
||||
|
||||
function getPathNormalizer(basePath?: string) {
|
||||
// normalize sourcepaths by removing the base path and always using "/" as a separator
|
||||
return (sourcePath: string) => {
|
||||
sourcePath = basePath ? path.relative(basePath, sourcePath) : sourcePath;
|
||||
return sourcePath.split(path.sep).join('/');
|
||||
};
|
||||
}
|
||||
|
||||
export function i18nGetExtension(formatName: string): string {
|
||||
|
@ -27,7 +27,7 @@ describe('metadata bundler', () => {
|
||||
|
||||
const originalOne = './src/one';
|
||||
const originalTwo = './src/two/index';
|
||||
expect(Object.keys(result.metadata.origins !)
|
||||
expect(Object.keys(result.metadata.origins)
|
||||
.sort()
|
||||
.map(name => ({name, value: result.metadata.origins ![name]})))
|
||||
.toEqual([
|
||||
|
@ -45,6 +45,7 @@ describe('Collector', () => {
|
||||
're-exports.ts',
|
||||
're-exports-2.ts',
|
||||
'export-as.d.ts',
|
||||
'named-module.d.ts',
|
||||
'static-field-reference.ts',
|
||||
'static-method.ts',
|
||||
'static-method-call.ts',
|
||||
@ -101,6 +102,12 @@ describe('Collector', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should preserve module names from TypeScript sources', () => {
|
||||
const sourceFile = program.getSourceFile('named-module.d.ts');
|
||||
const metadata = collector.getMetadata(sourceFile);
|
||||
expect(metadata !['importAs']).toEqual('some-named-module');
|
||||
});
|
||||
|
||||
it('should be able to collect a simple component\'s metadata', () => {
|
||||
const sourceFile = program.getSourceFile('app/hero-detail.component.ts');
|
||||
const metadata = collector.getMetadata(sourceFile);
|
||||
@ -1384,6 +1391,10 @@ const FILES: Directory = {
|
||||
declare function someFunction(): void;
|
||||
export { someFunction as SomeFunction };
|
||||
`,
|
||||
'named-module.d.ts': `
|
||||
/// <amd-module name="some-named-module" />
|
||||
export type SomeType = 'a';
|
||||
`,
|
||||
'local-symbol-ref.ts': `
|
||||
import {Component, Validators} from 'angular2/core';
|
||||
|
||||
|
@ -102,7 +102,6 @@ export class MockNode implements ts.Node {
|
||||
|
||||
export class MockIdentifier extends MockNode implements ts.Identifier {
|
||||
public text: string;
|
||||
public escapedText: ts.__String;
|
||||
// tslint:disable
|
||||
public _primaryExpressionBrand: any;
|
||||
public _memberExpressionBrand: any;
|
||||
@ -138,14 +137,12 @@ export class MockVariableDeclaration extends MockNode implements ts.VariableDecl
|
||||
}
|
||||
|
||||
export class MockSymbol implements ts.Symbol {
|
||||
public escapedName: ts.__String;
|
||||
constructor(
|
||||
public name: string, private node: ts.Declaration = MockVariableDeclaration.of(name),
|
||||
public flags: ts.SymbolFlags = 0) {}
|
||||
|
||||
getFlags(): ts.SymbolFlags { return this.flags; }
|
||||
getName(): string { return this.name; }
|
||||
getEscapedName(): ts.__String { return this.escapedName; }
|
||||
getDeclarations(): ts.Declaration[] { return [this.node]; }
|
||||
getDocumentationComment(): ts.SymbolDisplayPart[] { return []; }
|
||||
// TODO(vicb): removed in TS 2.2
|
||||
|
@ -119,7 +119,7 @@ describe('perform watch', () => {
|
||||
const errorFileContent = `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule((() => (1===1 ? null as any : null as any)) as any)
|
||||
@NgModule(() => (1===1 ? null as any : null as any))
|
||||
export class MyModule {}
|
||||
`;
|
||||
const indexTsPath = path.resolve(testSupport.basePath, 'src', 'index.ts');
|
||||
|
@ -325,7 +325,7 @@ describe('ng program', () => {
|
||||
'src/main.ts': `
|
||||
import {NgModule} from '@angular/core';
|
||||
|
||||
@NgModule((() => {if (1==1) return null as any;}) as any)
|
||||
@NgModule(() => {if (1==1) return null as any;})
|
||||
export class SomeClassWithInvalidMetadata {}
|
||||
`,
|
||||
});
|
||||
|
@ -42,7 +42,6 @@ export class JitCompiler {
|
||||
private _compiledDirectiveWrapperCache = new Map<Type, Type>();
|
||||
private _compiledNgModuleCache = new Map<Type, object>();
|
||||
private _sharedStylesheetCount = 0;
|
||||
private _addedAotSummaries = new Set<() => any[]>();
|
||||
|
||||
constructor(
|
||||
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
|
||||
@ -75,25 +74,10 @@ export class JitCompiler {
|
||||
|
||||
loadAotSummaries(summaries: () => any[]) {
|
||||
this.clearCache();
|
||||
this._addAotSummaries(summaries);
|
||||
}
|
||||
|
||||
private _addAotSummaries(fn: () => any[]) {
|
||||
if (this._addedAotSummaries.has(fn)) {
|
||||
return;
|
||||
}
|
||||
this._addedAotSummaries.add(fn);
|
||||
const summaries = fn();
|
||||
for (let i = 0; i < summaries.length; i++) {
|
||||
const entry = summaries[i];
|
||||
if (typeof entry === 'function') {
|
||||
this._addAotSummaries(entry);
|
||||
} else {
|
||||
const summary = entry as CompileTypeSummary;
|
||||
this._summaryResolver.addSummary(
|
||||
{symbol: summary.type.reference, metadata: null, type: summary});
|
||||
}
|
||||
}
|
||||
flattenSummaries(summaries).forEach((summary) => {
|
||||
this._summaryResolver.addSummary(
|
||||
{symbol: summary.type.reference, metadata: null, type: summary});
|
||||
});
|
||||
}
|
||||
|
||||
hasAotSummary(ref: Type) { return !!this._summaryResolver.resolveSummary(ref); }
|
||||
@ -216,7 +200,6 @@ export class JitCompiler {
|
||||
}
|
||||
|
||||
clearCache(): void {
|
||||
// Note: don't clear the _addedAotSummaries, as they don't change!
|
||||
this._metadataResolver.clearCache();
|
||||
this._compiledTemplateCache.clear();
|
||||
this._compiledHostTemplateCache.clear();
|
||||
@ -352,6 +335,25 @@ function assertComponent(meta: CompileDirectiveMetadata) {
|
||||
}
|
||||
}
|
||||
|
||||
function flattenSummaries(
|
||||
fn: () => any[], out: CompileTypeSummary[] = [],
|
||||
seen = new Set<() => any[]>()): CompileTypeSummary[] {
|
||||
if (seen.has(fn)) {
|
||||
return out;
|
||||
}
|
||||
seen.add(fn);
|
||||
const summaries = fn();
|
||||
for (let i = 0; i < summaries.length; i++) {
|
||||
const entry = summaries[i];
|
||||
if (typeof entry === 'function') {
|
||||
flattenSummaries(entry, out, seen);
|
||||
} else {
|
||||
out.push(entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function createOutputContext(): OutputContext {
|
||||
const importExpr = (symbol: any) =>
|
||||
ir.importExpr({name: identifierName(symbol), moduleName: null, runtime: symbol});
|
||||
|
@ -20,5 +20,5 @@ export {DirectRenderer as ɵDirectRenderer, RenderDebugInfo as ɵRenderDebugInfo
|
||||
export {global as ɵglobal, looseIdentical as ɵlooseIdentical, stringify as ɵstringify} from './util';
|
||||
export {makeDecorator as ɵmakeDecorator} from './util/decorators';
|
||||
export {isObservable as ɵisObservable, isPromise as ɵisPromise} from './util/lang';
|
||||
export {clearOverrides as ɵclearOverrides, overrideComponentView as ɵoverrideComponentView, overrideProvider as ɵoverrideProvider} from './view/index';
|
||||
export {clearProviderOverrides as ɵclearProviderOverrides, overrideProvider as ɵoverrideProvider} from './view/index';
|
||||
export {NOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR as ɵNOT_FOUND_CHECK_ONLY_ELEMENT_INJECTOR} from './view/provider';
|
||||
|
@ -7,12 +7,11 @@
|
||||
*/
|
||||
|
||||
import {Injector} from '../di/injector';
|
||||
import {ComponentFactory} from '../linker/component_factory';
|
||||
import {NgModuleFactory, NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {Type} from '../type';
|
||||
|
||||
import {initServicesIfNeeded} from './services';
|
||||
import {NgModuleDefinitionFactory, ProviderOverride, Services, ViewDefinition} from './types';
|
||||
import {NgModuleDefinitionFactory, ProviderOverride, Services} from './types';
|
||||
import {resolveDefinition} from './util';
|
||||
|
||||
export function overrideProvider(override: ProviderOverride) {
|
||||
@ -20,14 +19,9 @@ export function overrideProvider(override: ProviderOverride) {
|
||||
return Services.overrideProvider(override);
|
||||
}
|
||||
|
||||
export function overrideComponentView(comp: Type<any>, componentFactory: ComponentFactory<any>) {
|
||||
export function clearProviderOverrides() {
|
||||
initServicesIfNeeded();
|
||||
return Services.overrideComponentView(comp, componentFactory);
|
||||
}
|
||||
|
||||
export function clearOverrides() {
|
||||
initServicesIfNeeded();
|
||||
return Services.clearOverrides();
|
||||
return Services.clearProviderOverrides();
|
||||
}
|
||||
|
||||
// Attention: this function is called as top level function.
|
||||
|
@ -7,7 +7,7 @@
|
||||
*/
|
||||
|
||||
export {anchorDef, elementDef} from './element';
|
||||
export {clearOverrides, createNgModuleFactory, overrideComponentView, overrideProvider} from './entrypoint';
|
||||
export {clearProviderOverrides, createNgModuleFactory, overrideProvider} from './entrypoint';
|
||||
export {ngContentDef} from './ng_content';
|
||||
export {moduleDef, moduleProvideDef} from './ng_module';
|
||||
export {directiveDef, pipeDef, providerDef} from './provider';
|
||||
|
@ -14,7 +14,7 @@ import {ViewContainerRef} from '../linker/view_container_ref';
|
||||
import {Renderer as RendererV1, Renderer2} from '../render/api';
|
||||
|
||||
import {createChangeDetectorRef, createInjector, createRendererV1} from './refs';
|
||||
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData, shouldCallLifecycleInitHook} from './types';
|
||||
import {BindingDef, BindingFlags, DepDef, DepFlags, NodeDef, NodeFlags, OutputDef, OutputType, ProviderData, QueryValueType, Services, ViewData, ViewFlags, ViewState, asElementData, asProviderData} from './types';
|
||||
import {calcBindingFlags, checkBinding, dispatchEvent, isComponentView, splitDepsDsl, splitMatchedQueriesDsl, tokenKey, viewParentEl} from './util';
|
||||
|
||||
const RendererV1TokenKey = tokenKey(RendererV1);
|
||||
@ -198,8 +198,7 @@ export function checkAndUpdateDirectiveInline(
|
||||
if (changes) {
|
||||
directive.ngOnChanges(changes);
|
||||
}
|
||||
if ((def.flags & NodeFlags.OnInit) &&
|
||||
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingOnInit, def.nodeIndex)) {
|
||||
if ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||
directive.ngOnInit();
|
||||
}
|
||||
if (def.flags & NodeFlags.DoCheck) {
|
||||
@ -223,8 +222,7 @@ export function checkAndUpdateDirectiveDynamic(
|
||||
if (changes) {
|
||||
directive.ngOnChanges(changes);
|
||||
}
|
||||
if ((def.flags & NodeFlags.OnInit) &&
|
||||
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingOnInit, def.nodeIndex)) {
|
||||
if ((view.state & ViewState.FirstCheck) && (def.flags & NodeFlags.OnInit)) {
|
||||
directive.ngOnInit();
|
||||
}
|
||||
if (def.flags & NodeFlags.DoCheck) {
|
||||
@ -449,61 +447,17 @@ function updateProp(
|
||||
return changes;
|
||||
}
|
||||
|
||||
// This function calls the ngAfterContentCheck, ngAfterContentInit,
|
||||
// ngAfterViewCheck, and ngAfterViewInit lifecycle hooks (depending on the node
|
||||
// flags in lifecycle). Unlike ngDoCheck, ngOnChanges and ngOnInit, which are
|
||||
// called during a pre-order traversal of the view tree (that is calling the
|
||||
// parent hooks before the child hooks) these events are sent in using a
|
||||
// post-order traversal of the tree (children before parents). This changes the
|
||||
// meaning of initIndex in the view state. For ngOnInit, initIndex tracks the
|
||||
// expected nodeIndex which a ngOnInit should be called. When sending
|
||||
// ngAfterContentInit and ngAfterViewInit it is the expected count of
|
||||
// ngAfterContentInit or ngAfterViewInit methods that have been called. This
|
||||
// ensure that dispite being called recursively or after picking up after an
|
||||
// exception, the ngAfterContentInit or ngAfterViewInit will be called on the
|
||||
// correct nodes. Consider for example, the following (where E is an element
|
||||
// and D is a directive)
|
||||
// Tree: pre-order index post-order index
|
||||
// E1 0 6
|
||||
// E2 1 1
|
||||
// D3 2 0
|
||||
// E4 3 5
|
||||
// E5 4 4
|
||||
// E6 5 2
|
||||
// E7 6 3
|
||||
// As can be seen, the post-order index has an unclear relationship to the
|
||||
// pre-order index (postOrderIndex === preOrderIndex - parentCount +
|
||||
// childCount). Since number of calls to ngAfterContentInit and ngAfterViewInit
|
||||
// are stable (will be the same for the same view regardless of exceptions or
|
||||
// recursion) we just need to count them which will roughly correspond to the
|
||||
// post-order index (it skips elements and directives that do not have
|
||||
// lifecycle hooks).
|
||||
//
|
||||
// For example, if an exception is raised in the E6.onAfterViewInit() the
|
||||
// initIndex is left at 3 (by shouldCallLifecycleInitHook() which set it to
|
||||
// initIndex + 1). When checkAndUpdateView() is called again D3, E2 and E6 will
|
||||
// not have their ngAfterViewInit() called but, starting with E7, the rest of
|
||||
// the view will begin getting ngAfterViewInit() called until a check and
|
||||
// pass is complete.
|
||||
//
|
||||
// This algorthim also handles recursion. Consider if E4's ngAfterViewInit()
|
||||
// indirectly calls E1's ChangeDetectorRef.detectChanges(). The expected
|
||||
// initIndex is set to 6, the recusive checkAndUpdateView() starts walk again.
|
||||
// D3, E2, E6, E7, E5 and E4 are skipped, ngAfterViewInit() is called on E1.
|
||||
// When the recursion returns the initIndex will be 7 so E1 is skipped as it
|
||||
// has already been called in the recursively called checkAnUpdateView().
|
||||
export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: NodeFlags) {
|
||||
if (!(view.def.nodeFlags & lifecycles)) {
|
||||
return;
|
||||
}
|
||||
const nodes = view.def.nodes;
|
||||
let initIndex = 0;
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
const nodeDef = nodes[i];
|
||||
let parent = nodeDef.parent;
|
||||
if (!parent && nodeDef.flags & lifecycles) {
|
||||
// matching root node (e.g. a pipe)
|
||||
callProviderLifecycles(view, i, nodeDef.flags & lifecycles, initIndex++);
|
||||
callProviderLifecycles(view, i, nodeDef.flags & lifecycles);
|
||||
}
|
||||
if ((nodeDef.childFlags & lifecycles) === 0) {
|
||||
// no child matches one of the lifecycles
|
||||
@ -513,28 +467,25 @@ export function callLifecycleHooksChildrenFirst(view: ViewData, lifecycles: Node
|
||||
i === parent.nodeIndex + parent.childCount) {
|
||||
// last child of an element
|
||||
if (parent.directChildFlags & lifecycles) {
|
||||
initIndex = callElementProvidersLifecycles(view, parent, lifecycles, initIndex);
|
||||
callElementProvidersLifecycles(view, parent, lifecycles);
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function callElementProvidersLifecycles(
|
||||
view: ViewData, elDef: NodeDef, lifecycles: NodeFlags, initIndex: number): number {
|
||||
function callElementProvidersLifecycles(view: ViewData, elDef: NodeDef, lifecycles: NodeFlags) {
|
||||
for (let i = elDef.nodeIndex + 1; i <= elDef.nodeIndex + elDef.childCount; i++) {
|
||||
const nodeDef = view.def.nodes[i];
|
||||
if (nodeDef.flags & lifecycles) {
|
||||
callProviderLifecycles(view, i, nodeDef.flags & lifecycles, initIndex++);
|
||||
callProviderLifecycles(view, i, nodeDef.flags & lifecycles);
|
||||
}
|
||||
// only visit direct children
|
||||
i += nodeDef.childCount;
|
||||
}
|
||||
return initIndex;
|
||||
}
|
||||
|
||||
function callProviderLifecycles(
|
||||
view: ViewData, index: number, lifecycles: NodeFlags, initIndex: number) {
|
||||
function callProviderLifecycles(view: ViewData, index: number, lifecycles: NodeFlags) {
|
||||
const providerData = asProviderData(view, index);
|
||||
if (!providerData) {
|
||||
return;
|
||||
@ -544,15 +495,13 @@ function callProviderLifecycles(
|
||||
return;
|
||||
}
|
||||
Services.setCurrentNode(view, index);
|
||||
if (lifecycles & NodeFlags.AfterContentInit &&
|
||||
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingAfterContentInit, initIndex)) {
|
||||
if (lifecycles & NodeFlags.AfterContentInit) {
|
||||
provider.ngAfterContentInit();
|
||||
}
|
||||
if (lifecycles & NodeFlags.AfterContentChecked) {
|
||||
provider.ngAfterContentChecked();
|
||||
}
|
||||
if (lifecycles & NodeFlags.AfterViewInit &&
|
||||
shouldCallLifecycleInitHook(view, ViewState.InitState_CallingAfterViewInit, initIndex)) {
|
||||
if (lifecycles & NodeFlags.AfterViewInit) {
|
||||
provider.ngAfterViewInit();
|
||||
}
|
||||
if (lifecycles & NodeFlags.AfterViewChecked) {
|
||||
|
@ -10,7 +10,6 @@ import {isDevMode} from '../application_ref';
|
||||
import {DebugElement, DebugNode, EventListener, getDebugNode, indexDebugNode, removeDebugNodeFromIndex} from '../debug/debug_node';
|
||||
import {Injector} from '../di';
|
||||
import {ErrorHandler} from '../error_handler';
|
||||
import {ComponentFactory} from '../linker/component_factory';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {Renderer2, RendererFactory2, RendererStyleFlags2, RendererType2} from '../render/api';
|
||||
import {Sanitizer} from '../security';
|
||||
@ -19,9 +18,9 @@ import {Type} from '../type';
|
||||
import {isViewDebugError, viewDestroyedError, viewWrappedDebugError} from './errors';
|
||||
import {resolveDep} from './provider';
|
||||
import {dirtyParentQueries, getQueryValue} from './query';
|
||||
import {createInjector, createNgModuleRef, getComponentViewDefinitionFactory} from './refs';
|
||||
import {createInjector, createNgModuleRef} from './refs';
|
||||
import {ArgumentType, BindingFlags, CheckType, DebugContext, DepDef, ElementData, NgModuleDefinition, NgModuleProviderDef, NodeDef, NodeFlags, NodeLogger, ProviderOverride, RootData, Services, ViewData, ViewDefinition, ViewState, asElementData, asPureExpressionData} from './types';
|
||||
import {NOOP, isComponentView, renderNode, resolveDefinition, splitDepsDsl, viewParentEl} from './util';
|
||||
import {NOOP, isComponentView, renderNode, splitDepsDsl, viewParentEl} from './util';
|
||||
import {checkAndUpdateNode, checkAndUpdateView, checkNoChangesNode, checkNoChangesView, createComponentView, createEmbeddedView, createRootView, destroyView} from './view';
|
||||
|
||||
|
||||
@ -39,8 +38,7 @@ export function initServicesIfNeeded() {
|
||||
Services.createComponentView = services.createComponentView;
|
||||
Services.createNgModuleRef = services.createNgModuleRef;
|
||||
Services.overrideProvider = services.overrideProvider;
|
||||
Services.overrideComponentView = services.overrideComponentView;
|
||||
Services.clearOverrides = services.clearOverrides;
|
||||
Services.clearProviderOverrides = services.clearProviderOverrides;
|
||||
Services.checkAndUpdateView = services.checkAndUpdateView;
|
||||
Services.checkNoChangesView = services.checkNoChangesView;
|
||||
Services.destroyView = services.destroyView;
|
||||
@ -60,8 +58,7 @@ function createProdServices() {
|
||||
createComponentView: createComponentView,
|
||||
createNgModuleRef: createNgModuleRef,
|
||||
overrideProvider: NOOP,
|
||||
overrideComponentView: NOOP,
|
||||
clearOverrides: NOOP,
|
||||
clearProviderOverrides: NOOP,
|
||||
checkAndUpdateView: checkAndUpdateView,
|
||||
checkNoChangesView: checkNoChangesView,
|
||||
destroyView: destroyView,
|
||||
@ -87,8 +84,7 @@ function createDebugServices() {
|
||||
createComponentView: debugCreateComponentView,
|
||||
createNgModuleRef: debugCreateNgModuleRef,
|
||||
overrideProvider: debugOverrideProvider,
|
||||
overrideComponentView: debugOverrideComponentView,
|
||||
clearOverrides: debugClearOverrides,
|
||||
clearProviderOverrides: debugClearProviderOverrides,
|
||||
checkAndUpdateView: debugCheckAndUpdateView,
|
||||
checkNoChangesView: debugCheckNoChangesView,
|
||||
destroyView: debugDestroyView,
|
||||
@ -143,15 +139,10 @@ function debugCreateEmbeddedView(
|
||||
|
||||
function debugCreateComponentView(
|
||||
parentView: ViewData, nodeDef: NodeDef, viewDef: ViewDefinition, hostElement: any): ViewData {
|
||||
const overrideComponentView =
|
||||
viewDefOverrides.get(nodeDef.element !.componentProvider !.provider !.token);
|
||||
if (overrideComponentView) {
|
||||
viewDef = overrideComponentView;
|
||||
} else {
|
||||
viewDef = applyProviderOverridesToView(viewDef);
|
||||
}
|
||||
const defWithOverride = applyProviderOverridesToView(viewDef);
|
||||
return callWithDebugContext(
|
||||
DebugAction.create, createComponentView, null, [parentView, nodeDef, viewDef, hostElement]);
|
||||
DebugAction.create, createComponentView, null,
|
||||
[parentView, nodeDef, defWithOverride, hostElement]);
|
||||
}
|
||||
|
||||
function debugCreateNgModuleRef(
|
||||
@ -162,21 +153,13 @@ function debugCreateNgModuleRef(
|
||||
}
|
||||
|
||||
const providerOverrides = new Map<any, ProviderOverride>();
|
||||
const viewDefOverrides = new Map<any, ViewDefinition>();
|
||||
|
||||
function debugOverrideProvider(override: ProviderOverride) {
|
||||
providerOverrides.set(override.token, override);
|
||||
}
|
||||
|
||||
function debugOverrideComponentView(comp: any, compFactory: ComponentFactory<any>) {
|
||||
const hostViewDef = resolveDefinition(getComponentViewDefinitionFactory(compFactory));
|
||||
const compViewDef = resolveDefinition(hostViewDef.nodes[0].element !.componentView !);
|
||||
viewDefOverrides.set(comp, compViewDef);
|
||||
}
|
||||
|
||||
function debugClearOverrides() {
|
||||
function debugClearProviderOverrides() {
|
||||
providerOverrides.clear();
|
||||
viewDefOverrides.clear();
|
||||
}
|
||||
|
||||
// Notes about the algorithm:
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
import {Injector} from '../di';
|
||||
import {ErrorHandler} from '../error_handler';
|
||||
import {ComponentFactory} from '../linker/component_factory';
|
||||
import {NgModuleRef} from '../linker/ng_module_factory';
|
||||
import {QueryList} from '../linker/query_list';
|
||||
import {TemplateRef} from '../linker/template_ref';
|
||||
@ -17,7 +16,6 @@ import {Renderer2, RendererFactory2, RendererType2} from '../render/api';
|
||||
import {Sanitizer, SecurityContext} from '../security';
|
||||
import {Type} from '../type';
|
||||
|
||||
|
||||
// -------------------------------------
|
||||
// Defs
|
||||
// -------------------------------------
|
||||
@ -354,7 +352,6 @@ export interface ViewData {
|
||||
state: ViewState;
|
||||
oldValues: any[];
|
||||
disposables: DisposableFn[]|null;
|
||||
initIndex: number;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -370,52 +367,8 @@ export const enum ViewState {
|
||||
CheckProjectedViews = 1 << 6,
|
||||
Destroyed = 1 << 7,
|
||||
|
||||
// InitState Uses 3 bits
|
||||
InitState_Mask = 7 << 8,
|
||||
InitState_BeforeInit = 0 << 8,
|
||||
InitState_CallingOnInit = 1 << 8,
|
||||
InitState_CallingAfterContentInit = 2 << 8,
|
||||
InitState_CallingAfterViewInit = 3 << 8,
|
||||
InitState_AfterInit = 4 << 8,
|
||||
|
||||
CatDetectChanges = Attached | ChecksEnabled,
|
||||
CatInit = BeforeFirstCheck | CatDetectChanges | InitState_BeforeInit
|
||||
}
|
||||
|
||||
// Called before each cycle of a view's check to detect whether this is in the
|
||||
// initState for which we need to call ngOnInit, ngAfterContentInit or ngAfterViewInit
|
||||
// lifecycle methods. Returns true if this check cycle should call lifecycle
|
||||
// methods.
|
||||
export function shiftInitState(
|
||||
view: ViewData, priorInitState: ViewState, newInitState: ViewState): boolean {
|
||||
// Only update the InitState if we are currently in the prior state.
|
||||
// For example, only move into CallingInit if we are in BeforeInit. Only
|
||||
// move into CallingContentInit if we are in CallingInit. Normally this will
|
||||
// always be true because of how checkCycle is called in checkAndUpdateView.
|
||||
// However, if checkAndUpdateView is called recursively or if an exception is
|
||||
// thrown while checkAndUpdateView is running, checkAndUpdateView starts over
|
||||
// from the beginning. This ensures the state is monotonically increasing,
|
||||
// terminating in the AfterInit state, which ensures the Init methods are called
|
||||
// at least once and only once.
|
||||
const state = view.state;
|
||||
const initState = state & ViewState.InitState_Mask;
|
||||
if (initState === priorInitState) {
|
||||
view.state = (state & ~ViewState.InitState_Mask) | newInitState;
|
||||
view.initIndex = -1;
|
||||
return true;
|
||||
}
|
||||
return initState === newInitState;
|
||||
}
|
||||
|
||||
// Returns true if the lifecycle init method should be called for the node with
|
||||
// the given init index.
|
||||
export function shouldCallLifecycleInitHook(
|
||||
view: ViewData, initState: ViewState, index: number): boolean {
|
||||
if ((view.state & ViewState.InitState_Mask) === initState && view.initIndex <= index) {
|
||||
view.initIndex = index + 1;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
CatInit = BeforeFirstCheck | CatDetectChanges
|
||||
}
|
||||
|
||||
export interface DisposableFn { (): void; }
|
||||
@ -569,8 +522,7 @@ export interface Services {
|
||||
moduleType: Type<any>, parent: Injector, bootstrapComponents: Type<any>[],
|
||||
def: NgModuleDefinition): NgModuleRef<any>;
|
||||
overrideProvider(override: ProviderOverride): void;
|
||||
overrideComponentView(compType: Type<any>, compFactory: ComponentFactory<any>): void;
|
||||
clearOverrides(): void;
|
||||
clearProviderOverrides(): void;
|
||||
checkAndUpdateView(view: ViewData): void;
|
||||
checkNoChangesView(view: ViewData): void;
|
||||
destroyView(view: ViewData): void;
|
||||
@ -595,8 +547,7 @@ export const Services: Services = {
|
||||
createComponentView: undefined !,
|
||||
createNgModuleRef: undefined !,
|
||||
overrideProvider: undefined !,
|
||||
overrideComponentView: undefined !,
|
||||
clearOverrides: undefined !,
|
||||
clearProviderOverrides: undefined !,
|
||||
checkAndUpdateView: undefined !,
|
||||
checkNoChangesView: undefined !,
|
||||
destroyView: undefined !,
|
||||
|
@ -16,7 +16,7 @@ import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline,
|
||||
import {checkAndUpdateQuery, createQuery} from './query';
|
||||
import {createTemplateData, createViewContainerData} from './refs';
|
||||
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
|
||||
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData, shiftInitState} from './types';
|
||||
import {ArgumentType, CheckType, ElementData, NodeData, NodeDef, NodeFlags, ProviderData, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asQueryList, asTextData} from './types';
|
||||
import {NOOP, checkBindingNoChanges, isComponentView, markParentViewsForCheckProjectedViews, resolveDefinition, tokenKey} from './util';
|
||||
import {detachProjectedView} from './view_attach';
|
||||
|
||||
@ -236,8 +236,7 @@ function createView(
|
||||
context: null,
|
||||
component: null, nodes,
|
||||
state: ViewState.CatInit, root, renderer,
|
||||
oldValues: new Array(def.bindingCount), disposables,
|
||||
initIndex: -1
|
||||
oldValues: new Array(def.bindingCount), disposables
|
||||
};
|
||||
return view;
|
||||
}
|
||||
@ -354,32 +353,29 @@ export function checkAndUpdateView(view: ViewData) {
|
||||
} else {
|
||||
view.state &= ~ViewState.FirstCheck;
|
||||
}
|
||||
shiftInitState(view, ViewState.InitState_BeforeInit, ViewState.InitState_CallingOnInit);
|
||||
markProjectedViewsForCheck(view);
|
||||
Services.updateDirectives(view, CheckType.CheckAndUpdate);
|
||||
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(
|
||||
view, NodeFlags.TypeContentQuery, NodeFlags.DynamicQuery, CheckType.CheckAndUpdate);
|
||||
let callInit = shiftInitState(
|
||||
view, ViewState.InitState_CallingOnInit, ViewState.InitState_CallingAfterContentInit);
|
||||
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterContentChecked | (callInit ? NodeFlags.AfterContentInit : 0));
|
||||
view, NodeFlags.AfterContentChecked |
|
||||
(view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0));
|
||||
|
||||
Services.updateRenderer(view, CheckType.CheckAndUpdate);
|
||||
|
||||
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
|
||||
execQueriesAction(
|
||||
view, NodeFlags.TypeViewQuery, NodeFlags.DynamicQuery, CheckType.CheckAndUpdate);
|
||||
callInit = shiftInitState(
|
||||
view, ViewState.InitState_CallingAfterContentInit, ViewState.InitState_CallingAfterViewInit);
|
||||
callLifecycleHooksChildrenFirst(
|
||||
view, NodeFlags.AfterViewChecked | (callInit ? NodeFlags.AfterViewInit : 0));
|
||||
view, NodeFlags.AfterViewChecked |
|
||||
(view.state & ViewState.FirstCheck ? NodeFlags.AfterViewInit : 0));
|
||||
|
||||
if (view.def.flags & ViewFlags.OnPush) {
|
||||
view.state &= ~ViewState.ChecksEnabled;
|
||||
}
|
||||
view.state &= ~(ViewState.CheckProjectedViews | ViewState.CheckProjectedView);
|
||||
shiftInitState(view, ViewState.InitState_CallingAfterViewInit, ViewState.InitState_AfterInit);
|
||||
}
|
||||
|
||||
export function checkAndUpdateNode(
|
||||
|
@ -31,7 +31,7 @@ import {EventEmitter} from '../event_emitter';
|
||||
* import {NgIf} from '@angular/common';
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'ng-zone-demo'.
|
||||
* selector: 'ng-zone-demo',
|
||||
* template: `
|
||||
* <h2>Demo: NgZone</h2>
|
||||
*
|
||||
@ -63,9 +63,10 @@ import {EventEmitter} from '../event_emitter';
|
||||
* this.progress = 0;
|
||||
* this._ngZone.runOutsideAngular(() => {
|
||||
* this._increaseProgress(() => {
|
||||
* // reenter the Angular zone and display done
|
||||
* this._ngZone.run(() => {console.log('Outside Done!') });
|
||||
* }}));
|
||||
* // reenter the Angular zone and display done
|
||||
* this._ngZone.run(() => { console.log('Outside Done!'); });
|
||||
* });
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* _increaseProgress(doneCallback: () => void) {
|
||||
@ -73,7 +74,7 @@ import {EventEmitter} from '../event_emitter';
|
||||
* console.log(`Current progress: ${this.progress}%`);
|
||||
*
|
||||
* if (this.progress < 100) {
|
||||
* window.setTimeout(() => this._increaseProgress(doneCallback)), 10)
|
||||
* window.setTimeout(() => this._increaseProgress(doneCallback), 10);
|
||||
* } else {
|
||||
* doneCallback();
|
||||
* }
|
||||
|
@ -80,38 +80,6 @@ export function main() {
|
||||
flushMicrotasks();
|
||||
expect(cmp.status).toEqual('done');
|
||||
}));
|
||||
|
||||
it('should always run .start callbacks before .done callbacks even for noop animations',
|
||||
fakeAsync(() => {
|
||||
@Component({
|
||||
selector: 'cmp',
|
||||
template: `
|
||||
<div [@myAnimation]="exp" (@myAnimation.start)="cb('start')" (@myAnimation.done)="cb('done')"></div>
|
||||
`,
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[
|
||||
transition('* => go', []),
|
||||
]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
exp: any = false;
|
||||
log: string[] = [];
|
||||
cb(status: string) { this.log.push(status); }
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.exp = 'go';
|
||||
fixture.detectChanges();
|
||||
expect(cmp.log).toEqual([]);
|
||||
|
||||
flushMicrotasks();
|
||||
expect(cmp.log).toEqual(['start', 'done']);
|
||||
}));
|
||||
});
|
||||
|
||||
describe('component fixture integration', () => {
|
||||
|
@ -5,7 +5,7 @@
|
||||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {AUTO_STYLE, AnimationPlayer, animate, animateChild, query, stagger, state, style, transition, trigger, ɵAnimationGroupPlayer as AnimationGroupPlayer} from '@angular/animations';
|
||||
import {AUTO_STYLE, AnimationPlayer, animate, animateChild, group, query, sequence, stagger, state, style, transition, trigger, ɵAnimationGroupPlayer as AnimationGroupPlayer} from '@angular/animations';
|
||||
import {AnimationDriver, ɵAnimationEngine} from '@angular/animations/browser';
|
||||
import {matchesElement} from '@angular/animations/browser/src/render/shared';
|
||||
import {ENTER_CLASSNAME, LEAVE_CLASSNAME} from '@angular/animations/browser/src/util';
|
||||
@ -2968,6 +2968,137 @@ export function main() {
|
||||
{offset: 0, width: '0px'}, {offset: .67, width: '0px'}, {offset: 1, width: '200px'}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should scope :enter queries between sub animations', () => {
|
||||
@Component({
|
||||
selector: 'cmp',
|
||||
animations: [
|
||||
trigger(
|
||||
'parent',
|
||||
[
|
||||
transition(':enter', group([
|
||||
sequence([
|
||||
style({opacity: 0}),
|
||||
animate(1000, style({opacity: 1})),
|
||||
]),
|
||||
query(':enter @child', animateChild()),
|
||||
])),
|
||||
]),
|
||||
trigger(
|
||||
'child',
|
||||
[
|
||||
transition(
|
||||
':enter',
|
||||
[
|
||||
query(
|
||||
':enter .item',
|
||||
[style({opacity: 0}), animate(1000, style({opacity: 1}))]),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
<div @parent *ngIf="exp1" class="container">
|
||||
<div *ngIf="exp2">
|
||||
<div @child>
|
||||
<div *ngIf="exp3">
|
||||
<div class="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class Cmp {
|
||||
public exp1: any;
|
||||
public exp2: any;
|
||||
public exp3: any;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
fixture.detectChanges();
|
||||
resetLog();
|
||||
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.exp1 = true;
|
||||
cmp.exp2 = true;
|
||||
cmp.exp3 = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
const players = getLog();
|
||||
expect(players.length).toEqual(2);
|
||||
|
||||
const [p1, p2] = players;
|
||||
expect(p1.element.classList.contains('container')).toBeTruthy();
|
||||
expect(p2.element.classList.contains('item')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should scope :leave queries between sub animations', () => {
|
||||
@Component({
|
||||
selector: 'cmp',
|
||||
animations: [
|
||||
trigger(
|
||||
'parent',
|
||||
[
|
||||
transition(':leave', group([
|
||||
sequence([
|
||||
style({opacity: 0}),
|
||||
animate(1000, style({opacity: 1})),
|
||||
]),
|
||||
query(':leave @child', animateChild()),
|
||||
])),
|
||||
]),
|
||||
trigger(
|
||||
'child',
|
||||
[
|
||||
transition(
|
||||
':leave',
|
||||
[
|
||||
query(
|
||||
':leave .item',
|
||||
[style({opacity: 0}), animate(1000, style({opacity: 1}))]),
|
||||
]),
|
||||
]),
|
||||
],
|
||||
template: `
|
||||
<div @parent *ngIf="exp1" class="container">
|
||||
<div *ngIf="exp2">
|
||||
<div @child>
|
||||
<div *ngIf="exp3">
|
||||
<div class="item"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
})
|
||||
class Cmp {
|
||||
public exp1: any;
|
||||
public exp2: any;
|
||||
public exp3: any;
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.exp1 = true;
|
||||
cmp.exp2 = true;
|
||||
cmp.exp3 = true;
|
||||
fixture.detectChanges();
|
||||
resetLog();
|
||||
|
||||
cmp.exp1 = false;
|
||||
fixture.detectChanges();
|
||||
|
||||
const players = getLog();
|
||||
expect(players.length).toEqual(2);
|
||||
|
||||
const [p1, p2] = players;
|
||||
expect(p1.element.classList.contains('container')).toBeTruthy();
|
||||
expect(p2.element.classList.contains('item')).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('animation control flags', () => {
|
||||
|
@ -61,10 +61,8 @@ export function main() {
|
||||
const injector = Injector.create([{provide: IterableDiffers, useValue: parent}]);
|
||||
const childInjector = Injector.create([IterableDiffers.extend([factory2])], injector);
|
||||
|
||||
expect(injector.get<IterableDiffers>(IterableDiffers).factories).toEqual([factory1]);
|
||||
expect(childInjector.get<IterableDiffers>(IterableDiffers).factories).toEqual([
|
||||
factory2, factory1
|
||||
]);
|
||||
expect(injector.get(IterableDiffers).factories).toEqual([factory1]);
|
||||
expect(childInjector.get(IterableDiffers).factories).toEqual([factory2, factory1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -98,7 +98,7 @@ export function main() {
|
||||
|
||||
it('should resolve dependencies based on type information', () => {
|
||||
const injector = Injector.create([Engine.PROVIDER, Car.PROVIDER]);
|
||||
const car = injector.get<Car>(Car);
|
||||
const car = injector.get(Car);
|
||||
|
||||
expect(car).toBeAnInstanceOf(Car);
|
||||
expect(car.engine).toBeAnInstanceOf(Engine);
|
||||
@ -138,7 +138,7 @@ export function main() {
|
||||
const injector = Injector.create(
|
||||
[Engine.PROVIDER, {provide: Car, useFactory: sportsCarFactory, deps: [Engine]}]);
|
||||
|
||||
const car = injector.get<Car>(Car);
|
||||
const car = injector.get(Car);
|
||||
expect(car).toBeAnInstanceOf(SportsCar);
|
||||
expect(car.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
@ -208,7 +208,7 @@ export function main() {
|
||||
{provide: Car, useFactory: (e: Engine) => new SportsCar(e), deps: [Engine]}
|
||||
]);
|
||||
|
||||
const car = injector.get<Car>(Car);
|
||||
const car = injector.get(Car);
|
||||
expect(car).toBeAnInstanceOf(SportsCar);
|
||||
expect(car.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
@ -216,7 +216,7 @@ export function main() {
|
||||
it('should support optional dependencies', () => {
|
||||
const injector = Injector.create([CarWithOptionalEngine.PROVIDER]);
|
||||
|
||||
const car = injector.get<CarWithOptionalEngine>(CarWithOptionalEngine);
|
||||
const car = injector.get(CarWithOptionalEngine);
|
||||
expect(car.engine).toEqual(null);
|
||||
});
|
||||
|
||||
@ -364,7 +364,7 @@ export function main() {
|
||||
const parent = Injector.create([Car.PROVIDER, Engine.PROVIDER]);
|
||||
const child = Injector.create([TurboEngine.PROVIDER], parent);
|
||||
|
||||
const carFromChild = child.get<Car>(Car);
|
||||
const carFromChild = child.get(Car);
|
||||
expect(carFromChild.engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
|
||||
@ -391,7 +391,7 @@ export function main() {
|
||||
it('should instantiate an object in the context of the injector', () => {
|
||||
const inj = Injector.create([Engine.PROVIDER]);
|
||||
const childInj = Injector.create([Car.PROVIDER], inj);
|
||||
const car = childInj.get<Car>(Car);
|
||||
const car = childInj.get(Car);
|
||||
expect(car).toBeAnInstanceOf(Car);
|
||||
expect(car.engine).toBe(inj.get(Engine));
|
||||
});
|
||||
@ -430,7 +430,7 @@ export function main() {
|
||||
],
|
||||
parent);
|
||||
|
||||
expect(child.get<Car>(Car).engine).toBeAnInstanceOf(Engine);
|
||||
expect(child.get(Car).engine).toBeAnInstanceOf(Engine);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1152,6 +1152,7 @@ export function main() {
|
||||
]);
|
||||
}));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('enforce no new changes', () => {
|
||||
@ -1462,151 +1463,6 @@ export function main() {
|
||||
expect(divEl.nativeElement).toHaveCssClass('foo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('lifecycle asserts', () => {
|
||||
let logged: string[];
|
||||
|
||||
function log(value: string) { logged.push(value); }
|
||||
function clearLog() { logged = []; }
|
||||
|
||||
function expectOnceAndOnlyOnce(log: string) {
|
||||
expect(logged.indexOf(log) >= 0)
|
||||
.toBeTruthy(`'${log}' not logged. Log was ${JSON.stringify(logged)}`);
|
||||
expect(logged.lastIndexOf(log) === logged.indexOf(log))
|
||||
.toBeTruthy(`'${log}' logged more than once. Log was ${JSON.stringify(logged)}`);
|
||||
}
|
||||
|
||||
beforeEach(() => { clearLog(); });
|
||||
|
||||
enum LifetimeMethods {
|
||||
None = 0,
|
||||
ngOnInit = 1 << 0,
|
||||
ngOnChanges = 1 << 1,
|
||||
ngAfterViewInit = 1 << 2,
|
||||
ngAfterContentInit = 1 << 3,
|
||||
ngDoCheck = 1 << 4,
|
||||
InitMethods = ngOnInit | ngAfterViewInit | ngAfterContentInit,
|
||||
InitMethodsAndChanges = InitMethods | ngOnChanges,
|
||||
All = InitMethodsAndChanges | ngDoCheck,
|
||||
}
|
||||
|
||||
function forEachMethod(methods: LifetimeMethods, cb: (method: LifetimeMethods) => void) {
|
||||
if (methods & LifetimeMethods.ngOnInit) cb(LifetimeMethods.ngOnInit);
|
||||
if (methods & LifetimeMethods.ngOnChanges) cb(LifetimeMethods.ngOnChanges);
|
||||
if (methods & LifetimeMethods.ngAfterContentInit) cb(LifetimeMethods.ngAfterContentInit);
|
||||
if (methods & LifetimeMethods.ngAfterViewInit) cb(LifetimeMethods.ngAfterViewInit);
|
||||
if (methods & LifetimeMethods.ngDoCheck) cb(LifetimeMethods.ngDoCheck);
|
||||
}
|
||||
|
||||
interface Options {
|
||||
childRecursion: LifetimeMethods;
|
||||
childThrows: LifetimeMethods;
|
||||
}
|
||||
|
||||
describe('calling init', () => {
|
||||
function initialize(options: Options) {
|
||||
@Component({selector: 'my-child', template: ''})
|
||||
class MyChild {
|
||||
private thrown = LifetimeMethods.None;
|
||||
|
||||
@Input() inp: boolean;
|
||||
@Output() outp = new EventEmitter<any>();
|
||||
|
||||
constructor() {}
|
||||
|
||||
ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); }
|
||||
ngOnInit() { this.check(LifetimeMethods.ngOnInit); }
|
||||
ngOnChanges() { this.check(LifetimeMethods.ngOnChanges); }
|
||||
ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); }
|
||||
ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); }
|
||||
|
||||
private check(method: LifetimeMethods) {
|
||||
log(`MyChild::${LifetimeMethods[method]}()`);
|
||||
|
||||
if ((options.childRecursion & method) !== 0) {
|
||||
if (logged.length < 20) {
|
||||
this.outp.emit(null);
|
||||
} else {
|
||||
fail(`Unexpected MyChild::${LifetimeMethods[method]} recursion`);
|
||||
}
|
||||
}
|
||||
if ((options.childThrows & method) !== 0) {
|
||||
if ((this.thrown & method) === 0) {
|
||||
this.thrown |= method;
|
||||
log(`<THROW from MyChild::${LifetimeMethods[method]}>()`);
|
||||
throw new Error(`Throw from MyChild::${LifetimeMethods[method]}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: `<my-child [inp]='true' (outp)='onOutp()'></my-child>`
|
||||
})
|
||||
class MyComponent {
|
||||
constructor(private changeDetectionRef: ChangeDetectorRef) {}
|
||||
ngDoCheck() { this.check(LifetimeMethods.ngDoCheck); }
|
||||
ngOnInit() { this.check(LifetimeMethods.ngOnInit); }
|
||||
ngAfterViewInit() { this.check(LifetimeMethods.ngAfterViewInit); }
|
||||
ngAfterContentInit() { this.check(LifetimeMethods.ngAfterContentInit); }
|
||||
onOutp() {
|
||||
log('<RECURSION START>');
|
||||
this.changeDetectionRef.detectChanges();
|
||||
log('<RECURSION DONE>');
|
||||
}
|
||||
|
||||
private check(method: LifetimeMethods) {
|
||||
log(`MyComponent::${LifetimeMethods[method]}()`);
|
||||
}
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [MyChild, MyComponent]});
|
||||
|
||||
return createCompFixture(`<my-component></my-component>`);
|
||||
}
|
||||
|
||||
function ensureOneInit(options: Options) {
|
||||
const ctx = initialize(options);
|
||||
|
||||
|
||||
const throws = options.childThrows != LifetimeMethods.None;
|
||||
if (throws) {
|
||||
log(`<CYCLE 0 START>`);
|
||||
expect(() => {
|
||||
// Expect child to throw.
|
||||
ctx.detectChanges();
|
||||
}).toThrow();
|
||||
log(`<CYCLE 0 END>`);
|
||||
log(`<CYCLE 1 START>`);
|
||||
}
|
||||
ctx.detectChanges();
|
||||
if (throws) log(`<CYCLE 1 DONE>`);
|
||||
expectOnceAndOnlyOnce('MyComponent::ngOnInit()');
|
||||
expectOnceAndOnlyOnce('MyChild::ngOnInit()');
|
||||
expectOnceAndOnlyOnce('MyComponent::ngAfterViewInit()');
|
||||
expectOnceAndOnlyOnce('MyComponent::ngAfterContentInit()');
|
||||
expectOnceAndOnlyOnce('MyChild::ngAfterViewInit()');
|
||||
expectOnceAndOnlyOnce('MyChild::ngAfterContentInit()');
|
||||
}
|
||||
|
||||
forEachMethod(LifetimeMethods.InitMethodsAndChanges, method => {
|
||||
it(`should ensure that init hooks are called once an only once with recursion in ${LifetimeMethods[method]} `,
|
||||
() => {
|
||||
// Ensure all the init methods are called once.
|
||||
ensureOneInit({childRecursion: method, childThrows: LifetimeMethods.None});
|
||||
});
|
||||
});
|
||||
forEachMethod(LifetimeMethods.All, method => {
|
||||
it(`should ensure that init hooks are called once an only once with a throw in ${LifetimeMethods[method]} `,
|
||||
() => {
|
||||
// Ensure all the init methods are called once.
|
||||
// the first cycle throws but the next cycle should complete the inits.
|
||||
ensureOneInit({childRecursion: LifetimeMethods.None, childThrows: method});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,10 @@ import {CompileMetadataResolver} from '@angular/compiler/src/metadata_resolver';
|
||||
import {MockResourceLoader} from '@angular/compiler/testing/src/resource_loader_mock';
|
||||
import {Component, Directive, Injectable, NgModule, Pipe, Type} from '@angular/core';
|
||||
import {TestBed, async, getTestBed} from '@angular/core/testing';
|
||||
import {expect} from '@angular/platform-browser/testing/src/matchers';
|
||||
|
||||
export function main() {
|
||||
describe('Jit Summaries', () => {
|
||||
let instances: Map<any, Base>;
|
||||
let summaries: () => any[];
|
||||
|
||||
class SomeDep {}
|
||||
|
||||
@ -71,7 +69,7 @@ export function main() {
|
||||
TestBed.configureCompiler({providers: [{provide: ResourceLoader, useValue: resourceLoader}]});
|
||||
TestBed.configureTestingModule({imports: [SomeModule], providers: [SomeDep]});
|
||||
|
||||
let summariesPromise = TestBed.compileComponents().then(() => {
|
||||
TestBed.compileComponents().then(() => {
|
||||
const metadataResolver = TestBed.get(CompileMetadataResolver) as CompileMetadataResolver;
|
||||
const summaries = [
|
||||
metadataResolver.getNgModuleSummary(SomeModule),
|
||||
@ -85,12 +83,10 @@ export function main() {
|
||||
metadataResolver.getInjectableSummary(SomeService)
|
||||
];
|
||||
clearMetadata();
|
||||
TestBed.resetTestingModule();
|
||||
return () => summaries;
|
||||
resetTestEnvironmentWithSummaries(() => summaries);
|
||||
});
|
||||
|
||||
resourceLoader.flush();
|
||||
return summariesPromise;
|
||||
}
|
||||
|
||||
function setMetadata(resourceLoader: MockResourceLoader) {
|
||||
@ -127,14 +123,12 @@ export function main() {
|
||||
|
||||
beforeEach(async(() => {
|
||||
instances = new Map<any, any>();
|
||||
createSummaries().then(s => summaries = s);
|
||||
createSummaries();
|
||||
}));
|
||||
|
||||
afterEach(() => { resetTestEnvironmentWithSummaries(); });
|
||||
|
||||
it('should use directive metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
@Component({template: '<div someDir></div>'})
|
||||
class TestComp {
|
||||
}
|
||||
@ -146,8 +140,6 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should use pipe metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
@Component({template: '{{1 | somePipe}}'})
|
||||
class TestComp {
|
||||
}
|
||||
@ -158,8 +150,6 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should use Service metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [SomeService, SomeDep],
|
||||
});
|
||||
@ -168,8 +158,6 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should use NgModule metadata from summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed
|
||||
.configureTestingModule(
|
||||
{providers: [SomeDep], declarations: [TestComp3], imports: [SomeModule]})
|
||||
@ -182,16 +170,12 @@ export function main() {
|
||||
});
|
||||
|
||||
it('should allow to create private components from imported NgModule summaries', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
|
||||
.createComponent(SomePrivateComponent);
|
||||
expectInstanceCreated(SomePrivateComponent);
|
||||
});
|
||||
|
||||
it('should throw when trying to mock a type with a summary', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.resetTestingModule();
|
||||
expect(() => TestBed.overrideComponent(SomePrivateComponent, {add: {}}).compileComponents())
|
||||
.toThrowError(
|
||||
@ -206,47 +190,5 @@ export function main() {
|
||||
expect(() => TestBed.overrideModule(SomeModule, {add: {}}).compileComponents())
|
||||
.toThrowError('SomeModule was AOT compiled, so its metadata cannot be changed.');
|
||||
});
|
||||
|
||||
it('should allow to add summaries via configureTestingModule', () => {
|
||||
resetTestEnvironmentWithSummaries();
|
||||
|
||||
@Component({template: '<div someDir></div>'})
|
||||
class TestComp {
|
||||
}
|
||||
|
||||
TestBed
|
||||
.configureTestingModule({
|
||||
providers: [SomeDep],
|
||||
declarations: [TestComp, SomeDirective],
|
||||
aotSummaries: summaries
|
||||
})
|
||||
.createComponent(TestComp);
|
||||
expectInstanceCreated(SomeDirective);
|
||||
});
|
||||
|
||||
it('should allow to override a provider', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
const overwrittenValue = {};
|
||||
|
||||
const fixture =
|
||||
TestBed.overrideProvider(SomeDep, {useFactory: () => overwrittenValue, deps: []})
|
||||
.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
|
||||
.createComponent<SomePublicComponent>(SomePublicComponent);
|
||||
|
||||
expect(fixture.componentInstance.dep).toBe(overwrittenValue);
|
||||
});
|
||||
|
||||
it('should allow to override a template', () => {
|
||||
resetTestEnvironmentWithSummaries(summaries);
|
||||
|
||||
TestBed.overrideTemplateUsingTestingModule(SomePublicComponent, 'overwritten');
|
||||
|
||||
const fixture = TestBed.configureTestingModule({providers: [SomeDep], imports: [SomeModule]})
|
||||
.createComponent(SomePublicComponent);
|
||||
expectInstanceCreated(SomePublicComponent);
|
||||
|
||||
expect(fixture.nativeElement).toHaveText('overwritten');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, StaticProvider, Type, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearOverrides as clearOverrides, ɵgetComponentViewDefinitionFactory as getComponentViewDefinitionFactory, ɵoverrideComponentView as overrideComponentView, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
||||
import {ApplicationInitStatus, CompilerOptions, Component, Directive, InjectionToken, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, Optional, Pipe, PlatformRef, Provider, SchemaMetadata, SkipSelf, Type, ɵDepFlags as DepFlags, ɵNodeFlags as NodeFlags, ɵclearProviderOverrides as clearProviderOverrides, ɵoverrideProvider as overrideProvider, ɵstringify as stringify} from '@angular/core';
|
||||
|
||||
import {AsyncTestCompleter} from './async_test_completer';
|
||||
import {ComponentFixture} from './component_fixture';
|
||||
@ -45,7 +45,6 @@ export type TestModuleMetadata = {
|
||||
declarations?: any[],
|
||||
imports?: any[],
|
||||
schemas?: Array<SchemaMetadata|any[]>,
|
||||
aotSummaries?: () => any[],
|
||||
};
|
||||
|
||||
/**
|
||||
@ -142,29 +141,15 @@ export class TestBed implements Injector {
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides the template of the given component, compiling the template
|
||||
* in the context of the TestingModule.
|
||||
*
|
||||
* Note: This works for JIT and AOTed components as well.
|
||||
*/
|
||||
static overrideTemplateUsingTestingModule(component: Type<any>, template: string):
|
||||
typeof TestBed {
|
||||
getTestBed().overrideTemplateUsingTestingModule(component, template);
|
||||
return TestBed;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Overwrites all providers for the given token with the given provider definition.
|
||||
*
|
||||
* Note: This works for JIT and AOTed components as well.
|
||||
*/
|
||||
static overrideProvider(token: any, provider: {
|
||||
useFactory: Function,
|
||||
deps: any[],
|
||||
}): typeof TestBed;
|
||||
static overrideProvider(token: any, provider: {useValue: any;}): typeof TestBed;
|
||||
}): void;
|
||||
static overrideProvider(token: any, provider: {useValue: any;}): void;
|
||||
static overrideProvider(token: any, provider: {
|
||||
useFactory?: Function,
|
||||
useValue?: any,
|
||||
@ -220,9 +205,7 @@ export class TestBed implements Injector {
|
||||
private _schemas: Array<SchemaMetadata|any[]> = [];
|
||||
private _activeFixtures: ComponentFixture<any>[] = [];
|
||||
|
||||
private _testEnvAotSummaries: () => any[] = () => [];
|
||||
private _aotSummaries: Array<() => any[]> = [];
|
||||
private _templateOverrides: Array<{component: Type<any>, templateOf: Type<any>}> = [];
|
||||
private _aotSummaries: () => any[] = () => [];
|
||||
|
||||
platform: PlatformRef = null !;
|
||||
|
||||
@ -249,7 +232,7 @@ export class TestBed implements Injector {
|
||||
this.platform = platform;
|
||||
this.ngModule = ngModule;
|
||||
if (aotSummaries) {
|
||||
this._testEnvAotSummaries = aotSummaries;
|
||||
this._aotSummaries = aotSummaries;
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,13 +245,11 @@ export class TestBed implements Injector {
|
||||
this.resetTestingModule();
|
||||
this.platform = null !;
|
||||
this.ngModule = null !;
|
||||
this._testEnvAotSummaries = () => [];
|
||||
this._aotSummaries = () => [];
|
||||
}
|
||||
|
||||
resetTestingModule() {
|
||||
clearOverrides();
|
||||
this._aotSummaries = [];
|
||||
this._templateOverrides = [];
|
||||
clearProviderOverrides();
|
||||
this._compiler = null !;
|
||||
this._moduleOverrides = [];
|
||||
this._componentOverrides = [];
|
||||
@ -312,9 +293,6 @@ export class TestBed implements Injector {
|
||||
if (moduleDef.schemas) {
|
||||
this._schemas.push(...moduleDef.schemas);
|
||||
}
|
||||
if (moduleDef.aotSummaries) {
|
||||
this._aotSummaries.push(moduleDef.aotSummaries);
|
||||
}
|
||||
}
|
||||
|
||||
compileComponents(): Promise<any> {
|
||||
@ -349,11 +327,6 @@ export class TestBed implements Injector {
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const {component, templateOf} of this._templateOverrides) {
|
||||
const compFactory = this._compiler.getComponentFactory(templateOf);
|
||||
overrideComponentView(component, compFactory);
|
||||
}
|
||||
|
||||
const ngZone = new NgZone({enableLongStackTrace: true});
|
||||
const ngZoneInjector =
|
||||
Injector.create([{provide: NgZone, useValue: ngZone}], this.platform.injector);
|
||||
@ -366,8 +339,7 @@ export class TestBed implements Injector {
|
||||
|
||||
private _createCompilerAndModule(): Type<any> {
|
||||
const providers = this._providers.concat([{provide: TestBed, useValue: this}]);
|
||||
const declarations =
|
||||
[...this._declarations, ...this._templateOverrides.map(entry => entry.templateOf)];
|
||||
const declarations = this._declarations;
|
||||
const imports = [this.ngModule, this._imports];
|
||||
const schemas = this._schemas;
|
||||
|
||||
@ -378,9 +350,7 @@ export class TestBed implements Injector {
|
||||
const compilerFactory: TestingCompilerFactory =
|
||||
this.platform.injector.get(TestingCompilerFactory);
|
||||
this._compiler = compilerFactory.createTestingCompiler(this._compilerOptions);
|
||||
for (const summary of [this._testEnvAotSummaries, ...this._aotSummaries]) {
|
||||
this._compiler.loadAotSummaries(summary);
|
||||
}
|
||||
this._compiler.loadAotSummaries(this._aotSummaries);
|
||||
this._moduleOverrides.forEach((entry) => this._compiler.overrideModule(entry[0], entry[1]));
|
||||
this._componentOverrides.forEach(
|
||||
(entry) => this._compiler.overrideComponent(entry[0], entry[1]));
|
||||
@ -500,16 +470,6 @@ export class TestBed implements Injector {
|
||||
overrideProvider({token, flags, deps, value, deprecatedBehavior: deprecated});
|
||||
}
|
||||
|
||||
overrideTemplateUsingTestingModule(component: Type<any>, template: string) {
|
||||
this._assertNotInstantiated('overrideTemplateUsingTestingModule', 'override template');
|
||||
|
||||
@Component({selector: 'empty', template})
|
||||
class OverrideComponent {
|
||||
}
|
||||
|
||||
this._templateOverrides.push({component, templateOf: OverrideComponent});
|
||||
}
|
||||
|
||||
createComponent<T>(component: Type<T>): ComponentFixture<T> {
|
||||
this._initIfNeeded();
|
||||
const componentFactory = this._compiler.getComponentFactory(component);
|
||||
|
@ -135,7 +135,7 @@ ng1AppModule.component('ng1Hero', {
|
||||
// This AngularJS service will be "upgraded" to be used in Angular
|
||||
ng1AppModule.factory(
|
||||
'titleCase',
|
||||
(() => (value: string) => value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase())) as any);
|
||||
() => (value: string) => value.replace(/((^|\s)[a-z])/g, (_, c) => c.toUpperCase()));
|
||||
// #enddocregion
|
||||
|
||||
// #docregion downgrade-ng2-heroes-service
|
||||
@ -154,20 +154,18 @@ ng1AppModule.component('exampleApp', {
|
||||
// We inject the "downgraded" HeroesService into this AngularJS component
|
||||
// (We don't need the `HeroesService` type for AngularJS DI - it just helps with TypeScript
|
||||
// compilation)
|
||||
controller:
|
||||
[
|
||||
'heroesService',
|
||||
function(heroesService: HeroesService) { this.heroesService = heroesService; }
|
||||
],
|
||||
// This template make use of the downgraded `ng2-heroes` component
|
||||
// Note that because its element is compiled by AngularJS we must use kebab-case attributes
|
||||
// for inputs and outputs
|
||||
template: `<link rel="stylesheet" href="./styles.css">
|
||||
controller: [
|
||||
'heroesService', function(heroesService: HeroesService) { this.heroesService = heroesService; }
|
||||
],
|
||||
// This template make use of the downgraded `ng2-heroes` component
|
||||
// Note that because its element is compiled by AngularJS we must use kebab-case attributes for
|
||||
// inputs and outputs
|
||||
template: `<link rel="stylesheet" href="./styles.css">
|
||||
<ng2-heroes [heroes]="$ctrl.heroesService.heroes" (add-hero)="$ctrl.heroesService.addHero()" (remove-hero)="$ctrl.heroesService.removeHero($event)">
|
||||
<h1>Heroes</h1>
|
||||
<p class="extra">There are {{ $ctrl.heroesService.heroes.length }} heroes.</p>
|
||||
</ng2-heroes>`
|
||||
} as any);
|
||||
});
|
||||
// #enddocregion
|
||||
// #enddocregion
|
||||
|
||||
|
@ -179,13 +179,7 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||
}
|
||||
|
||||
function completionToEntry(c: Completion): ts.CompletionEntry {
|
||||
return {
|
||||
// TODO: remove any and fix type error.
|
||||
kind: c.kind as any,
|
||||
name: c.name,
|
||||
sortText: c.sort,
|
||||
kindModifiers: ''
|
||||
};
|
||||
return {kind: c.kind, name: c.name, sortText: c.sort, kindModifiers: ''};
|
||||
}
|
||||
|
||||
function diagnosticToDiagnostic(d: Diagnostic, file: ts.SourceFile): ts.Diagnostic {
|
||||
@ -300,10 +294,9 @@ export function create(info: any /* ts.server.PluginCreateInfo */): ts.LanguageS
|
||||
fileName: loc.fileName,
|
||||
textSpan: {start: loc.span.start, length: loc.span.end - loc.span.start},
|
||||
name: '',
|
||||
// TODO: remove any and fix type error.
|
||||
kind: 'definition' as any,
|
||||
kind: 'definition',
|
||||
containerName: loc.fileName,
|
||||
containerKind: 'file' as any,
|
||||
containerKind: 'file'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -376,9 +376,6 @@ export class TypeScriptServiceHost implements LanguageServiceHost {
|
||||
if (compilerOptions && compilerOptions.baseUrl) {
|
||||
options.baseUrl = compilerOptions.baseUrl;
|
||||
}
|
||||
if (compilerOptions && compilerOptions.paths) {
|
||||
options.paths = compilerOptions.paths;
|
||||
}
|
||||
result = this._reflectorHost =
|
||||
new ReflectorHost(() => this.tsService.getProgram(), this.host, options);
|
||||
}
|
||||
|
@ -160,33 +160,6 @@ export class MyComponent {
|
||||
|
||||
});
|
||||
|
||||
it('should respect paths configuration', () => {
|
||||
mockHost.overrideOptions(options => {
|
||||
options.baseUrl = '/app';
|
||||
options.paths = {'bar/*': ['foo/bar/*']};
|
||||
return options;
|
||||
});
|
||||
mockHost.addScript('/app/foo/bar/shared.ts', `
|
||||
export interface Node {
|
||||
children: Node[];
|
||||
}
|
||||
`);
|
||||
mockHost.addScript('/app/my.component.ts', `
|
||||
import { Component } from '@angular/core';
|
||||
import { Node } from 'bar/shared';
|
||||
|
||||
@Component({
|
||||
selector: 'my-component',
|
||||
template: '{{tree.~{tree} }}'
|
||||
})
|
||||
export class MyComponent {
|
||||
tree: Node;
|
||||
}
|
||||
`);
|
||||
ngHost.updateAnalyzedModules();
|
||||
contains('/app/my.component.ts', 'tree', 'children');
|
||||
});
|
||||
|
||||
function addCode(code: string, cb: (fileName: string, content?: string) => void) {
|
||||
const fileName = '/app/app.component.ts';
|
||||
const originalContent = mockHost.getFileContent(fileName);
|
||||
|
@ -150,13 +150,8 @@ export const COMPILER_PROVIDERS = <StaticProvider[]>[
|
||||
{ provide: NgModuleResolver, deps: [CompileReflector]},
|
||||
];
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
*/
|
||||
export class JitCompilerFactory implements CompilerFactory {
|
||||
private _defaultOptions: CompilerOptions[];
|
||||
|
||||
/* @internal */
|
||||
constructor(defaultOptions: CompilerOptions[]) {
|
||||
const compilerOptions: CompilerOptions = {
|
||||
useJit: true,
|
||||
|
@ -15,7 +15,6 @@ import {CachedResourceLoader} from './resource_loader/resource_loader_cache';
|
||||
|
||||
export * from './private_export';
|
||||
export {VERSION} from './version';
|
||||
export {JitCompilerFactory} from './compiler_factory';
|
||||
|
||||
/**
|
||||
* @experimental
|
||||
|
@ -712,60 +712,6 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('overrideTemplateUsingTestingModule', () => {
|
||||
it('should compile the template in the context of the testing module', () => {
|
||||
@Component({selector: 'comp', template: 'a'})
|
||||
class MyComponent {
|
||||
prop = 'some prop';
|
||||
}
|
||||
|
||||
let testDir: TestDir|undefined;
|
||||
|
||||
@Directive({selector: '[test]'})
|
||||
class TestDir {
|
||||
constructor() { testDir = this; }
|
||||
|
||||
@Input('test')
|
||||
test: string;
|
||||
}
|
||||
|
||||
TestBed.overrideTemplateUsingTestingModule(
|
||||
MyComponent, '<div [test]="prop">Hello world!</div>');
|
||||
|
||||
const fixture = TestBed.configureTestingModule({declarations: [MyComponent, TestDir]})
|
||||
.createComponent(MyComponent);
|
||||
fixture.detectChanges();
|
||||
expect(fixture.nativeElement).toHaveText('Hello world!');
|
||||
expect(testDir).toBeAnInstanceOf(TestDir);
|
||||
expect(testDir !.test).toBe('some prop');
|
||||
});
|
||||
|
||||
it('should throw if the TestBed is already created', () => {
|
||||
@Component({selector: 'comp', template: 'a'})
|
||||
class MyComponent {
|
||||
}
|
||||
|
||||
TestBed.get(Injector);
|
||||
|
||||
expect(() => TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b'))
|
||||
.toThrowError(
|
||||
/Cannot override template when the test module has already been instantiated/);
|
||||
});
|
||||
|
||||
it('should reset overrides when the testing module is resetted', () => {
|
||||
@Component({selector: 'comp', template: 'a'})
|
||||
class MyComponent {
|
||||
}
|
||||
|
||||
TestBed.overrideTemplateUsingTestingModule(MyComponent, 'b');
|
||||
|
||||
const fixture = TestBed.resetTestingModule()
|
||||
.configureTestingModule({declarations: [MyComponent]})
|
||||
.createComponent(MyComponent);
|
||||
expect(fixture.nativeElement).toHaveText('a');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setting up the compiler', () => {
|
||||
|
||||
describe('providers', () => {
|
||||
|
@ -592,7 +592,7 @@ export function main() {
|
||||
platform.bootstrapModule(ExampleModule).then(ref => {
|
||||
const mock = ref.injector.get(MockBackend);
|
||||
const http = ref.injector.get(Http);
|
||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
||||
ref.injector.get(NgZone).run(() => {
|
||||
NgZone.assertInAngularZone();
|
||||
mock.connections.subscribe((mc: MockConnection) => {
|
||||
NgZone.assertInAngularZone();
|
||||
@ -612,11 +612,11 @@ export function main() {
|
||||
platform.bootstrapModule(ExampleModule).then(ref => {
|
||||
const mock = ref.injector.get(MockBackend);
|
||||
const http = ref.injector.get(Http);
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
ref.injector.get(NgZone).run(() => {
|
||||
NgZone.assertInAngularZone();
|
||||
mock.connections.subscribe((mc: MockConnection) => {
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
||||
});
|
||||
http.get('http://localhost/testing').subscribe(resp => {
|
||||
@ -631,11 +631,11 @@ export function main() {
|
||||
platform.bootstrapModule(HttpBeforeExampleModule).then(ref => {
|
||||
const mock = ref.injector.get(MockBackend);
|
||||
const http = ref.injector.get(Http);
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
ref.injector.get(NgZone).run(() => {
|
||||
NgZone.assertInAngularZone();
|
||||
mock.connections.subscribe((mc: MockConnection) => {
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
||||
});
|
||||
http.get('http://localhost/testing').subscribe(resp => {
|
||||
@ -650,11 +650,11 @@ export function main() {
|
||||
platform.bootstrapModule(HttpAfterExampleModule).then(ref => {
|
||||
const mock = ref.injector.get(MockBackend);
|
||||
const http = ref.injector.get(Http);
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
ref.injector.get(NgZone).run(() => {
|
||||
NgZone.assertInAngularZone();
|
||||
mock.connections.subscribe((mc: MockConnection) => {
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
mc.mockRespond(new Response(new ResponseOptions({body: 'success!', status: 200})));
|
||||
});
|
||||
http.get('http://localhost/testing').subscribe(resp => {
|
||||
@ -688,7 +688,7 @@ export function main() {
|
||||
platform.bootstrapModule(HttpClientExmapleModule).then(ref => {
|
||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
||||
const http = ref.injector.get(HttpClient);
|
||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
||||
ref.injector.get(NgZone).run(() => {
|
||||
http.get('http://localhost/testing').subscribe(body => {
|
||||
NgZone.assertInAngularZone();
|
||||
expect(body).toEqual('success!');
|
||||
@ -703,13 +703,13 @@ export function main() {
|
||||
platform.bootstrapModule(HttpClientExmapleModule).then(ref => {
|
||||
const mock = ref.injector.get(HttpTestingController) as HttpTestingController;
|
||||
const http = ref.injector.get(HttpClient);
|
||||
ref.injector.get<NgZone>(NgZone).run(() => {
|
||||
ref.injector.get(NgZone).run(() => {
|
||||
http.get('http://localhost/testing').subscribe(body => {
|
||||
expect(body).toEqual('success!');
|
||||
});
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeTruthy();
|
||||
mock.expectOne('http://localhost/testing').flush('success!');
|
||||
expect(ref.injector.get<NgZone>(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
expect(ref.injector.get(NgZone).hasPendingMacrotasks).toBeFalsy();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
@ -32,7 +32,7 @@ export const WORKER_UI_LOCATION_PROVIDERS = <StaticProvider[]>[
|
||||
|
||||
function initUiLocation(injector: Injector): () => void {
|
||||
return () => {
|
||||
const zone = injector.get<NgZone>(NgZone);
|
||||
const zone = injector.get(NgZone);
|
||||
|
||||
zone.runGuarded(() => injector.get(MessageBasedPlatformLocation).start());
|
||||
};
|
||||
|
@ -112,7 +112,7 @@ export const _WORKER_UI_PLATFORM_PROVIDERS: StaticProvider[] = [
|
||||
|
||||
function initializeGenericWorkerRenderer(injector: Injector) {
|
||||
const bus = injector.get(MessageBus);
|
||||
const zone = injector.get<NgZone>(NgZone);
|
||||
const zone = injector.get(NgZone);
|
||||
bus.attachToZone(zone);
|
||||
|
||||
// initialize message services after the bus has been created
|
||||
|
@ -43,12 +43,12 @@ declare let Zone: any;
|
||||
*/
|
||||
export interface NavigationExtras {
|
||||
/**
|
||||
* Enables relative navigation from the current ActivatedRoute.
|
||||
*
|
||||
* Configuration:
|
||||
*
|
||||
* ```
|
||||
* [{
|
||||
* Enables relative navigation from the current ActivatedRoute.
|
||||
*
|
||||
* Configuration:
|
||||
*
|
||||
* ```
|
||||
* [{
|
||||
* path: 'parent',
|
||||
* component: ParentComponent,
|
||||
* children: [{
|
||||
@ -59,92 +59,92 @@ export interface NavigationExtras {
|
||||
* component: ChildComponent
|
||||
* }]
|
||||
* }]
|
||||
* ```
|
||||
*
|
||||
* Navigate to list route from child route:
|
||||
*
|
||||
* ```
|
||||
* @Component({...})
|
||||
* class ChildComponent {
|
||||
* ```
|
||||
*
|
||||
* Navigate to list route from child route:
|
||||
*
|
||||
* ```
|
||||
* @Component({...})
|
||||
* class ChildComponent {
|
||||
* constructor(private router: Router, private route: ActivatedRoute) {}
|
||||
*
|
||||
* go() {
|
||||
* this.router.navigate(['../list'], { relativeTo: this.route });
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
* ```
|
||||
*/
|
||||
relativeTo?: ActivatedRoute|null;
|
||||
|
||||
/**
|
||||
* Sets query parameters to the URL.
|
||||
*
|
||||
* ```
|
||||
* // Navigate to /results?page=1
|
||||
* this.router.navigate(['/results'], { queryParams: { page: 1 } });
|
||||
* ```
|
||||
*/
|
||||
* Sets query parameters to the URL.
|
||||
*
|
||||
* ```
|
||||
* // Navigate to /results?page=1
|
||||
* this.router.navigate(['/results'], { queryParams: { page: 1 } });
|
||||
* ```
|
||||
*/
|
||||
queryParams?: Params|null;
|
||||
|
||||
/**
|
||||
* Sets the hash fragment for the URL.
|
||||
*
|
||||
* ```
|
||||
* // Navigate to /results#top
|
||||
* this.router.navigate(['/results'], { fragment: 'top' });
|
||||
* ```
|
||||
*/
|
||||
* Sets the hash fragment for the URL.
|
||||
*
|
||||
* ```
|
||||
* // Navigate to /results#top
|
||||
* this.router.navigate(['/results'], { fragment: 'top' });
|
||||
* ```
|
||||
*/
|
||||
fragment?: string;
|
||||
|
||||
/**
|
||||
* Preserves the query parameters for the next navigation.
|
||||
*
|
||||
* deprecated, use `queryParamsHandling` instead
|
||||
*
|
||||
* ```
|
||||
* // Preserve query params from /results?page=1 to /view?page=1
|
||||
* this.router.navigate(['/view'], { preserveQueryParams: true });
|
||||
* ```
|
||||
*
|
||||
* @deprecated since v4
|
||||
*/
|
||||
* Preserves the query parameters for the next navigation.
|
||||
*
|
||||
* deprecated, use `queryParamsHandling` instead
|
||||
*
|
||||
* ```
|
||||
* // Preserve query params from /results?page=1 to /view?page=1
|
||||
* this.router.navigate(['/view'], { preserveQueryParams: true });
|
||||
* ```
|
||||
*
|
||||
* @deprecated since v4
|
||||
*/
|
||||
preserveQueryParams?: boolean;
|
||||
|
||||
/**
|
||||
* config strategy to handle the query parameters for the next navigation.
|
||||
*
|
||||
* ```
|
||||
* // from /results?page=1 to /view?page=1&page=2
|
||||
* this.router.navigate(['/view'], { queryParams: { page: 2 }, queryParamsHandling: "merge" });
|
||||
* ```
|
||||
*/
|
||||
* config strategy to handle the query parameters for the next navigation.
|
||||
*
|
||||
* ```
|
||||
* // from /results?page=1 to /view?page=1&page=2
|
||||
* this.router.navigate(['/view'], { queryParams: { page: 2 }, queryParamsHandling: "merge" });
|
||||
* ```
|
||||
*/
|
||||
queryParamsHandling?: QueryParamsHandling|null;
|
||||
/**
|
||||
* Preserves the fragment for the next navigation
|
||||
*
|
||||
* ```
|
||||
* // Preserve fragment from /results#top to /view#top
|
||||
* this.router.navigate(['/view'], { preserveFragment: true });
|
||||
* ```
|
||||
*/
|
||||
* Preserves the fragment for the next navigation
|
||||
*
|
||||
* ```
|
||||
* // Preserve fragment from /results#top to /view#top
|
||||
* this.router.navigate(['/view'], { preserveFragment: true });
|
||||
* ```
|
||||
*/
|
||||
preserveFragment?: boolean;
|
||||
/**
|
||||
* Navigates without pushing a new state into history.
|
||||
*
|
||||
* ```
|
||||
* // Navigate silently to /view
|
||||
* this.router.navigate(['/view'], { skipLocationChange: true });
|
||||
* ```
|
||||
*/
|
||||
* Navigates without pushing a new state into history.
|
||||
*
|
||||
* ```
|
||||
* // Navigate silently to /view
|
||||
* this.router.navigate(['/view'], { skipLocationChange: true });
|
||||
* ```
|
||||
*/
|
||||
skipLocationChange?: boolean;
|
||||
/**
|
||||
* Navigates while replacing the current state in history.
|
||||
*
|
||||
* ```
|
||||
* // Navigate to /view
|
||||
* this.router.navigate(['/view'], { replaceUrl: true });
|
||||
* ```
|
||||
*/
|
||||
* Navigates while replacing the current state in history.
|
||||
*
|
||||
* ```
|
||||
* // Navigate to /view
|
||||
* this.router.navigate(['/view'], { replaceUrl: true });
|
||||
* ```
|
||||
*/
|
||||
replaceUrl?: boolean;
|
||||
}
|
||||
|
||||
@ -241,14 +241,6 @@ export class Router {
|
||||
|
||||
routeReuseStrategy: RouteReuseStrategy = new DefaultRouteReuseStrategy();
|
||||
|
||||
/**
|
||||
* Define what the router should do if it receives a navigation request to the current URL.
|
||||
* By default, the router will ignore this navigation. However, this prevents features such
|
||||
* as a "refresh" button. Use this option to configure the behavior when navigating to the
|
||||
* current URL. Default is 'ignore'.
|
||||
*/
|
||||
onSameUrlNavigation: 'reload'|'ignore' = 'ignore';
|
||||
|
||||
/**
|
||||
* Creates the router service.
|
||||
*/
|
||||
@ -526,18 +518,11 @@ export class Router {
|
||||
|
||||
// Because of a bug in IE and Edge, the location class fires two events (popstate and
|
||||
// hashchange) every single time. The second one should be ignored. Otherwise, the URL will
|
||||
// flicker. Handles the case when a popstate was emitted first.
|
||||
// flicker.
|
||||
if (lastNavigation && source == 'hashchange' && lastNavigation.source === 'popstate' &&
|
||||
lastNavigation.rawUrl.toString() === rawUrl.toString()) {
|
||||
return Promise.resolve(true); // return value is not used
|
||||
}
|
||||
// Because of a bug in IE and Edge, the location class fires two events (popstate and
|
||||
// hashchange) every single time. The second one should be ignored. Otherwise, the URL will
|
||||
// flicker. Handles the case when a hashchange was emitted first.
|
||||
if (lastNavigation && source == 'popstate' && lastNavigation.source === 'hashchange' &&
|
||||
lastNavigation.rawUrl.toString() === rawUrl.toString()) {
|
||||
return Promise.resolve(true); // return value is not used
|
||||
}
|
||||
|
||||
let resolve: any = null;
|
||||
let reject: any = null;
|
||||
@ -560,8 +545,7 @@ export class Router {
|
||||
const url = this.urlHandlingStrategy.extract(rawUrl);
|
||||
const urlTransition = !this.navigated || url.toString() !== this.currentUrlTree.toString();
|
||||
|
||||
if ((this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
|
||||
this.urlHandlingStrategy.shouldProcessUrl(rawUrl)) {
|
||||
if (urlTransition && this.urlHandlingStrategy.shouldProcessUrl(rawUrl)) {
|
||||
(this.events as Subject<Event>).next(new NavigationStart(id, this.serializeUrl(url)));
|
||||
Promise.resolve()
|
||||
.then(
|
||||
@ -589,9 +573,10 @@ export class Router {
|
||||
}
|
||||
|
||||
private runNavigate(
|
||||
url: UrlTree, rawUrl: UrlTree, skipLocationChange: boolean, replaceUrl: boolean, id: number,
|
||||
precreatedState: RouterStateSnapshot|null): Promise<boolean> {
|
||||
url: UrlTree, rawUrl: UrlTree, shouldPreventPushState: boolean, shouldReplaceUrl: boolean,
|
||||
id: number, precreatedState: RouterStateSnapshot|null): Promise<boolean> {
|
||||
if (id !== this.navigationId) {
|
||||
this.location.go(this.urlSerializer.serialize(this.currentUrlTree));
|
||||
(this.events as Subject<Event>)
|
||||
.next(new NavigationCancel(
|
||||
id, this.serializeUrl(url),
|
||||
@ -713,9 +698,9 @@ export class Router {
|
||||
|
||||
(this as{routerState: RouterState}).routerState = state;
|
||||
|
||||
if (!skipLocationChange) {
|
||||
if (!shouldPreventPushState) {
|
||||
const path = this.urlSerializer.serialize(this.rawUrlTree);
|
||||
if (this.location.isCurrentPathEqualTo(path) || replaceUrl) {
|
||||
if (this.location.isCurrentPathEqualTo(path) || shouldReplaceUrl) {
|
||||
this.location.replaceState(path);
|
||||
} else {
|
||||
this.location.go(path);
|
||||
@ -763,13 +748,14 @@ export class Router {
|
||||
(this as{routerState: RouterState}).routerState = storedState;
|
||||
this.currentUrlTree = storedUrl;
|
||||
this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl);
|
||||
this.resetUrlToCurrentUrlTree();
|
||||
this.location.replaceState(this.serializeUrl(this.rawUrlTree));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private resetUrlToCurrentUrlTree(): void {
|
||||
this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree));
|
||||
const path = this.urlSerializer.serialize(this.rawUrlTree);
|
||||
this.location.replaceState(path);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,15 +129,12 @@ export class RouterModule {
|
||||
* Creates a module with all the router providers and directives. It also optionally sets up an
|
||||
* application listener to perform an initial navigation.
|
||||
*
|
||||
* Options (see {@link ExtraOptions}):
|
||||
* Options:
|
||||
* * `enableTracing` makes the router log all its internal events to the console.
|
||||
* * `useHash` enables the location strategy that uses the URL fragment instead of the history
|
||||
* API.
|
||||
* * `initialNavigation` disables the initial navigation.
|
||||
* * `errorHandler` provides a custom error handler.
|
||||
* * `preloadingStrategy` configures a preloading strategy (see {@link PreloadAllModules}).
|
||||
* * `onSameUrlNavigation` configures how the router handles navigation to the current URL. See
|
||||
* {@link ExtraOptions} for more details.
|
||||
*/
|
||||
static forRoot(routes: Routes, config?: ExtraOptions): ModuleWithProviders {
|
||||
return {
|
||||
@ -270,14 +267,6 @@ export interface ExtraOptions {
|
||||
* Configures a preloading strategy. See {@link PreloadAllModules}.
|
||||
*/
|
||||
preloadingStrategy?: any;
|
||||
|
||||
/**
|
||||
* Define what the router should do if it receives a navigation request to the current URL.
|
||||
* By default, the router will ignore this navigation. However, this prevents features such
|
||||
* as a "refresh" button. Use this option to configure the behavior when navigating to the
|
||||
* current URL. Default is 'ignore'.
|
||||
*/
|
||||
onSameUrlNavigation?: 'reload'|'ignore';
|
||||
}
|
||||
|
||||
export function setupRouter(
|
||||
@ -310,10 +299,6 @@ export function setupRouter(
|
||||
});
|
||||
}
|
||||
|
||||
if (opts.onSameUrlNavigation) {
|
||||
router.onSameUrlNavigation = opts.onSameUrlNavigation;
|
||||
}
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
@ -381,7 +366,7 @@ export class RouterInitializer {
|
||||
const opts = this.injector.get(ROUTER_CONFIGURATION);
|
||||
const preloader = this.injector.get(RouterPreloader);
|
||||
const router = this.injector.get(Router);
|
||||
const ref = this.injector.get<ApplicationRef>(ApplicationRef);
|
||||
const ref = this.injector.get(ApplicationRef);
|
||||
|
||||
if (bootstrappedComponentRef !== ref.components[0]) {
|
||||
return;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user