Compare commits

..

89 Commits

Author SHA1 Message Date
6efdf84d3e docs(changelog): add changelog for 2.4.0 2016-12-19 17:42:17 -08:00
e61bfc8b24 chore(release): cut the 2.4.0 release 2016-12-19 17:19:25 -08:00
070f9d0644 refactor: formatting fixes 2016-12-19 17:18:33 -08:00
8d5da1e57a feat: update to rxjs@5.0.1 and unpin the rxjs peerDeps via ^5.0.1 (#13572)
Now that rxjs is stable and the rxjs team follows semver, we can update and unpin the dependency safely.

From now on the Angular application/library developers are in charge of controlling the rxjs version as long as it's newer than 5.0.1.

closes #13561
closes #13478
closes #13572
2016-12-19 17:09:41 -08:00
b6406191c7 build(npm): update angular version in shrinkwrap files 2016-12-19 17:09:41 -08:00
124face441 refactor(compiler-cli): support extracting the mesage bundle without writing a file (#13580) 2016-12-19 17:09:41 -08:00
de4ace77fe feat(compiler-cli): private i18n API for the CLI (#13536)
Also change the Extractor API to align with the Codegen API (internal APIs)
2016-12-19 17:09:41 -08:00
debb0c9798 fix(compiler-cli): produce metadata for .d.ts files without metadata (#13526)
Fixes #13307
Fixes #13473
Fixes #13521
2016-12-19 17:09:40 -08:00
9b87bb6d7f fix(compiler): do not lex }} when interpolation is disabled (#13531)
* doc(compiler): fix the ICU expander API docs

* test(compiler): add lexer and parser specs

* fix(compiler): do not lex `}}` when interpolation is disabled

fix #13525
2016-12-19 17:09:40 -08:00
71e88a8c3c refactor(core): fix typo (#13515)
Closes #13512
2016-12-19 17:09:40 -08:00
c26c24c544 fix(upgrade): fix registerForNg1Tests (#13522)
Fix an issue in `registerForNg1Tests`, where it passes a `null` as
`ng1Injector` to `_bootstrapDone`. This causes a "TypeError: Cannot
read property 'get' of null" to be thrown from `_bootstrapDone`.
2016-12-19 17:09:40 -08:00
3f178410c3 fix(i18n): add a default example to xmb placeholders (#13507)
Otherwise the TC would not be able to load the message
2016-12-19 17:09:40 -08:00
b36f4bc00d fix(animations): allow players to be destroyed before initialized (#13346)
Closes #13293
Closes #13346
2016-12-19 17:09:40 -08:00
355c537883 refactor(compiler): format update (#13506) 2016-12-19 17:09:39 -08:00
f277303ca3 refactor(compiler): don't print stack trace on template parse errors (#13390) 2016-12-19 17:09:39 -08:00
50afbe094f fix(build): use bash string comparison operator (#13502) 2016-12-19 17:09:39 -08:00
15ea758d01 feature(DEVELOPER.md): add easy way to publish personal snapshot builds (#13469) 2016-12-19 17:09:39 -08:00
1f0f429f2a refactor(compiler): store metadata of top level symbols also in summaries (#13289)
This allows a build using summaries to not need .metadata.json files at all
any more.

Part of #12787
2016-12-19 17:09:39 -08:00
dbb364e23a docs(changelog): minor updates to 2.3.1 changelog 2016-12-14 21:57:56 -08:00
540b1197a6 fix(form): fix merge errors 2016-12-14 18:22:03 -08:00
d30cc8461b docs(changelog): add changelog for 2.3.1 2016-12-14 18:14:34 -08:00
f27954e62c build: bump angular to 2.3.1 & tsc-wrapped to 0.5.0 2016-12-14 18:11:35 -08:00
69b52eb2b3 fix(compiler): fix merge error in compiler_host 2016-12-14 18:08:54 -08:00
b9b557cdb0 fix(compiler): update to metadata version 3 (#13464)
This change retracts support for metadata version 2.

The collector used to produce version 2 metadata was incomplete
and can cause the AOT compiler to fail to resolve symbols or
produce other spurious errors.

All libraries compiled and published with 2.3.0 ngc will need
to be recompiled and updated with this change.
2016-12-14 18:08:48 -08:00
a72a002a8d refactor: format & lint 2016-12-14 18:08:43 -08:00
a0437f8c9d chore(animations/aot): always export NoOpAnimationDriver (#13480) 2016-12-14 18:08:36 -08:00
1c279b3264 fix(compiler): fix simplify a reference without a name
closes #13470
2016-12-14 18:08:32 -08:00
cd03c77364 fix(tsc-wrapped): generate metadata for exports without module specifier
fixes #13327
2016-12-14 18:08:29 -08:00
f6ef7d6e5a fix(compiler): propagate exports when upgrading metadata to v2 2016-12-14 18:08:25 -08:00
6aeaca3fb4 fix(compiler): resolver should merge host bindings and listeners (#13474)
fixes #13327
2016-12-14 18:07:41 -08:00
af62050729 docs(upgrade): fix UpgradeAdapter examples
closes #12675
2016-12-14 18:02:26 -08:00
cb69656b56 docs(upgrade/upgrade_adapter): fix up references to AngularJS and Angular 2 2016-12-14 18:02:14 -08:00
2fc0560988 feat(upgrade): enable Angular 1 unit testing of upgrade module
- New method `UpgradeAdapter.registerForNg1Tests(modules)` declares the
  Angular 1 upgrade module and provides it to the `angular.mock.module()`
  helper.
  This prevents the need to bootstrap the entire hybrid for every test.

Closes #5462, #12675
2016-12-14 18:02:05 -08:00
86c50983d7 fix(upgrade): fix downgrade content projection and injector inheritance
- Full support for content projection in downgraded Angular 2
  components. In particular, this enables multi-slot projection and
  other features on <ng-content>.
- Correctly wire up hierarchical injectors for downgraded Angular 2
  components: downgraded components inherit the injector of the first
  other downgraded Angular 2 component they find up the DOM tree.

Closes #6629, #7727, #8729, #9643, #9649, #12675
2016-12-14 17:56:20 -08:00
21976446e0 refactor(upgrade/upgrade_adapter): use Deferred helper
Making Angular 1's `$compile` asynchronous by chaining injector promises
in linking functions can cause flickering views in applications.
2016-12-14 17:56:16 -08:00
998ce9ad7e refactor(upgrade/util): remove unused stringify() method 2016-12-14 17:56:11 -08:00
111523677c refactor(compiler/template_parser): export createElementCssSelector
This is needed in `ngUpgrade`.
2016-12-14 17:56:06 -08:00
2d74a224d0 refactor(upgrade): add missing Angular 1 type info 2016-12-14 17:55:56 -08:00
4d6ac9d414 fix(core): detectChanges() doesn't work on detached instance
Closes #13426
Closes #13472
2016-12-14 17:55:38 -08:00
6557bc34f6 fix(animations): throw errors and normalize offset beyond the range of [0,1]
Closes #13348
Closes #13440
2016-12-14 17:55:34 -08:00
e2622add07 perf(animations): always run the animation queue outside of zones
Related #12732
Closes #13440
2016-12-14 17:55:27 -08:00
ecfad467a1 fix(compiler): emit quoted object literal keys if the source is quoted
feat(tsc-wrapped): recored when to quote a object literal key

Collecting quoted literals is off by default as it introduces
a breaking change in the .metadata.json file. A follow-up commit
will address this.

Fixes #13249
Closes #13356
2016-12-14 17:55:22 -08:00
5918133784 Revert "fix(compiler): xmb <ph> tags should not self close (#13413)"
This reverts commit 4b3d135193.
closes #13463
2016-12-14 17:55:18 -08:00
700bce9ec1 Revert "test(i18n): fix a typo in the reference xmb (#13441)"
This reverts commit a8d237581d.
2016-12-14 17:55:14 -08:00
a64a35a8c1 refactor(facade): don't expect super() to return a new Error object in BaseError (#12600)
Related to #12575
2016-12-14 17:55:10 -08:00
b3dcff0cc1 fix(forms): ensure select[multiple] retains selections
If you bound an array to select[multiple] via ngModel and subsequently
changed the options to select from, the UI would drop any selections
made since by the user. This was due to
SelectMultipleControlValueAccessor not keeping a reference to the new
model arrays it generated when users interacted with the select control.
Update code to keep the reference.

Closes #12527
Closes #12654
2016-12-14 17:55:02 -08:00
124267c87a fix(forms): introduce checkbox required validator
Closes #11459
Closes #13364
2016-12-14 17:52:53 -08:00
547bfa92ef fix (forms): clear selected options when model is not an array (#12519)
When an invalid model value (eg empty string) was preset ngModel on
select[multiple] would throw an error, which is inconsistent with how it
works on other user input elements. Setting the model value to null or
undefined would also have no effect on what was already selected in the
UI. Fix this by clearing selected options when model set to null,
undefined or a type other than Array.

Closes #11926
2016-12-14 17:52:02 -08:00
d40bbf4d5c fix(core): properly destroy embedded Views attatched to ApplicationRef (#13459)
Fixes #13062
2016-12-14 17:51:56 -08:00
94b7031fe9 refactor: format & lint 2016-12-14 17:51:48 -08:00
df0bf1dd74 chore(internal API): introduce an internal API for ngtools. (#13415) 2016-12-14 17:51:40 -08:00
c8a9b70890 fix(compiler): generated CSS files suffixed with ngstyle. (#13353)
Mirrors factories which ends in `ngfactory`.

Closes #13141.
2016-12-14 17:50:29 -08:00
efa2d80df8 fix(compiler): make sure provider values with name property don’t break.
Fixes #13394
Closes #13445
2016-12-14 17:50:22 -08:00
a58e5efd09 test(i18n): fix a typo in the reference xmb (#13441) 2016-12-14 17:50:12 -08:00
86cf0ef892 refactor: remove intl from facades (#13404)
The existing intl.ts file is not a facade but
rather a set of utils used by i18n-related pipes only.
As such moving it back to common module so those utils
are not used accidently from other places.
2016-12-14 17:50:02 -08:00
5c568fab86 test(upgrade): fix failing test in browsers which do not support RAF
closes #13399
2016-12-14 17:49:52 -08:00
566104504c ci(browser providers): update browsers in SL and BS (#13431) 2016-12-14 17:49:37 -08:00
307d305b2d fix(compiler): narrow the span reported for invalid pipes
fixes #13326
closes #13411
2016-12-14 17:49:05 -08:00
0a7364feea fix(language-service): correctly type undefined
fixes #13412
closes #13414
2016-12-14 17:40:58 -08:00
4544b1d7a6 fix(compiler): xmb <ph> tags should not self close (#13413) 2016-12-14 17:39:51 -08:00
9e0e6b59d1 docs(core): update OnDestroy description (#13369)
Closes #11228
2016-12-14 17:39:45 -08:00
14dd2b367a fix(language-service): treat string unions as strings (#13406)
Fixes #13403
2016-12-14 17:39:36 -08:00
91eb8914dd build: update the package list of the symlinks scripts for Windows (#13408) 2016-12-14 17:39:30 -08:00
77823d721f refactor: format and lint code 2016-12-14 17:38:31 -08:00
2afe2d107f docs(Location): updating Location docs and adding example
closes #11500
2016-12-14 17:38:22 -08:00
17f40fb75f chore: Add @types/systemjs 2016-12-14 17:37:43 -08:00
98936fdf16 chore: convert hash_location_strategy example to a tested spec 2016-12-14 17:37:33 -08:00
7383e4a801 fix(forms): fix Validators.min/maxLength with FormArray (#13095)
Fixes #13089
2016-12-14 17:37:18 -08:00
65c9b5b6aa fix(http): create a copy of headers when merge options (#13365)
Closes #11980
2016-12-14 17:36:48 -08:00
5fab8710cb fix(dom_adapter): remove logError from logGroup (#12925) 2016-12-14 17:35:12 -08:00
f106a18b96 fix(http): check response body text against undefined (#13017) 2016-12-14 17:35:05 -08:00
8db184d349 fix(compiler): support dotted property binding
fixes angular/flex-layout#34
2016-12-14 17:31:08 -08:00
c18eb298eb test(Selector): add a test for dotted attribute names 2016-12-14 17:31:00 -08:00
3f4aa59cfa refactor(Compiler): cleanup 2016-12-14 17:30:50 -08:00
79728b4c41 fix(compiler): fix PR 13322 (#13331) 2016-12-14 17:30:40 -08:00
413167ab1b style: clang-format the code 2016-12-14 17:26:52 -08:00
203cc7e1f1 fix: Better instructions on running examples and their tests 2016-12-14 17:23:11 -08:00
b0cd514709 fix: Better error when directive not listed in NgModule.declarations 2016-12-14 17:22:58 -08:00
392c9ac214 fix(selector): SelectorMatcher match elements with :not selector (#12977) 2016-12-14 17:21:34 -08:00
a26e054857 fix(animations): always cleanup players after they have finished internally (#13334)
Closes #13333
Closes #13334
2016-12-14 17:21:23 -08:00
c0b001a6af refactor(router): misc refactoring (#13330) 2016-12-14 17:21:09 -08:00
c8c1f22f9c refactor(router): simplify regexp
closes #11373
closes #13329
2016-12-14 17:20:47 -08:00
e4d5a5f003 fix(router): add support for query params with mulitple values
closes #11373
2016-12-14 17:20:22 -08:00
03d9de33a1 Revert "fix(compiler): fix transpiled ES5 code (#13322)"
This reverts commit 4398056146.
2016-12-14 17:20:14 -08:00
a8a80cf523 doc: update triage owners for language service and router (#13325) 2016-12-14 17:19:50 -08:00
6c1d7908d5 fix(compiler): fix transpiled ES5 code (#13322)
fixes #13301

The inner class would transpile to a nested function declaration which is not
allowed in ES5.

See http://eslint.org/docs/rules/no-inner-declarations
2016-12-14 17:18:12 -08:00
9aab6d24eb build(tslint): enable no-inner-declarations (#13316) 2016-12-14 17:18:02 -08:00
5ee8155e4e fix(router): Use T type in Resolve interface (#13242) 2016-12-14 17:17:45 -08:00
21de0f239d docs(changelog): fix a typo (#13298) 2016-12-14 17:16:49 -08:00
340 changed files with 3487 additions and 8156 deletions

View File

@ -1,32 +0,0 @@
# Configuration for pullapprove.com
# See ownership spreadsheet:
# https://docs.google.com/spreadsheets/d/1-HIlzfbPYGsPr9KuYMe6bLfc4LXzPjpoALqtYRYTZB0/edit?pli=1#gid=0&vpid=A5
version: 2
group_defaults:
required: 1
reset_on_reopened:
enabled: true
approve_by_comment:
enabled: true
approve_regex: '^(Approved|:\+1:|LGTM)'
groups:
config:
conditions:
files:
- "*.yml"
- "*.json"
teams:
- repoowners
compiler:
conditions:
files:
- "tools/@angular/tsc-wrapped/*"
- "modules/@angular/compiler/*"
- "modules/@angular/compiler-cli/*"
teams:
- compiler-owners
- repoowners

View File

@ -1,216 +1,3 @@
<a name="4.0.0-beta.4"></a>
# [4.0.0-beta.4](https://github.com/angular/angular/compare/4.0.0-beta.3...4.0.0-beta.4) (2017-01-19)
### Bug Fixes
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([c2aa981](https://github.com/angular/angular/commit/c2aa981))
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([f364557](https://github.com/angular/angular/commit/f364557))
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([1200cf2](https://github.com/angular/angular/commit/1200cf2)), closes [#13973](https://github.com/angular/angular/issues/13973)
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([635bf02](https://github.com/angular/angular/commit/635bf02)), closes [#11075](https://github.com/angular/angular/issues/11075)
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([e8ea741](https://github.com/angular/angular/commit/e8ea741)), closes [#13270](https://github.com/angular/angular/issues/13270)
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([e19bf70](https://github.com/angular/angular/commit/e19bf70))
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([d6382bf](https://github.com/angular/angular/commit/d6382bf)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
### Features
* **build:** optionally build an ES2015 distro ([#13471](https://github.com/angular/angular/issues/13471)) ([be6c95a](https://github.com/angular/angular/commit/be6c95a))
* **core:** Add type information to injector.get() ([#13785](https://github.com/angular/angular/issues/13785)) ([d169c24](https://github.com/angular/angular/commit/d169c24))
### BREAKING CHANGES
* core: - Because `injector.get()` is now parameterize it is possible that code
which used to work no longer type checks. Example would be if one
injects `Foo` but configures it as `{provide: Foo, useClass: MockFoo}`.
The injection instance will be that of `MockFoo` but the type will be
`Foo` instead of `any` as in the past. This means that it was possible
to call a method on `MockFoo` in the past which now will fail type
check. See this example:
```
class Foo {}
class MockFoo extends Foo {
setupMock();
}
var PROVIDERS = [
{provide: Foo, useClass: MockFoo}
];
...
function myTest(injector: Injector) {
var foo = injector.get(Foo);
// This line used to work since `foo` used to be `any` before this
// change, it will now be `Foo`, and `Foo` does not have `setUpMock()`.
// The fix is to downcast: `injector.get(Foo) as MockFoo`.
foo.setUpMock();
}
```
<a name="2.4.4"></a>
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
* **animations:** fix internal jscompiler issue and AOT quoting ([#13798](https://github.com/angular/angular/issues/13798)) ([261fd16](https://github.com/angular/angular/commit/261fd16))
* **common:** support numeric value as discrete cases for NgPlural ([#13876](https://github.com/angular/angular/issues/13876)) ([3d0b1b8](https://github.com/angular/angular/commit/3d0b1b8))
* **http:** don't create a blob out of ArrayBuffer when type is application/octet-stream ([#13992](https://github.com/angular/angular/issues/13992)) ([015878a](https://github.com/angular/angular/commit/015878a)), closes [#13973](https://github.com/angular/angular/issues/13973)
* **router:** enable loadChildren with function in aot ([#13909](https://github.com/angular/angular/issues/13909)) ([2af5862](https://github.com/angular/angular/commit/2af5862)), closes [#11075](https://github.com/angular/angular/issues/11075)
* **router:** routerLinkActive should not throw when not initialized ([#13273](https://github.com/angular/angular/issues/13273)) ([49c4b0f](https://github.com/angular/angular/commit/49c4b0f)), closes [#13270](https://github.com/angular/angular/issues/13270)
* **security:** allow calc and gradient functions. ([#13943](https://github.com/angular/angular/issues/13943)) ([bd15110](https://github.com/angular/angular/commit/bd15110))
* **upgrade:** detect async downgrade component changes ([#13812](https://github.com/angular/angular/issues/13812)) ([2250082](https://github.com/angular/angular/commit/2250082)), closes [#6385](https://github.com/angular/angular/issues/6385) [#6385](https://github.com/angular/angular/issues/6385) [#10660](https://github.com/angular/angular/issues/10660) [#12318](https://github.com/angular/angular/issues/12318) [#12034](https://github.com/angular/angular/issues/12034)
<a name="4.0.0-beta.3"></a>
# [4.0.0-beta.3](https://github.com/angular/angular/compare/4.0.0-beta.2...4.0.0-beta.3) (2017-01-11)
### Bug Fixes
* **compiler:** avoid evaluating arguments to unknown decorators ([d061adc](https://github.com/angular/angular/commit/d061adc)), closes [#13605](https://github.com/angular/angular/issues/13605)
* **compiler:** fix template binding parsing (`*directive="-..."`) ([e5c6bb4](https://github.com/angular/angular/commit/e5c6bb4)), closes [#13800](https://github.com/angular/angular/issues/13800)
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([5d9cbd7](https://github.com/angular/angular/commit/5d9cbd7)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([aeed737](https://github.com/angular/angular/commit/aeed737)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
* **core:** Add type information to differs ([8c7e93b](https://github.com/angular/angular/commit/8c7e93b)), closes [#13382](https://github.com/angular/angular/issues/13382)
* **i18n:** translate attributes inside elements marked for translation ([424e6c4](https://github.com/angular/angular/commit/424e6c4)), closes [#13796](https://github.com/angular/angular/issues/13796) [#13814](https://github.com/angular/angular/issues/13814)
* **router:** RouterLink mirrors input `target` as attribute ([d9a22da](https://github.com/angular/angular/commit/d9a22da)), closes [#13837](https://github.com/angular/angular/issues/13837)
* **router:** throw an error when navigate to null/undefined path ([46cb04d](https://github.com/angular/angular/commit/46cb04d)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
* **router:** fix checking for object intersection ([6d29fae](https://github.com/angular/angular/commit/6d29fae))
### Features
* **animations:** expose the `element` value within transition events ([4bae4b3](https://github.com/angular/angular/commit/4bae4b3))
* **animations:** expose the `triggerName` within the transition event ([3f67ab0](https://github.com/angular/angular/commit/3f67ab0)), closes [#13600](https://github.com/angular/angular/issues/13600)
* **animations:** support function types in transitions ([9211a22](https://github.com/angular/angular/commit/9211a22)), closes [#13538](https://github.com/angular/angular/issues/13538) [#13537](https://github.com/angular/angular/issues/13537)
* **language-service:** support TS2.2 plugin model ([99aa49a](https://github.com/angular/angular/commit/99aa49a))
* **NgComponentOutlet:** add NgComponentOutlet directive ([8578682](https://github.com/angular/angular/commit/8578682)), closes [#11168](https://github.com/angular/angular/issues/11168)
* **NgTemplateOutlet:** Make NgTemplateOutlet compatible with * syntax ([c0178de](https://github.com/angular/angular/commit/c0178de))
* **router:** call resolver when upstream params change ([#12942](https://github.com/angular/angular/issues/12942)) ([d4d3782](https://github.com/angular/angular/commit/d4d3782))
### BREAKING CHANGES
* core: - `IterableChangeRecord` is now an interface and parameterized on `<V>`.
This should not be an issue unless your code does
`new IterableChangeRecord` which it should not have a reason to do.
- `KeyValueChangeRecord` is now an interface and parameterized on `<V>`.
This should not be an issue unless your code does
`new KeyValueChangeRecord` which it should not have a reason to do.
### DEPRECATION
* Deprecate `ngOutletContext`. Use `ngTemplateOutletContext` instead.
* `CollectionChangeRecord` is renamed to `IterableChangeRecord`.
`CollectionChangeRecord` is aliased to `IterableChangeRecord` and is
marked as `@deprecated`. It will be removed in `v6.x.x`.
* Deprecate `DefaultIterableDiffer` as it is private class which
was erroneously exposed.
* Deprecate `KeyValueDiffers#factories` as it is private field which
was erroneously exposed.
* Deprecate `IterableDiffers#factories` as it is private field which
was erroneously exposed.
<a name="2.4.3"></a>
## [2.4.3](https://github.com/angular/angular/compare/2.4.2...2.4.3) (2017-01-11)
### Bug Fixes
* **compiler:** avoid evaluating arguments to unknown decorators ([5e9d3db](https://github.com/angular/angular/commit/5e9d3db)), closes [#13605](https://github.com/angular/angular/issues/13605)
* **compiler:** fix template binding parsing (`*directive="-..."`) ([7dc12b9](https://github.com/angular/angular/commit/7dc12b9)), closes [#13800](https://github.com/angular/angular/issues/13800)
* **compiler-cli:** add support for more than 2 levels of nested lazy routes ([6164eb2](https://github.com/angular/angular/commit/6164eb2)), closes [angular/angular-cli#3663](https://github.com/angular/angular-cli/issues/3663)
* **compiler-cli:** avoid handling functions in loadChildren as lazy load routes paths ([313683f](https://github.com/angular/angular/commit/313683f)), closes [angular/angular-cli#3204](https://github.com/angular/angular-cli/issues/3204)
* **i18n:** translate attributes inside elements marked for translation ([d7f2a3c](https://github.com/angular/angular/commit/d7f2a3c))
* **router:** RouterLink mirrors input `target` as attribute ([1c82b58](https://github.com/angular/angular/commit/1c82b58)), closes [#13837](https://github.com/angular/angular/issues/13837)
* **router:** throw an error when navigate to null/undefined path ([61ba223](https://github.com/angular/angular/commit/61ba223)), closes [#10560](https://github.com/angular/angular/issues/10560) [#13384](https://github.com/angular/angular/issues/13384)
* **router:** fix checking for object intersection ([1692265](https://github.com/angular/angular/commit/1692265))
<a name="2.4.2"></a>
## [2.4.2](https://github.com/angular/angular/compare/2.4.1...2.4.2) (2017-01-06)
### Bug Fixes
* **common:** add link to trackBy docs from the error message ([#13634](https://github.com/angular/angular/issues/13634)) ([f723437](https://github.com/angular/angular/commit/f723437))
* **common:** do not override locale provided on bootstrap ([#13654](https://github.com/angular/angular/issues/13654)) ([5f49c3e](https://github.com/angular/angular/commit/5f49c3e)), closes [#13607](https://github.com/angular/angular/issues/13607)
* **common:** allow null/undefined values for `NgForTrackBy` ([6be55cc](https://github.com/angular/angular/commit/6be55cc)), closes [#13641](https://github.com/angular/angular/issues/13641)
* **compiler:** dont throw when using `ANALYZE_FOR_ENTRY_COMPONENTS` with user classes ([#13679](https://github.com/angular/angular/issues/13679)) ([230e33f](https://github.com/angular/angular/commit/230e33f)), closes [#13565](https://github.com/angular/angular/issues/13565)
* **compiler:** query `<template>` elements before their children. ([#13677](https://github.com/angular/angular/issues/13677)) ([1cd73c7](https://github.com/angular/angular/commit/1cd73c7)), closes [#13118](https://github.com/angular/angular/issues/13118) [#13167](https://github.com/angular/angular/issues/13167)
* **compiler:** allow "." in attribute selectors ([#13653](https://github.com/angular/angular/issues/13653)) ([29ffdfd](https://github.com/angular/angular/commit/29ffdfd)), closes [#13645](https://github.com/angular/angular/issues/13645) [#13982](https://github.com/angular/angular/issues/13982)
* **core:** animations no longer silently exits if the element is not apart of the DOM ([#13763](https://github.com/angular/angular/issues/13763)) ([f1cde43](https://github.com/angular/angular/commit/f1cde43))
* **core:** animations should blend in all previously transitioned styles into next animation if interrupted ([#13148](https://github.com/angular/angular/issues/13148)) ([b245b92](https://github.com/angular/angular/commit/b245b92))
* **core:** remove reference to "Angular 2" in dev mode warning ([#13751](https://github.com/angular/angular/issues/13751)) ([21f5f05](https://github.com/angular/angular/commit/21f5f05))
* **core/testing:** improve misleading error message when don't call compileComponents ([#13543](https://github.com/angular/angular/issues/13543)) ([0e7f9f0](https://github.com/angular/angular/commit/0e7f9f0)), closes [#11301](https://github.com/angular/angular/issues/11301)
* **forms:** Validators.required properly validate arrays ([#13362](https://github.com/angular/angular/issues/13362)) ([17c5fa9](https://github.com/angular/angular/commit/17c5fa9)), closes [#12274](https://github.com/angular/angular/issues/12274)
* **language-service:** support TypeScript 2.1 ([#13655](https://github.com/angular/angular/issues/13655)) ([56b4296](https://github.com/angular/angular/commit/56b4296))
* **router:** fix lazy loaded module with wildcard route ([#13649](https://github.com/angular/angular/issues/13649)) ([5754ecc](https://github.com/angular/angular/commit/5754ecc)), closes [#12955](https://github.com/angular/angular/issues/12955)
* **router:** routerLink support of null ([#13380](https://github.com/angular/angular/issues/13380)) ([018865e](https://github.com/angular/angular/commit/018865e)), closes [#6971](https://github.com/angular/angular/issues/6971)
* **router:** update route snapshot before emit new values ([#13558](https://github.com/angular/angular/issues/13558)) ([9f6a647](https://github.com/angular/angular/commit/9f6a647)), closes [#12912](https://github.com/angular/angular/issues/12912)
* **upgrade:** fix/improve support for lifecycle hooks ([#13020](https://github.com/angular/angular/issues/13020)) ([21942a8](https://github.com/angular/angular/commit/21942a8))
<a name="4.0.0-beta.2"></a>
# [4.0.0-beta.2](https://github.com/angular/angular/compare/4.0.0-beta.1...4.0.0-beta.2) (2017-01-06)
### Features
* **compiler:** generate less code for bindings to DOM elements ([db49d42](https://github.com/angular/angular/commit/db49d42))
* **compiler:** generate proper reexports in `.ngfactory.ts` files to not need transitive deps for compiling `.ngfactory.ts` files. ([#13524](https://github.com/angular/angular/issues/13524)) ([9c69703](https://github.com/angular/angular/commit/9c69703)), closes [#12787](https://github.com/angular/angular/issues/12787)
* **router:** add an extra argument to CanDeactivate interface ([#13560](https://github.com/angular/angular/issues/13560)) ([69fa3bb](https://github.com/angular/angular/commit/69fa3bb)), closes [#9853](https://github.com/angular/angular/issues/9853)
### Bug Fixes
* **compiler:** improve error message for undefined providers ([#13546](https://github.com/angular/angular/issues/13546)) ([6b02b80](https://github.com/angular/angular/commit/6b02b80)), closes [#10835](https://github.com/angular/angular/issues/10835)
* **compiler:** improve the error when template is not a string ([2c0c86e](https://github.com/angular/angular/commit/2c0c86e)), closes [#8708](https://github.com/angular/angular/issues/8708) [#13377](https://github.com/angular/angular/issues/13377)
* **compiler:** throw an error for invalid provider ([#13544](https://github.com/angular/angular/issues/13544)) ([445ed43](https://github.com/angular/angular/commit/445ed43)), closes [#8870](https://github.com/angular/angular/issues/8870)
* **i18n:** parse ICU messages while normalizing templates ([e74d8aa](https://github.com/angular/angular/commit/e74d8aa))
Note: 4.0.0-beta.2 release also contains all the changes present in the 2.4.2 release.
### BREAKING CHANGES
* **core**: `SimpleChange` now takes an additional argument that defines whether this is the first
change or not. This is a low profile API and we don't expect anyone to be affected by this change.
If you are impacted by this change please file an issue. ([465516b](https://github.com/angular/angular/commit/465516b))
<a name="4.0.0-beta.1"></a>
# [4.0.0-beta.1](https://github.com/angular/angular/compare/2.4.0-marker...4.0.0-beta.1) (2016-12-22)
### Features
* **upgrade:** support the `$doCheck()` lifecycle hook in `UpgradeComponent` ([#13015](https://github.com/angular/angular/issues/13015)) ([9da4c25](https://github.com/angular/angular/commit/9da4c25))
Note: 4.0.0-beta.1 release also contains all the changes present in the 2.4.0 and the 2.4.1 releases.
<a name="2.4.1"></a>
## [2.4.1](https://github.com/angular/angular/compare/2.4.0...2.4.1) (2016-12-21)
### Bug Fixes
* **animations:** always quote string map key values in AOT code ([#13602](https://github.com/angular/angular/issues/13602)) ([6a5e46c](https://github.com/angular/angular/commit/6a5e46c))
* **animations:** always recover from a failed animation step ([#13604](https://github.com/angular/angular/issues/13604)) ([d788c67](https://github.com/angular/angular/commit/d788c67))
* **compiler:** ignore `@import` in comments ([#13368](https://github.com/angular/angular/issues/13368)) ([6316e5d](https://github.com/angular/angular/commit/6316e5d)), closes [#12196](https://github.com/angular/angular/issues/12196)
* **core:** improve error message when component factory cannot be found ([#13541](https://github.com/angular/angular/issues/13541)) ([b9e979e](https://github.com/angular/angular/commit/b9e979e)), closes [#12678](https://github.com/angular/angular/issues/12678)
* **router:** should reset location if a navigation by location is successful ([#13545](https://github.com/angular/angular/issues/13545)) ([a38f14b](https://github.com/angular/angular/commit/a38f14b)), closes [#13491](https://github.com/angular/angular/issues/13491)
<a name="2.4.0"></a>
# [2.4.0 stability-interjection](https://github.com/angular/angular/compare/2.3.1...2.4.0) (2016-12-20)
@ -230,25 +17,6 @@ Note: 4.0.0-beta.1 release also contains all the changes present in the 2.4.0 an
* update to `rxjs@5.0.1` and unpin the rxjs peerDeps via `^5.0.1` ([#13572](https://github.com/angular/angular/issues/13572)) ([8d5da1e](https://github.com/angular/angular/commit/8d5da1e)), closes [#13561](https://github.com/angular/angular/issues/13561) [#13478](https://github.com/angular/angular/issues/13478)
<a name="4.0.0-beta.0"></a>
# [4.0.0-beta.0](https://github.com/angular/angular/compare/2.3.0...4.0.0-beta.0) (2016-12-15)
### Features
* **common:** add a `titlecase` pipe ([#13324](https://github.com/angular/angular/issues/13324)) ([61d7c1e](https://github.com/angular/angular/commit/61d7c1e)), closes [#11436](https://github.com/angular/angular/issues/11436)
* **common:** export NgLocaleLocalization ([#13367](https://github.com/angular/angular/issues/13367)) ([56dce0e](https://github.com/angular/angular/commit/56dce0e)), closes [#11921](https://github.com/angular/angular/issues/11921)
* **compiler:** add id property to i18nMessage ([6dd5201](https://github.com/angular/angular/commit/6dd5201))
* **compiler:** digest methods return i18nMessage id if sets ([562f7a2](https://github.com/angular/angular/commit/562f7a2))
* **forms:** add novalidate by default ([#13092](https://github.com/angular/angular/issues/13092)) ([4c35be3](https://github.com/angular/angular/commit/4c35be3))
* **http:** simplify URLSearchParams creation ([#13338](https://github.com/angular/angular/issues/13338)) ([90c2235](https://github.com/angular/angular/commit/90c2235)), closes [#8858](https://github.com/angular/angular/issues/8858)
* **language-service:** warn when a method isn't called in an event ([#13437](https://github.com/angular/angular/issues/13437)) ([9ec0a4e](https://github.com/angular/angular/commit/9ec0a4e))
* **platform browser:** introduce Meta service ([#12322](https://github.com/angular/angular/issues/12322)) ([72361fb](https://github.com/angular/angular/commit/72361fb))
* **router:** routerLink add tabindex attribute ([#13094](https://github.com/angular/angular/issues/13094)) ([a006c14](https://github.com/angular/angular/commit/a006c14)), closes [#10895](https://github.com/angular/angular/issues/10895)
* **testing:** add overrideTemplate method ([#13372](https://github.com/angular/angular/issues/13372)) ([169ed82](https://github.com/angular/angular/commit/169ed82)), closes [#10685](https://github.com/angular/angular/issues/10685)
* **common** ngIf now supports else; saves condition to local var ([b4db73d](https://github.com/angular/angular/commit/b4db73d)), closes [#13061](https://github.com/angular/angular/issues/13061) [#13297](https://github.com/angular/angular/issues/13297)
Note: 4.0.0-beta.0 release also contains all the changes present in the 2.3.1 release.
<a name="2.3.1"></a>
## [2.3.1](https://github.com/angular/angular/compare/2.3.0...2.3.1) (2016-12-15)
@ -303,6 +71,7 @@ The >=2.3.1 compiler will issue is the following error if it encounters componen
We are adding more tests to our test suite to catch these kinds of problems before we cut a release.
<a name="2.3.0"></a>
# [2.3.0](https://github.com/angular/angular/compare/2.3.0-rc.0...2.3.0) (2016-12-07)

View File

@ -74,15 +74,6 @@ use in these instructions.
*Option 2*: defining a bash alias like `alias nbin='PATH=$(npm bin):$PATH'` as detailed in this
[Stackoverflow answer](http://stackoverflow.com/questions/9679932/how-to-use-package-installed-locally-in-node-modules/15157360#15157360) and used like this: e.g., `nbin gulp build`.
## Installing Bower Modules
Now run `bower` to install additional dependencies:
```shell
# Install other Angular project dependencies (bower.json)
bower install
```
## Windows only
In order to create the right symlinks, run **as administrator**:
@ -133,8 +124,7 @@ If you happen to modify the public API of Angular, API golden files must be upda
$ gulp public-api:update
```
Note: The command `gulp public-api:enforce` fails when the API doesn't match the golden files. Make sure to rebuild
the project before trying to verify after an API change.
Note: The command `./test.sh tools` fails when the API doesn't match the golden files.
## <a name="clang-format"></a> Formatting your source code
@ -147,14 +137,6 @@ You can automatically format your code by running:
$ gulp format
```
## Linting/verifying your source code
You can check that your code is properly formatted and adheres to coding style by running:
``` shell
$ gulp lint
```
## Publishing your own personal snapshot build
You may find that your un-merged change needs some validation from external participants.
@ -173,6 +155,3 @@ For subsequent snapshots, just run
``` shell
$ ./scripts/publish/publish-build-artifacts.sh [github username]
```
The script will publish the build snapshot to a branch with the same name as your current branch,
and create it if it doesn't exist.

View File

@ -112,7 +112,6 @@ do
PWD=`pwd`
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
DESTDIR=${PWD}/dist/packages-dist/${PACKAGE}
ES2015_DESTDIR=${PWD}/dist/packages-dist-es2015/${PACKAGE}
UMD_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.js
UMD_TESTING_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-testing.umd.js
UMD_STATIC_ES5_PATH=${DESTDIR}/bundles/${PACKAGE}-static.umd.js
@ -132,13 +131,6 @@ do
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
$TSC -p ${SRCDIR}/tsconfig-build.json
# ES2015 distro is not ready yet; don't slow down all builds for it
# TODO(alexeagle,igorminar): figure out ES2015 story and enable
if [[ -n "${EXPERIMENTAL_ES2015_DISTRO}" ]]; then
$TSC -p ${SRCDIR}/tsconfig-build.json --target es2015 --outDir ${ES2015_DESTDIR}
cp ${SRCDIR}/package.json ${ES2015_DESTDIR}/
cp ${PWD}/modules/@angular/README.md ${ES2015_DESTDIR}/
fi
if [[ -e ${SRCDIR}/tsconfig-upgrade.json ]]; then
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-upgrade.json ====="
@ -222,10 +214,6 @@ do
$UGLIFYJS -c --screw-ie8 --comments -o ${UMD_UPGRADE_ES5_MIN_PATH} ${UMD_UPGRADE_ES5_PATH}
fi
) 2>&1 | grep -v "as external dependency"
if [[ -n "${EXPERIMENTAL_ES2015_DISTRO}" ]]; then
cp -prv ${DESTDIR}/bundles ${ES2015_DESTDIR}
fi
fi
(

View File

@ -1,82 +0,0 @@
# Angular Release Schedule
This document contains historic record of past Angular releases and future release schedule.
The purpose of this document is to assist coordination among the Angular team, Angular contributors, Angular application teams, and Angular community projects.
We'll keep this doc up to date when unplanned releases or other schedule changes occur.
## Schedule Caveats and Exceptions
The dates listed here are approximate last minute issues, team or community events, etc. can cause us to release a few days sooner or later.
This page contains only planned and past unplanned releases.
Due to serious regressions or other important reasons we reserve the right to release an unplanned patch or minor release.
In such case, we'll update this document accordingly.
The dates past March 2017 are just a guidance and might be adjusted slightly if necessary.
## Tentative Schedule Until March 2017
<!--
The table below is formatted so that it's easy to read and edit in both markdown and rendered html form.
In order to deal with undesirable line breaks, two special characters are occasionally used:
- non-breaking hyphen: "" http://www.fileformat.info/info/unicode/char/2011/index.htm
- non-breaking space: " " http://www.fileformat.info/info/unicode/char/00a0/index.htm
If you see undesirable wrapping issues in the rendered form, please copy&paste the quoted characters and use them in the table below where needed.
-->
Week Of | Stable Release<br>(@latest npm tag) | Beta/RC Release<br>(@next npm tag) | Note
------------- | ----------------------------------- | ---------------------------------- | ---------------------
2016-09-14 | 2.0.0 | - | Major Version Release
2016-09-21 | 2.0.1 | 2.1.0-beta.0 |
20160928 | - | - | Angular Connect
2016-10-05 | 2.0.2 | 2.1.0-rc.0 |
2016-10-12 | 2.1.0 | - | Minor Version Release
2016-10-19 | 2.1.1 | 2.2.0-beta.0 |
2016-10-26 | 2.1.2 | 2.2.0-beta.1 |
2016-11-02 | 2.1.3 | 2.2.0-rc.0 |
2016-11-09 | 2.2.0 | - | Minor Version Release
2016-11-16 | 2.2.1 | 2.3.0-beta.0 |
2016-11-23 | 2.2.2 | 2.3.0-beta.1 |
*2016-11-23* | *2.2.3* | - | *Unplanned Patch Release to fix regressions*
2016-11-30 | 2.2.4 | 2.3.0-rc.0 |
2016-12-07 | 2.3.0 | - | Minor Version Release
2016-12-14 | 2.3.1 | 4.0.0-beta.0 |
*2016-12-21* | *2.4.0* | - | *Unplanned Minor Release due to release of RxJS 5.0.0*
2016-12-21 | 2.4.1 | 4.0.0-beta.1 |
2016-12-28 | - | - | Holiday Break
2017-01-04 | 2.4.2 | 4.0.0-beta.2 |
2017-01-11 | 2.4.3 | 4.0.0-beta.3 |
2017-01-18 | 2.4.4 | 4.0.0-beta.4 |
2017-01-25 | 2.4.5 | 4.0.0-beta.5 |
2017-02-01 | 2.4.6 | 4.0.0-beta.6 |
2017-02-08 | 2.4.7 | 4.0.0-rc.0 |
2017-02-15 | 2.4.8 | 4.0.0-rc.1 |
2017-02-22 | 2.4.9 | 4.0.0-rc.2 |
2017-03-01 | 2.4.10 | 4.0.0-rc.3 |
2017-03-08 | 4.0.0 + 2.4.11 | - | Major Version Release
## Tentative Schedule After March 2017
Date | Stable Release | Compatibility`*`
---------------------- | -------------- | ----------------
September/October 2017 | 5.0.0 | ^4.0.0
March 2018 | 6.0.0 | ^5.0.0
September/October 2018 | 7.0.0 | ^6.0.0
`*` The goal of the backwards compatibility promise, is to ensure that changes in the core framework and tooling don't break the existing ecosystem of components and applications and don't put undue upgrade/migration burden on Angular application and component authors.
## More Info & Resources
In [September 2016 we announced](http://angularjs.blogspot.com/2016/10/versioning-and-releasing-angular.html) that Angular is fully adopting [semantic versioning](http://semver.org/) and that we'll be releasing patch versions on a weekly basis (~50 per year), minor versions monthly for 3 months following a major version release, and every 6 months we'll release a major version that will be backwards compatible with the previous release for most developers, but might remove APIs that have been deprecated two major versions ago (6 or more months ago).
In [December 2016 we clarified this message](http://angularjs.blogspot.com/2016/12/ok-let-me-explain-its-going-to-be.html), and provided additional details about the plans to release Angular 4.0.0 in March 2017.
This document contains updates to the schedule that happened since then.

View File

@ -9,7 +9,7 @@
// Must be imported first, because angular2 decorators throws on load.
import 'reflect-metadata';
export {InjectionToken, Injector, Provider, ReflectiveInjector} from '@angular/core';
export {Injector, OpaqueToken, Provider, ReflectiveInjector} from '@angular/core';
export {Options} from './src/common_options';
export {MeasureValues} from './src/measure_values';
export {Metric} from './src/metric';

View File

@ -6,26 +6,26 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '@angular/core';
import {OpaqueToken} from '@angular/core';
import * as fs from 'fs';
export class Options {
static SAMPLE_ID = new InjectionToken('Options.sampleId');
static DEFAULT_DESCRIPTION = new InjectionToken('Options.defaultDescription');
static SAMPLE_DESCRIPTION = new InjectionToken('Options.sampleDescription');
static FORCE_GC = new InjectionToken('Options.forceGc');
static SAMPLE_ID = new OpaqueToken('Options.sampleId');
static DEFAULT_DESCRIPTION = new OpaqueToken('Options.defaultDescription');
static SAMPLE_DESCRIPTION = new OpaqueToken('Options.sampleDescription');
static FORCE_GC = new OpaqueToken('Options.forceGc');
static NO_PREPARE = () => true;
static PREPARE = new InjectionToken('Options.prepare');
static EXECUTE = new InjectionToken('Options.execute');
static CAPABILITIES = new InjectionToken('Options.capabilities');
static USER_AGENT = new InjectionToken('Options.userAgent');
static MICRO_METRICS = new InjectionToken('Options.microMetrics');
static USER_METRICS = new InjectionToken('Options.userMetrics');
static NOW = new InjectionToken('Options.now');
static WRITE_FILE = new InjectionToken('Options.writeFile');
static RECEIVED_DATA = new InjectionToken('Options.receivedData');
static REQUEST_COUNT = new InjectionToken('Options.requestCount');
static CAPTURE_FRAMES = new InjectionToken('Options.frameCapture');
static PREPARE = new OpaqueToken('Options.prepare');
static EXECUTE = new OpaqueToken('Options.execute');
static CAPABILITIES = new OpaqueToken('Options.capabilities');
static USER_AGENT = new OpaqueToken('Options.userAgent');
static MICRO_METRICS = new OpaqueToken('Options.microMetrics');
static USER_METRICS = new OpaqueToken('Options.userMetrics');
static NOW = new OpaqueToken('Options.now');
static WRITE_FILE = new OpaqueToken('Options.writeFile');
static RECEIVED_DATA = new OpaqueToken('Options.receivedData');
static REQUEST_COUNT = new OpaqueToken('Options.requestCount');
static CAPTURE_FRAMES = new OpaqueToken('Options.frameCapture');
static DEFAULT_PROVIDERS = [
{provide: Options.DEFAULT_DESCRIPTION, useValue: {}},
{provide: Options.SAMPLE_DESCRIPTION, useValue: {}},

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken, Injector} from '@angular/core';
import {Injector, OpaqueToken} from '@angular/core';
import {Metric} from '../metric';
@ -60,4 +60,4 @@ function mergeStringMaps(maps: {[key: string]: string}[]): {[key: string]: strin
return result;
}
const _CHILDREN = new InjectionToken('MultiMetric.children');
const _CHILDREN = new OpaqueToken('MultiMetric.children');

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {Options} from '../common_options';
import {Metric} from '../metric';
@ -18,7 +18,7 @@ import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_e
*/
@Injectable()
export class PerflogMetric extends Metric {
static SET_TIMEOUT = new InjectionToken('PerflogMetric.setTimeout');
static SET_TIMEOUT = new OpaqueToken('PerflogMetric.setTimeout');
static PROVIDERS = [
PerflogMetric, {
provide: PerflogMetric.SET_TIMEOUT,

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {print} from '../facade/lang';
import {MeasureValues} from '../measure_values';
import {Reporter} from '../reporter';
@ -20,8 +20,8 @@ import {formatNum, formatStats, sortedProps} from './util';
*/
@Injectable()
export class ConsoleReporter extends Reporter {
static PRINT = new InjectionToken('ConsoleReporter.print');
static COLUMN_WIDTH = new InjectionToken('ConsoleReporter.columnWidth');
static PRINT = new OpaqueToken('ConsoleReporter.print');
static COLUMN_WIDTH = new OpaqueToken('ConsoleReporter.columnWidth');
static PROVIDERS = [
ConsoleReporter, {provide: ConsoleReporter.COLUMN_WIDTH, useValue: 18},
{provide: ConsoleReporter.PRINT, useValue: print}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {Options} from '../common_options';
import {MeasureValues} from '../measure_values';
@ -21,7 +21,7 @@ import {formatStats, sortedProps} from './util';
*/
@Injectable()
export class JsonFileReporter extends Reporter {
static PATH = new InjectionToken('JsonFileReporter.path');
static PATH = new OpaqueToken('JsonFileReporter.path');
static PROVIDERS = [JsonFileReporter, {provide: JsonFileReporter.PATH, useValue: '.'}];
constructor(

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken, Injector} from '@angular/core';
import {Injector, OpaqueToken} from '@angular/core';
import {MeasureValues} from '../measure_values';
import {Reporter} from '../reporter';
@ -39,4 +39,4 @@ export class MultiReporter extends Reporter {
}
}
const _CHILDREN = new InjectionToken('MultiReporter.children');
const _CHILDREN = new OpaqueToken('MultiReporter.children');

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '@angular/core';
import {OpaqueToken} from '@angular/core';
import {Options} from './common_options';
import {Metric} from './metric';

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {MeasureValues} from '../measure_values';
import {Statistic} from '../statistic';
@ -18,8 +18,8 @@ import {Validator} from '../validator';
*/
@Injectable()
export class RegressionSlopeValidator extends Validator {
static SAMPLE_SIZE = new InjectionToken('RegressionSlopeValidator.sampleSize');
static METRIC = new InjectionToken('RegressionSlopeValidator.metric');
static SAMPLE_SIZE = new OpaqueToken('RegressionSlopeValidator.sampleSize');
static METRIC = new OpaqueToken('RegressionSlopeValidator.metric');
static PROVIDERS = [
RegressionSlopeValidator, {provide: RegressionSlopeValidator.SAMPLE_SIZE, useValue: 10},
{provide: RegressionSlopeValidator.METRIC, useValue: 'scriptTime'}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Inject, Injectable, InjectionToken} from '@angular/core';
import {Inject, Injectable, OpaqueToken} from '@angular/core';
import {MeasureValues} from '../measure_values';
import {Validator} from '../validator';
@ -16,7 +16,7 @@ import {Validator} from '../validator';
*/
@Injectable()
export class SizeValidator extends Validator {
static SAMPLE_SIZE = new InjectionToken('SizeValidator.sampleSize');
static SAMPLE_SIZE = new OpaqueToken('SizeValidator.sampleSize');
static PROVIDERS = [SizeValidator, {provide: SizeValidator.SAMPLE_SIZE, useValue: 10}];
constructor(@Inject(SizeValidator.SAMPLE_SIZE) private _sampleSize: number) { super(); }

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken, Injector} from '@angular/core';
import {Injector, OpaqueToken} from '@angular/core';
import {Options} from './common_options';
@ -101,4 +101,4 @@ export class PerfLogFeatures {
}
}
const _CHILDREN = new InjectionToken('WebDriverExtension.children');
const _CHILDREN = new OpaqueToken('WebDriverExtension.children');

View File

@ -12,8 +12,9 @@
* Entry point for all public APIs of the common package.
*/
export * from './location/index';
export {NgLocaleLocalization, NgLocalization} from './localization';
export {NgLocalization} from './localization';
export {CommonModule} from './common_module';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NgComponentOutlet} from './directives/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe, TitleCasePipe} from './pipes/index';
export {NgClass, NgFor, NgIf, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet} from './directives/index';
export {AsyncPipe, DatePipe, I18nPluralPipe, I18nSelectPipe, JsonPipe, LowerCasePipe, CurrencyPipe, DecimalPipe, PercentPipe, SlicePipe, UpperCasePipe} from './pipes/index';
export {VERSION} from './version';
export {Version} from '@angular/core';

View File

@ -9,7 +9,6 @@
import {Provider} from '@angular/core';
import {NgClass} from './ng_class';
import {NgComponentOutlet} from './ng_component_outlet';
import {NgFor} from './ng_for';
import {NgIf} from './ng_if';
import {NgPlural, NgPluralCase} from './ng_plural';
@ -19,7 +18,6 @@ import {NgTemplateOutlet} from './ng_template_outlet';
export {
NgClass,
NgComponentOutlet,
NgFor,
NgIf,
NgPlural,
@ -31,14 +29,12 @@ export {
NgTemplateOutlet
};
/**
* A collection of Angular directives that are likely to be used in each and every Angular
* application.
*/
export const COMMON_DIRECTIVES: Provider[] = [
NgClass,
NgComponentOutlet,
NgFor,
NgIf,
NgTemplateOutlet,

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, DoCheck, ElementRef, Input, IterableChanges, IterableDiffer, IterableDiffers, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableDiffer, IterableDiffers, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {isListLikeIterable} from '../facade/collection';
import {stringify} from '../facade/lang';
import {isPresent, stringify} from '../facade/lang';
/**
* @ngModule CommonModule
@ -25,8 +25,6 @@ import {stringify} from '../facade/lang';
* <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
*
* <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
*
* <some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
* ```
*
* @description
@ -41,8 +39,8 @@ import {stringify} from '../facade/lang';
*/
@Directive({selector: '[ngClass]'})
export class NgClass implements DoCheck {
private _iterableDiffer: IterableDiffer<string>;
private _keyValueDiffer: KeyValueDiffer<string, any>;
private _iterableDiffer: IterableDiffer;
private _keyValueDiffer: KeyValueDiffer;
private _initialClasses: string[] = [];
private _rawClass: string[]|Set<string>|{[klass: string]: any};
@ -78,35 +76,39 @@ export class NgClass implements DoCheck {
ngDoCheck(): void {
if (this._iterableDiffer) {
const iterableChanges = this._iterableDiffer.diff(this._rawClass as string[]);
if (iterableChanges) {
this._applyIterableChanges(iterableChanges);
const changes = this._iterableDiffer.diff(this._rawClass);
if (changes) {
this._applyIterableChanges(changes);
}
} else if (this._keyValueDiffer) {
const keyValueChanges = this._keyValueDiffer.diff(this._rawClass as{[k: string]: any});
if (keyValueChanges) {
this._applyKeyValueChanges(keyValueChanges);
const changes = this._keyValueDiffer.diff(this._rawClass);
if (changes) {
this._applyKeyValueChanges(changes);
}
}
}
private _cleanupClasses(rawClassVal: string[]|{[klass: string]: any}): void {
private _cleanupClasses(rawClassVal: string[]|Set<string>|{[klass: string]: any}): void {
this._applyClasses(rawClassVal, true);
this._applyInitialClasses(false);
}
private _applyKeyValueChanges(changes: KeyValueChanges<string, any>): void {
changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue));
changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue));
changes.forEachRemovedItem((record) => {
private _applyKeyValueChanges(changes: any): void {
changes.forEachAddedItem(
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
changes.forEachChangedItem(
(record: KeyValueChangeRecord) => this._toggleClass(record.key, record.currentValue));
changes.forEachRemovedItem((record: KeyValueChangeRecord) => {
if (record.previousValue) {
this._toggleClass(record.key, false);
}
});
}
private _applyIterableChanges(changes: IterableChanges<string>): void {
changes.forEachAddedItem((record) => {
private _applyIterableChanges(changes: any): void {
changes.forEachAddedItem((record: CollectionChangeRecord) => {
if (typeof record.item === 'string') {
this._toggleClass(record.item, true);
} else {
@ -115,7 +117,8 @@ export class NgClass implements DoCheck {
}
});
changes.forEachRemovedItem((record) => this._toggleClass(record.item, false));
changes.forEachRemovedItem(
(record: CollectionChangeRecord) => this._toggleClass(record.item, false));
}
private _applyInitialClasses(isCleanup: boolean) {
@ -123,23 +126,23 @@ export class NgClass implements DoCheck {
}
private _applyClasses(
rawClassVal: string[]|Set<string>|{[klass: string]: any}, isCleanup: boolean) {
rawClassVal: string[]|Set<string>|{[key: string]: any}, isCleanup: boolean) {
if (rawClassVal) {
if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
(<any>rawClassVal).forEach((klass: string) => this._toggleClass(klass, !isCleanup));
} else {
Object.keys(rawClassVal).forEach(klass => {
if (rawClassVal[klass] != null) this._toggleClass(klass, !isCleanup);
if (isPresent(rawClassVal[klass])) this._toggleClass(klass, !isCleanup);
});
}
}
}
private _toggleClass(klass: string, enabled: any): void {
private _toggleClass(klass: string, enabled: boolean): void {
klass = klass.trim();
if (klass) {
klass.split(/\s+/g).forEach(
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, !!enabled); });
klass => { this._renderer.setElementClass(this._ngEl.nativeElement, klass, enabled); });
}
}
}

View File

@ -1,86 +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 {ComponentFactoryResolver, ComponentRef, Directive, Injector, Input, OnChanges, Provider, SimpleChanges, Type, ViewContainerRef} from '@angular/core';
/**
* Instantiates a single {@link Component} type and inserts its Host View into current View.
* `NgComponentOutlet` provides a declarative approach for dynamic component creation.
*
* `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and
* any existing component will get destroyed.
*
* ### Fine tune control
*
* You can control the component creation process by using the following optional attributes:
*
* * `ngOutletInjector`: Optional custom {@link Injector} that will be used as parent for the
* Component.
* Defaults to the injector of the current view container.
*
* * `ngOutletProviders`: Optional injectable objects ({@link Provider}) that are visible to the
* component.
*
* * `ngOutletContent`: Optional list of projectable nodes to insert into the content
* section of the component, if exists. ({@link NgContent}).
*
*
* ### Syntax
*
* Simple
* ```
* <ng-container *ngComponentOutlet="componentTypeExpression"></ng-container>
* ```
*
* Customized
* ```
* <ng-container *ngComponentOutlet="componentTypeExpression;
* injector: injectorExpression;
* content: contentNodesExpression">
* </ng-container>
* ```
*
* # Example
*
* {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
*
* A more complete example with additional options:
*
* {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'}
*
* @experimental
*/
@Directive({selector: '[ngComponentOutlet]'})
export class NgComponentOutlet implements OnChanges {
@Input() ngComponentOutlet: Type<any>;
@Input() ngComponentOutletInjector: Injector;
@Input() ngComponentOutletContent: any[][];
componentRef: ComponentRef<any>;
constructor(
private _cmpFactoryResolver: ComponentFactoryResolver,
private _viewContainerRef: ViewContainerRef) {}
ngOnChanges(changes: SimpleChanges) {
if (this.componentRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this.componentRef.hostView));
}
this._viewContainerRef.clear();
this.componentRef = null;
if (this.ngComponentOutlet) {
let injector = this.ngComponentOutletInjector || this._viewContainerRef.parentInjector;
this.componentRef = this._viewContainerRef.createComponent(
this._cmpFactoryResolver.resolveComponentFactory(this.ngComponentOutlet),
this._viewContainerRef.length, injector, this.ngComponentOutletContent);
}
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectorRef, Directive, DoCheck, EmbeddedViewRef, Input, IterableChangeRecord, IterableChanges, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef, isDevMode} from '@angular/core';
import {ChangeDetectorRef, CollectionChangeRecord, DefaultIterableDiffer, Directive, DoCheck, EmbeddedViewRef, Input, IterableDiffer, IterableDiffers, OnChanges, SimpleChanges, TemplateRef, TrackByFn, ViewContainerRef} from '@angular/core';
import {getTypeNameForDebugging} from '../facade/lang';
@ -89,23 +89,9 @@ export class NgForRow {
@Directive({selector: '[ngFor][ngForOf]'})
export class NgFor implements DoCheck, OnChanges {
@Input() ngForOf: any;
@Input()
set ngForTrackBy(fn: TrackByFn) {
if (isDevMode() && fn != null && typeof fn !== 'function') {
// TODO(vicb): use a log service once there is a public one available
if (<any>console && <any>console.warn) {
console.warn(
`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
`See https://angular.io/docs/ts/latest/api/common/index/NgFor-directive.html#!#change-propagation for more information.`);
}
}
this._trackByFn = fn;
}
@Input() ngForTrackBy: TrackByFn;
get ngForTrackBy(): TrackByFn { return this._trackByFn; }
private _differ: IterableDiffer<any> = null;
private _trackByFn: TrackByFn;
private _differ: IterableDiffer = null;
constructor(
private _viewContainer: ViewContainerRef, private _template: TemplateRef<NgForRow>,
@ -133,17 +119,17 @@ export class NgFor implements DoCheck, OnChanges {
}
}
ngDoCheck(): void {
ngDoCheck() {
if (this._differ) {
const changes = this._differ.diff(this.ngForOf);
if (changes) this._applyChanges(changes);
}
}
private _applyChanges(changes: IterableChanges<any>) {
private _applyChanges(changes: DefaultIterableDiffer) {
const insertTuples: RecordViewTuple[] = [];
changes.forEachOperation(
(item: IterableChangeRecord<any>, adjustedPreviousIndex: number, currentIndex: number) => {
(item: CollectionChangeRecord, adjustedPreviousIndex: number, currentIndex: number) => {
if (item.previousIndex == null) {
const view = this._viewContainer.createEmbeddedView(
this._template, new NgForRow(null, null, null), currentIndex);
@ -175,7 +161,7 @@ export class NgFor implements DoCheck, OnChanges {
});
}
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: IterableChangeRecord<any>) {
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
view.context.$implicit = record.item;
}
}

View File

@ -6,152 +6,46 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, EmbeddedViewRef, Input, TemplateRef, ViewContainerRef} from '@angular/core';
import {Directive, Input, TemplateRef, ViewContainerRef} from '@angular/core';
/**
* Conditionally includes a template based on the value of an `expression`.
* Removes or recreates a portion of the DOM tree based on an {expression}.
*
* `ngIf` evaluates the `expression` and then renders the `then` or `else` template in its place
* when expression is truthy or falsy respectively. Typically the:
* - `then` template is the inline template of `ngIf` unless bound to a different value.
* - `else` template is blank unless it is bound.
* If the expression assigned to `ngIf` evaluates to a falsy value then the element
* is removed from the DOM, otherwise a clone of the element is reinserted into the DOM.
*
* # Most common usage
*
* The most common usage of the `ngIf` directive is to conditionally show the inline template as
* seen in this example:
* {@example common/ngIf/ts/module.ts region='NgIfSimple'}
*
* # Showing an alternative template using `else`
*
* If it is necessary to display a template when the `expression` is falsy use the `else` template
* binding as shown. Note that the `else` binding points to a `<template>` labeled `#elseBlock`.
* The template can be defined anywhere in the component view but is typically placed right after
* `ngIf` for readability.
*
* {@example common/ngIf/ts/module.ts region='NgIfElse'}
*
* # Using non-inlined `then` template
*
* Usually the `then` template is the inlined template of the `ngIf`, but it can be changed using
* a binding (just like `else`). Because `then` and `else` are bindings, the template references can
* change at runtime as shown in this example.
*
* {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
*
* # Storing conditional result in a variable
*
* A common pattern is that we need to show a set of properties from the same object. If the
* object is undefined, then we have to use the safe-traversal-operator `?.` to guard against
* dereferencing a `null` value. This is especially the case when waiting on async data such as
* when using the `async` pipe as shown in folowing example:
* ### Example ([live demo](http://plnkr.co/edit/fe0kgemFBtmQOY31b4tw?p=preview)):
*
* ```
* Hello {{ (userStream|async)?.last }}, {{ (userStream|async)?.first }}!
* <div *ngIf="errorCount > 0" class="error">
* <!-- Error message displayed when the errorCount property in the current context is greater
* than 0. -->
* {{errorCount}} errors detected
* </div>
* ```
*
* There are several inefficiencies in the above example:
* - We create multiple subscriptions on `userStream`. One for each `async` pipe, or two in the
* example above.
* - We cannot display an alternative screen while waiting for the data to arrive asynchronously.
* - We have to use the safe-traversal-operator `?.` to access properties, which is cumbersome.
* - We have to place the `async` pipe in parenthesis.
*
* A better way to do this is to use `ngIf` and store the result of the condition in a local
* variable as shown in the the example below:
*
* {@example common/ngIf/ts/module.ts region='NgIfLet'}
*
* Notice that:
* - We use only one `async` pipe and hence only one subscription gets created.
* - `ngIf` stores the result of the `userStream|async` in the local variable `user`.
* - The local `user` can then be bound repeatedly in a more efficient way.
* - No need to use the safe-traversal-operator `?.` to access properties as `ngIf` will only
* display the data if `userStream` returns a value.
* - We can display an alternative template while waiting for the data.
*
* ### Syntax
*
* Simple form:
* - `<div *ngIf="condition">...</div>`
* - `<div template="ngIf condition">...</div>`
* - `<template [ngIf]="condition"><div>...</div></template>`
*
* Form with an else block:
* ```
* <div *ngIf="condition; else elseBlock">...</div>
* <template #elseBlock>...</template>
* ```
*
* Form with a `then` and `else` block:
* ```
* <div *ngIf="condition; then thenBlock else elseBlock"></div>
* <template #thenBlock>...</template>
* <template #elseBlock>...</template>
* ```
*
* Form with storing the value locally:
* ```
* <div *ngIf="condition; else elseBlock; let value">{{value}}</div>
* <template #elseBlock>...</template>
* ```
*
* @stable
*/
@Directive({selector: '[ngIf]'})
export class NgIf {
private _context: NgIfContext = new NgIfContext();
private _thenTemplateRef: TemplateRef<NgIfContext> = null;
private _elseTemplateRef: TemplateRef<NgIfContext> = null;
private _thenViewRef: EmbeddedViewRef<NgIfContext> = null;
private _elseViewRef: EmbeddedViewRef<NgIfContext> = null;
private _hasView = false;
constructor(private _viewContainer: ViewContainerRef, templateRef: TemplateRef<NgIfContext>) {
this._thenTemplateRef = templateRef;
}
constructor(private _viewContainer: ViewContainerRef, private _template: TemplateRef<Object>) {}
@Input()
set ngIf(condition: any) {
this._context.$implicit = condition;
this._updateView();
}
@Input()
set ngIfThen(templateRef: TemplateRef<NgIfContext>) {
this._thenTemplateRef = templateRef;
this._thenViewRef = null; // clear previous view if any.
this._updateView();
}
@Input()
set ngIfElse(templateRef: TemplateRef<NgIfContext>) {
this._elseTemplateRef = templateRef;
this._elseViewRef = null; // clear previous view if any.
this._updateView();
}
private _updateView() {
if (this._context.$implicit) {
if (!this._thenViewRef) {
if (condition && !this._hasView) {
this._hasView = true;
this._viewContainer.createEmbeddedView(this._template);
} else if (!condition && this._hasView) {
this._hasView = false;
this._viewContainer.clear();
this._elseViewRef = null;
if (this._thenTemplateRef) {
this._thenViewRef =
this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
}
}
} else {
if (!this._elseViewRef) {
this._viewContainer.clear();
this._thenViewRef = null;
if (this._elseTemplateRef) {
this._elseViewRef =
this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
}
}
}
}
}
export class NgIfContext { public $implicit: any = null; }

View File

@ -21,9 +21,10 @@ import {SwitchView} from './ng_switch';
* @howToUse
* ```
* <some-element [ngPlural]="value">
* <template ngPluralCase="=0">there is nothing</template>
* <template ngPluralCase="=1">there is one</template>
* <template ngPluralCase="few">there are a few</template>
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
* </some-element>
* ```
*
@ -89,8 +90,8 @@ export class NgPlural {
* @howToUse
* ```
* <some-element [ngPlural]="value">
* <template ngPluralCase="=0">...</template>
* <template ngPluralCase="other">...</template>
* <ng-container *ngPluralCase="'=0'">...</ng-container>
* <ng-container *ngPluralCase="'other'">...</ng-container>
* </some-element>
*```
*
@ -103,7 +104,6 @@ export class NgPluralCase {
constructor(
@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
viewContainer: ViewContainerRef, @Host() ngPlural: NgPlural) {
const isANumber: boolean = !isNaN(Number(value));
ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template));
ngPlural.addCase(value, new SwitchView(viewContainer, template));
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
/**
* @ngModule CommonModule
@ -33,7 +33,7 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChanges, KeyValueDiffer,
@Directive({selector: '[ngStyle]'})
export class NgStyle implements DoCheck {
private _ngStyle: {[key: string]: string};
private _differ: KeyValueDiffer<string, string|number>;
private _differ: KeyValueDiffer;
constructor(
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
@ -55,16 +55,20 @@ export class NgStyle implements DoCheck {
}
}
private _applyChanges(changes: KeyValueChanges<string, string|number>): void {
changes.forEachRemovedItem((record) => this._setStyle(record.key, null));
changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue));
changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue));
private _applyChanges(changes: any): void {
changes.forEachRemovedItem((record: KeyValueChangeRecord) => this._setStyle(record.key, null));
changes.forEachAddedItem(
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
changes.forEachChangedItem(
(record: KeyValueChangeRecord) => this._setStyle(record.key, record.currentValue));
}
private _setStyle(nameAndUnit: string, value: string|number): void {
private _setStyle(nameAndUnit: string, value: string): void {
const [name, unit] = nameAndUnit.split('.');
value = value != null && unit ? `${value}${unit}` : value;
value = value && unit ? `${value}${unit}` : value;
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value as string);
this._renderer.setElementStyle(this._ngEl.nativeElement, name, value);
}
}

View File

@ -15,47 +15,42 @@ import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef
*
* @howToUse
* ```
* <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
* <template [ngTemplateOutlet]="templateRefExpression"
* [ngOutletContext]="objectExpression">
* </template>
* ```
*
* @description
*
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`.
* `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding
* by the local template `let` declarations.
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
* available within the `TemplateRef`.
*
* Note: using the key `$implicit` in the context object will set it's value as default.
*
* # Example
*
* {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
*
* @experimental
*/
@Directive({selector: '[ngTemplateOutlet]'})
export class NgTemplateOutlet implements OnChanges {
private _viewRef: EmbeddedViewRef<any>;
@Input() public ngTemplateOutletContext: Object;
@Input() public ngTemplateOutlet: TemplateRef<any>;
private _context: Object;
private _templateRef: TemplateRef<any>;
constructor(private _viewContainerRef: ViewContainerRef) {}
/**
* @deprecated v4.0.0 - Renamed to ngTemplateOutletContext.
*/
@Input()
set ngOutletContext(context: Object) { this.ngTemplateOutletContext = context; }
set ngOutletContext(context: Object) { this._context = context; }
@Input()
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
ngOnChanges(changes: SimpleChanges) {
if (this._viewRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
}
if (this.ngTemplateOutlet) {
this._viewRef = this._viewContainerRef.createEmbeddedView(
this.ngTemplateOutlet, this.ngTemplateOutletContext);
if (this._templateRef) {
this._viewRef = this._viewContainerRef.createEmbeddedView(this._templateRef, this._context);
}
}
}

View File

@ -49,10 +49,10 @@ export function getPluralCategory(
*/
@Injectable()
export class NgLocaleLocalization extends NgLocalization {
constructor(@Inject(LOCALE_ID) protected locale: string) { super(); }
constructor(@Inject(LOCALE_ID) private _locale: string) { super(); }
getPluralCategory(value: any): string {
const plural = getPluralCase(this.locale, value);
const plural = getPluralCase(this._locale, value);
switch (plural) {
case Plural.Zero:

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {InjectionToken} from '@angular/core';
import {OpaqueToken} from '@angular/core';
import {LocationChangeListener} from './platform_location';
/**
@ -61,4 +61,4 @@ export abstract class LocationStrategy {
*
* @stable
*/
export const APP_BASE_HREF = new InjectionToken<string>('appBaseHref');
export const APP_BASE_HREF: OpaqueToken = new OpaqueToken('appBaseHref');

View File

@ -1,72 +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 {Pipe, PipeTransform} from '@angular/core';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
* Transforms text to lowercase.
*
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe' }
*
* @stable
*/
@Pipe({name: 'lowercase'})
export class LowerCasePipe implements PipeTransform {
transform(value: string): string {
if (!value) return value;
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(LowerCasePipe, value);
}
return value.toLowerCase();
}
}
/**
* Helper method to transform a single word to titlecase.
*
* @stable
*/
function titleCaseWord(word: string) {
if (!word) return word;
return word[0].toUpperCase() + word.substr(1).toLowerCase();
}
/**
* Transforms text to titlecase.
*
* @stable
*/
@Pipe({name: 'titlecase'})
export class TitleCasePipe implements PipeTransform {
transform(value: string): string {
if (!value) return value;
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(TitleCasePipe, value);
}
return value.split(/\b/g).map(word => titleCaseWord(word)).join('');
}
}
/**
* Transforms text to uppercase.
*
* @stable
*/
@Pipe({name: 'uppercase'})
export class UpperCasePipe implements PipeTransform {
transform(value: string): string {
if (!value) return value;
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(UpperCasePipe, value);
}
return value.toUpperCase();
}
}

View File

@ -7,6 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {NgLocalization, getPluralCategory} from '../localization';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -34,7 +35,7 @@ export class I18nPluralPipe implements PipeTransform {
constructor(private _localization: NgLocalization) {}
transform(value: number, pluralMap: {[count: string]: string}): string {
if (value == null) return '';
if (isBlank(value)) return '';
if (typeof pluralMap !== 'object' || pluralMap === null) {
throw new InvalidPipeArgumentError(I18nPluralPipe, pluralMap);

View File

@ -12,13 +12,14 @@
* This module provides a set of common Pipes.
*/
import {AsyncPipe} from './async_pipe';
import {LowerCasePipe, TitleCasePipe, UpperCasePipe} from './case_conversion_pipes';
import {DatePipe} from './date_pipe';
import {I18nPluralPipe} from './i18n_plural_pipe';
import {I18nSelectPipe} from './i18n_select_pipe';
import {JsonPipe} from './json_pipe';
import {LowerCasePipe} from './lowercase_pipe';
import {CurrencyPipe, DecimalPipe, PercentPipe} from './number_pipe';
import {SlicePipe} from './slice_pipe';
import {UpperCasePipe} from './uppercase_pipe';
export {
AsyncPipe,
@ -31,11 +32,9 @@ export {
LowerCasePipe,
PercentPipe,
SlicePipe,
TitleCasePipe,
UpperCasePipe
};
/**
* A collection of Angular pipes that are likely to be used in each and every application.
*/
@ -47,7 +46,6 @@ export const COMMON_PIPES = [
SlicePipe,
DecimalPipe,
PercentPipe,
TitleCasePipe,
CurrencyPipe,
DatePipe,
I18nPluralPipe,

View File

@ -0,0 +1,37 @@
/**
* @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 {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
* @ngModule CommonModule
* @whatItDoes Transforms string to lowercase.
* @howToUse `expression | lowercase`
* @description
*
* Converts value into a lowercase string using `String.prototype.toLowerCase()`.
*
* ### Example
*
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
*
* @stable
*/
@Pipe({name: 'lowercase'})
export class LowerCasePipe implements PipeTransform {
transform(value: string): string {
if (isBlank(value)) return value;
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(LowerCasePipe, value);
}
return value.toLowerCase();
}
}

View File

@ -8,7 +8,7 @@
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
import {NumberWrapper} from '../facade/lang';
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
import {NumberFormatStyle, NumberFormatter} from './intl';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -18,7 +18,7 @@ const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
function formatNumber(
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
if (value == null) return null;
if (isBlank(value)) return null;
// Convert strings to numbers
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
@ -41,13 +41,13 @@ function formatNumber(
if (parts === null) {
throw new Error(`${digits} is not a valid digit info for number pipes`);
}
if (parts[1] != null) { // min integer digits
if (isPresent(parts[1])) { // min integer digits
minInt = NumberWrapper.parseIntAutoRadix(parts[1]);
}
if (parts[3] != null) { // min fraction digits
if (isPresent(parts[3])) { // min fraction digits
minFraction = NumberWrapper.parseIntAutoRadix(parts[3]);
}
if (parts[5] != null) { // max fraction digits
if (isPresent(parts[5])) { // max fraction digits
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
}
}

View File

@ -7,6 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
@ -57,7 +58,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@Pipe({name: 'slice', pure: false})
export class SlicePipe implements PipeTransform {
transform(value: any, start: number, end?: number): any {
if (value == null) return value;
if (isBlank(value)) return value;
if (!this.supports(value)) {
throw new InvalidPipeArgumentError(SlicePipe, value);

View File

@ -0,0 +1,36 @@
/**
* @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 {Pipe, PipeTransform} from '@angular/core';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
* @ngModule CommonModule
* @whatItDoes Transforms string to uppercase.
* @howToUse `expression | uppercase`
* @description
*
* Converts value into an uppercase string using `String.prototype.toUpperCase()`.
*
* ### Example
*
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
*
* @stable
*/
@Pipe({name: 'uppercase'})
export class UpperCasePipe implements PipeTransform {
transform(value: string): string {
if (isBlank(value)) return value;
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(UpperCasePipe, value);
}
return value.toUpperCase();
}
}

View File

@ -1,184 +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 {CommonModule} from '@angular/common';
import {NgComponentOutlet} from '@angular/common/src/directives/ng_component_outlet';
import {Component, ComponentRef, Inject, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, Optional, Provider, QueryList, ReflectiveInjector, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef} from '@angular/core';
import {TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('insert/remove', () => {
beforeEach(() => { TestBed.configureTestingModule({imports: [TestModule]}); });
it('should do nothing if component is null', async(() => {
const template = `<template *ngComponentOutlet="currentComponent"></template>`;
TestBed.overrideComponent(TestComponent, {set: {template: template}});
let fixture = TestBed.createComponent(TestComponent);
fixture.componentInstance.currentComponent = null;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
}));
it('should insert content specified by a component', async(() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
}));
it('should emit a ComponentRef once a component was created', async(() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
fixture.componentInstance.cmpRef = null;
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
expect(fixture.componentInstance.cmpRef).toBeAnInstanceOf(ComponentRef);
expect(fixture.componentInstance.cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
}));
it('should clear view if component becomes null', async(() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
fixture.componentInstance.currentComponent = null;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
}));
it('should swap content if component changes', async(() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('foo');
fixture.componentInstance.currentComponent = InjectedComponentAgain;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('bar');
}));
it('should use the injector, if one supplied', async(() => {
let fixture = TestBed.createComponent(TestComponent);
const uniqueValue = {};
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.componentInstance.injector = ReflectiveInjector.resolveAndCreate(
[{provide: TEST_TOKEN, useValue: uniqueValue}], fixture.componentRef.injector);
fixture.detectChanges();
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef;
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
expect(cmpRef.instance.testToken).toBe(uniqueValue);
}));
it('should resolve a with injector', async(() => {
let fixture = TestBed.createComponent(TestComponent);
fixture.componentInstance.cmpRef = null;
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.detectChanges();
let cmpRef: ComponentRef<InjectedComponent> = fixture.componentInstance.cmpRef;
expect(cmpRef).toBeAnInstanceOf(ComponentRef);
expect(cmpRef.instance).toBeAnInstanceOf(InjectedComponent);
expect(cmpRef.instance.testToken).toBeNull();
}));
it('should render projectable nodes, if supplied', async(() => {
const template = `<template>projected foo</template>${TEST_CMP_TEMPLATE}`;
TestBed.overrideComponent(TestComponent, {set: {template: template}})
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
TestBed
.overrideComponent(InjectedComponent, {set: {template: `<ng-content></ng-content>`}})
.configureTestingModule({schemas: [NO_ERRORS_SCHEMA]});
let fixture = TestBed.createComponent(TestComponent);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('');
fixture.componentInstance.currentComponent = InjectedComponent;
fixture.componentInstance.projectables =
[fixture.componentInstance.vcRef
.createEmbeddedView(fixture.componentInstance.tplRefs.first)
.rootNodes];
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('projected foo');
}));
});
}
const TEST_TOKEN = new InjectionToken('TestToken');
@Component({selector: 'injected-component', template: 'foo'})
class InjectedComponent {
constructor(@Optional() @Inject(TEST_TOKEN) public testToken: any) {}
}
@Component({selector: 'injected-component-again', template: 'bar'})
class InjectedComponentAgain {
}
const TEST_CMP_TEMPLATE =
`<template *ngComponentOutlet="currentComponent; injector: injector; content: projectables"></template>`;
@Component({selector: 'test-cmp', template: TEST_CMP_TEMPLATE})
class TestComponent {
currentComponent: Type<any>;
injector: Injector;
projectables: any[][];
get cmpRef(): ComponentRef<any> { return this.ngComponentOutlet.componentRef; }
set cmpRef(value: ComponentRef<any>) { this.ngComponentOutlet.componentRef = value; }
@ViewChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;
@ViewChild(NgComponentOutlet) ngComponentOutlet: NgComponentOutlet;
constructor(public vcRef: ViewContainerRef) {}
}
@NgModule({
imports: [CommonModule],
declarations: [TestComponent, InjectedComponent, InjectedComponentAgain],
exports: [TestComponent, InjectedComponent, InjectedComponentAgain],
entryComponents: [InjectedComponent, InjectedComponentAgain]
})
export class TestModule {
}

View File

@ -7,7 +7,7 @@
*/
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {Component, ContentChild, TemplateRef} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -29,7 +29,10 @@ export function main() {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [TestComponent],
declarations: [
TestComponent,
ComponentUsingTestComponent,
],
imports: [CommonModule],
});
});
@ -74,7 +77,7 @@ export function main() {
}));
it('should iterate over an array of objects', async(() => {
const template = '<ul><li *ngFor="let item of items">{{item["name"]}};</li></ul>';
const template = '<ul><li template="ngFor let item of items">{{item["name"]}};</li></ul>';
fixture = createTestComponent(template);
// INIT
@ -92,7 +95,7 @@ export function main() {
}));
it('should gracefully handle nulls', async(() => {
const template = '<ul><li *ngFor="let item of null">{{item}};</li></ul>';
const template = '<ul><li template="ngFor let item of null">{{item}};</li></ul>';
fixture = createTestComponent(template);
detectChangesAndExpectText('');
@ -137,8 +140,12 @@ export function main() {
}));
it('should repeat over nested arrays', async(() => {
const template = '<div *ngFor="let item of items">' +
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>|' +
const template = '<div>' +
'<div template="ngFor let item of items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div>|' +
'</div>' +
'</div>';
fixture = createTestComponent(template);
@ -150,9 +157,10 @@ export function main() {
}));
it('should repeat over nested arrays with no intermediate element', async(() => {
const template = '<div *ngFor="let item of items">' +
'<div *ngFor="let subitem of item">{{subitem}}-{{item.length}};</div>' +
'</div>';
const template = '<div><template ngFor let-item [ngForOf]="items">' +
'<div template="ngFor let subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div></template></div>';
fixture = createTestComponent(template);
getComponent().items = [['a', 'b'], ['c']];
@ -162,11 +170,10 @@ export function main() {
detectChangesAndExpectText('e-1;f-2;g-2;');
}));
it('should repeat over nested ngIf that are the last node in the ngFor template', async(() => {
const template = `<div *ngFor="let item of items; let i=index">` +
`<div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div>` +
`</div>`;
it('should repeat over nested ngIf that are the last node in the ngFor temlate', async(() => {
const template =
`<div><template ngFor let-item [ngForOf]="items" let-i="index"><div>{{i}}|</div>` +
`<div *ngIf="i % 2 == 0">even|</div></template></div>`;
fixture = createTestComponent(template);
@ -182,7 +189,8 @@ export function main() {
}));
it('should display indices correctly', async(() => {
const template = '<span *ngFor ="let item of items; let i=index">{{i.toString()}}</span>';
const template =
'<div><span template="ngFor: let item of items; let i=index">{{i.toString()}}</span></div>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
@ -194,7 +202,7 @@ export function main() {
it('should display first item correctly', async(() => {
const template =
'<span *ngFor="let item of items; let isFirst=first">{{isFirst.toString()}}</span>';
'<div><span template="ngFor: let item of items; let isFirst=first">{{isFirst.toString()}}</span></div>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -206,7 +214,7 @@ export function main() {
it('should display last item correctly', async(() => {
const template =
'<span *ngFor="let item of items; let isLast=last">{{isLast.toString()}}</span>';
'<div><span template="ngFor: let item of items; let isLast=last">{{isLast.toString()}}</span></div>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -218,7 +226,7 @@ export function main() {
it('should display even items correctly', async(() => {
const template =
'<span *ngFor="let item of items; let isEven=even">{{isEven.toString()}}</span>';
'<div><span template="ngFor: let item of items; let isEven=even">{{isEven.toString()}}</span></div>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2];
@ -230,7 +238,7 @@ export function main() {
it('should display odd items correctly', async(() => {
const template =
'<span *ngFor="let item of items; let isOdd=odd">{{isOdd.toString()}}</span>';
'<div><span template="ngFor: let item of items; let isOdd=odd">{{isOdd.toString()}}</span></div>';
fixture = createTestComponent(template);
getComponent().items = [0, 1, 2, 3];
@ -241,57 +249,54 @@ export function main() {
}));
it('should allow to use a custom template', async(() => {
const template =
'<ng-container *ngFor="let item of items; template: tpl"></ng-container>' +
'<template let-item let-i="index" #tpl><p>{{i}}: {{item}};</p></template>';
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
const tcTemplate =
'<ul><template ngFor [ngForOf]="items" [ngForTemplate]="contentTpl"></template></ul>';
TestBed.overrideComponent(TestComponent, {set: {template: tcTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
fixture.detectChanges();
detectChangesAndExpectText('0: a;1: b;2: c;');
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
}));
it('should use a default template if a custom one is null', async(() => {
const template =
`<ul><ng-container *ngFor="let item of items; template: null; let i=index">{{i}}: {{item}};</ng-container></ul>`;
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}: {{item}};</template></ul>`;
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
fixture.detectChanges();
detectChangesAndExpectText('0: a;1: b;2: c;');
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
}));
it('should use a custom template when both default and a custom one are present', async(() => {
const template =
'<ng-container *ngFor="let item of items; template: tpl">{{i}};</ng-container>' +
'<template let-item let-i="index" #tpl>{{i}}: {{item}};</template>';
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c'];
const testTemplate = `<ul><template ngFor let-item [ngForOf]="items"
[ngForTemplate]="contentTpl" let-i="index">{{i}}=> {{item}};</template></ul>`;
TestBed.overrideComponent(TestComponent, {set: {template: testTemplate}});
const cutTemplate =
'<test-cmp><li template="let item; let i=index">{{i}}: {{item}};</li></test-cmp>';
TestBed.overrideComponent(ComponentUsingTestComponent, {set: {template: cutTemplate}});
fixture = TestBed.createComponent(ComponentUsingTestComponent);
const testComponent = fixture.debugElement.children[0];
testComponent.componentInstance.items = ['a', 'b', 'c'];
fixture.detectChanges();
detectChangesAndExpectText('0: a;1: b;2: c;');
expect(testComponent.nativeElement).toHaveText('0: a;1: b;2: c;');
}));
describe('track by', () => {
it('should console.warn if trackBy is not a function', async(() => {
// TODO(vicb): expect a warning message when we have a proper log service
const template = `<p *ngFor="let item of items; trackBy: value"></p>`;
fixture = createTestComponent(template);
fixture.componentInstance.value = 0;
fixture.detectChanges();
}));
it('should track by identity when trackBy is to `null` or `undefined`', async(() => {
// TODO(vicb): expect no warning message when we have a proper log service
const template = `<p *ngFor="let item of items; trackBy: value">{{ item }}</p>`;
fixture = createTestComponent(template);
fixture.componentInstance.items = ['a', 'b', 'c'];
fixture.componentInstance.value = null;
detectChangesAndExpectText('abc');
fixture.componentInstance.value = undefined;
detectChangesAndExpectText('abc');
}));
it('should set the context to the component instance', async(() => {
const template =
`<p *ngFor="let item of items; trackBy: trackByContext.bind(this)"></p>`;
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByContext.bind(this)"></template>`;
fixture = createTestComponent(template);
thisArg = null;
@ -301,7 +306,9 @@ export function main() {
it('should not replace tracked items', async(() => {
const template =
`<p *ngFor="let item of items; trackBy: trackById; let i=index">{{items[i]}}</p>`;
`<template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById" let-i="index">
<p>{{items[i]}}</p>
</template>`;
fixture = createTestComponent(template);
const buildItemList = () => {
@ -317,7 +324,7 @@ export function main() {
it('should update implicit local variable on view', async(() => {
const template =
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
fixture = createTestComponent(template);
getComponent().items = [{'id': 'a', 'color': 'blue'}];
@ -326,10 +333,9 @@ export function main() {
getComponent().items = [{'id': 'a', 'color': 'red'}];
detectChangesAndExpectText('red');
}));
it('should move items around and keep them updated ', async(() => {
const template =
`<div *ngFor="let item of items; trackBy: trackById">{{item['color']}}</div>`;
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackById">{{item['color']}}</template></div>`;
fixture = createTestComponent(template);
getComponent().items = [{'id': 'a', 'color': 'blue'}, {'id': 'b', 'color': 'yellow'}];
@ -340,7 +346,8 @@ export function main() {
}));
it('should handle added and removed items properly when tracking by index', async(() => {
const template = `<div *ngFor="let item of items; trackBy: trackByIndex">{{item}}</div>`;
const template =
`<div><template ngFor let-item [ngForOf]="items" [ngForTrackBy]="trackByIndex">{{item}}</template></div>`;
fixture = createTestComponent(template);
getComponent().items = ['a', 'b', 'c', 'd'];
@ -360,14 +367,19 @@ class Foo {
@Component({selector: 'test-cmp', template: ''})
class TestComponent {
value: any;
@ContentChild(TemplateRef) contentTpl: TemplateRef<Object>;
items: any[] = [1, 2];
trackById(index: number, item: any): string { return item['id']; }
trackByIndex(index: number, item: any): number { return index; }
trackByContext(): void { thisArg = this; }
}
const TEMPLATE = '<div><span *ngFor="let item of items">{{item.toString()}};</span></div>';
@Component({selector: 'outer-cmp', template: ''})
class ComponentUsingTestComponent {
items: any = [1, 2];
}
const TEMPLATE = '<div><span template="ngFor let item of items">{{item.toString()}};</span></div>';
function createTestComponent(template: string = TEMPLATE): ComponentFixture<TestComponent> {
return TestBed.overrideComponent(TestComponent, {set: {template: template}})

View File

@ -9,7 +9,6 @@
import {CommonModule} from '@angular/common';
import {Component} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {By} from '@angular/platform-browser/src/dom/debug/by';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
import {expect} from '@angular/platform-browser/testing/matchers';
@ -29,180 +28,131 @@ export function main() {
});
it('should work in a template attribute', async(() => {
const template = '<span *ngIf="booleanCondition">hello</span>';
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
}));
it('should work on a template element', async(() => {
const template = '<template [ngIf]="booleanCondition">hello2</template>';
it('should work in a template element', async(() => {
const template =
'<div><template [ngIf]="booleanCondition"><span>hello2</span></template></div>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello2');
}));
it('should toggle node when condition changes', async(() => {
const template = '<span *ngIf="booleanCondition">hello</span>';
const template = '<div><span template="ngIf booleanCondition">hello</span></div>';
fixture = createTestComponent(template);
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().booleanCondition = true;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
}));
it('should handle nested if correctly', async(() => {
const template =
'<div *ngIf="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></div>';
'<div><template [ngIf]="booleanCondition"><span *ngIf="nestedBooleanCondition">hello</span></template></div>';
fixture = createTestComponent(template);
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().booleanCondition = true;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().nestedBooleanCondition = false;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
getComponent().nestedBooleanCondition = true;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(0);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(0);
expect(fixture.nativeElement).toHaveText('');
}));
it('should update several nodes with if', async(() => {
const template = '<span *ngIf="numberCondition + 1 >= 2">helloNumber</span>' +
'<span *ngIf="stringCondition == \'foo\'">helloString</span>' +
'<span *ngIf="functionCondition(stringCondition, numberCondition)">helloFunction</span>';
const template = '<div>' +
'<span template="ngIf numberCondition + 1 >= 2">helloNumber</span>' +
'<span template="ngIf stringCondition == \'foo\'">helloString</span>' +
'<span template="ngIf functionCondition(stringCondition, numberCondition)">helloFunction</span>' +
'</div>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(3);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(3);
expect(getDOM().getText(fixture.nativeElement))
.toEqual('helloNumberhelloStringhelloFunction');
getComponent().numberCondition = 0;
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('helloString');
getComponent().numberCondition = 1;
getComponent().stringCondition = 'bar';
fixture.detectChanges();
expect(fixture.debugElement.queryAll(By.css('span')).length).toEqual(1);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('helloNumber');
}));
it('should not add the element twice if the condition goes from truthy to truthy', async(() => {
const template = '<span *ngIf="numberCondition">hello</span>';
it('should not add the element twice if the condition goes from true to true (JS)',
async(() => {
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
fixture = createTestComponent(template);
fixture.detectChanges();
let els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
getDOM().addClass(els[0].nativeElement, 'marker');
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
getComponent().numberCondition = 2;
fixture.detectChanges();
els = fixture.debugElement.queryAll(By.css('span'));
expect(els.length).toEqual(1);
expect(getDOM().hasClass(els[0].nativeElement, 'marker')).toBe(true);
expect(getDOM().querySelectorAll(fixture.nativeElement, 'span').length).toEqual(1);
expect(fixture.nativeElement).toHaveText('hello');
}));
describe('else', () => {
it('should support else', async(() => {
const template = '<span *ngIf="booleanCondition; else elseBlock">TRUE</span>' +
'<template #elseBlock>FALSE</template>';
it('should not recreate the element if the condition goes from true to true (JS)', async(() => {
const template = '<div><span template="ngIf numberCondition">hello</span></div>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('TRUE');
getDOM().addClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo');
getComponent().booleanCondition = false;
getComponent().numberCondition = 2;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('FALSE');
expect(getDOM().hasClass(getDOM().querySelector(fixture.nativeElement, 'span'), 'foo'))
.toBe(true);
}));
it('should support then and else', async(() => {
const template =
'<span *ngIf="booleanCondition; then thenBlock; else elseBlock">IGNORE</span>' +
'<template #thenBlock>THEN</template>' +
'<template #elseBlock>ELSE</template>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('THEN');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('ELSE');
}));
it('should support dynamic else', async(() => {
const template =
'<span *ngIf="booleanCondition; else nestedBooleanCondition ? b1 : b2">TRUE</span>' +
'<template #b1>FALSE1</template>' +
'<template #b2>FALSE2</template>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('TRUE');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('FALSE1');
getComponent().nestedBooleanCondition = false;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('FALSE2');
}));
it('should support binding to variable', async(() => {
const template = '<span *ngIf="booleanCondition; else elseBlock; let v">{{v}}</span>' +
'<template #elseBlock let-v>{{v}}</template>';
fixture = createTestComponent(template);
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('true');
getComponent().booleanCondition = false;
fixture.detectChanges();
expect(fixture.nativeElement).toHaveText('false');
}));
});
});
}

View File

@ -12,7 +12,7 @@ import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('ngPlural', () => {
describe('switch', () => {
let fixture: ComponentFixture<any>;
function getComponent(): TestComponent { return fixture.componentInstance; }
@ -33,25 +33,10 @@ export function main() {
});
it('should display the template according to the exact value', async(() => {
const template = '<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
'</ul>';
fixture = createTestComponent(template);
getComponent().switchValue = 0;
detectChangesAndExpectText('you have no messages.');
getComponent().switchValue = 1;
detectChangesAndExpectText('you have one message.');
}));
it('should display the template according to the exact numeric value', async(() => {
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="1"><li>you have one message.</li></template>' +
'<template ngPluralCase="=0"><li>you have no messages.</li></template>' +
'<template ngPluralCase="=1"><li>you have one message.</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
@ -66,9 +51,10 @@ export function main() {
// https://github.com/angular/angular/issues/9868
// https://github.com/angular/angular/issues/9882
it('should not throw when ngPluralCase contains expressions', async(() => {
const template = '<ul [ngPlural]="switchValue">' +
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="=0"><li>{{ switchValue }}</li></template>' +
'</ul>';
'</ul></div>';
fixture = createTestComponent(template);
@ -78,10 +64,11 @@ export function main() {
it('should be applicable to <ng-container> elements', async(() => {
const template = '<ng-container [ngPlural]="switchValue">' +
const template = '<div>' +
'<ng-container [ngPlural]="switchValue">' +
'<template ngPluralCase="=0">you have no messages.</template>' +
'<template ngPluralCase="=1">you have one message.</template>' +
'</ng-container>';
'</ng-container></div>';
fixture = createTestComponent(template);
@ -93,10 +80,11 @@ export function main() {
}));
it('should display the template according to the category', async(() => {
const template = '<ul [ngPlural]="switchValue">' +
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="many"><li>you have many messages.</li></template>' +
'</ul>';
'</ul></div>';
fixture = createTestComponent(template);
@ -108,10 +96,11 @@ export function main() {
}));
it('should default to other when no matches are found', async(() => {
const template = '<ul [ngPlural]="switchValue">' +
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="other"><li>default message.</li></template>' +
'</ul>';
'</ul></div>';
fixture = createTestComponent(template);
@ -120,10 +109,11 @@ export function main() {
}));
it('should prioritize value matches over category matches', async(() => {
const template = '<ul [ngPlural]="switchValue">' +
const template = '<div>' +
'<ul [ngPlural]="switchValue">' +
'<template ngPluralCase="few"><li>you have a few messages.</li></template>' +
'<template ngPluralCase="=2">you have two messages.</template>' +
'</ul>';
'</ul></div>';
fixture = createTestComponent(template);

View File

@ -29,19 +29,22 @@ export function main() {
it('should add styles specified in an object literal', async(() => {
const template = `<div [ngStyle]="{'max-width': '40px'}"></div>`;
fixture = createTestComponent(template);
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
}));
it('should add and change styles specified in an object expression', async(() => {
const template = `<div [ngStyle]="expr"></div>`;
fixture = createTestComponent(template);
let expr: {[k: string]: string};
getComponent().expr = {'max-width': '40px'};
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '40px'});
let expr = getComponent().expr;
expr = getComponent().expr;
expr['max-width'] = '30%';
fixture.detectChanges();
expectNativeEl(fixture).toHaveCssStyle({'max-width': '30%'});

View File

@ -33,10 +33,11 @@ export function main() {
describe('switch value changes', () => {
it('should switch amongst when values', () => {
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a</li>' +
'<li *ngSwitchCase="\'b\'">when b</li>' +
'</ul>';
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a</li></template>' +
'<template ngSwitchCase="b"><li>when b</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
@ -50,10 +51,11 @@ export function main() {
});
it('should switch amongst when values with fallback to default', () => {
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a</li>' +
'<li *ngSwitchDefault>when default</li>' +
'</ul>';
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<li template="ngSwitchCase \'a\'">when a</li>' +
'<li template="ngSwitchDefault">when default</li>' +
'</ul></div>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default');
@ -69,14 +71,15 @@ export function main() {
});
it('should support multiple whens with the same value', () => {
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'</ul>';
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');
@ -91,11 +94,12 @@ export function main() {
describe('when values changes', () => {
it('should switch amongst when values', () => {
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchCase="when1">when 1;</li>' +
'<li *ngSwitchCase="when2">when 2;</li>' +
'<li *ngSwitchDefault>when default;</li>' +
'</ul>';
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template [ngSwitchCase]="when1"><li>when 1;</li></template>' +
'<template [ngSwitchCase]="when2"><li>when 2;</li></template>' +
'<template ngSwitchDefault><li>when default;</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
getComponent().when1 = 'a';
@ -144,10 +148,11 @@ export function main() {
});
it('should create the default case if there is no other case', () => {
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'</ul>';
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');
@ -155,14 +160,15 @@ export function main() {
});
it('should allow defaults before cases', () => {
const template = '<ul [ngSwitch]="switchValue">' +
'<li *ngSwitchDefault>when default1;</li>' +
'<li *ngSwitchDefault>when default2;</li>' +
'<li *ngSwitchCase="\'a\'">when a1;</li>' +
'<li *ngSwitchCase="\'b\'">when b1;</li>' +
'<li *ngSwitchCase="\'a\'">when a2;</li>' +
'<li *ngSwitchCase="\'b\'">when b2;</li>' +
'</ul>';
const template = '<div>' +
'<ul [ngSwitch]="switchValue">' +
'<template ngSwitchDefault><li>when default1;</li></template>' +
'<template ngSwitchDefault><li>when default2;</li></template>' +
'<template ngSwitchCase="a"><li>when a1;</li></template>' +
'<template ngSwitchCase="b"><li>when b1;</li></template>' +
'<template ngSwitchCase="a"><li>when a2;</li></template>' +
'<template ngSwitchCase="b"><li>when b2;</li></template>' +
'</ul></div>';
fixture = createTestComponent(template);
detectChangesAndExpectText('when default1;when default2;');

View File

@ -34,22 +34,29 @@ export function main() {
});
});
it('should do nothing if templateRef is `null`', async(() => {
const template = `<ng-container [ngTemplateOutlet]="null"></ng-container>`;
it('should do nothing if templateRef is null', async(() => {
const template = `<template [ngTemplateOutlet]="null"></template>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
}));
it('should insert content specified by TemplateRef', async(() => {
const template = `<template #tpl>foo</template>` +
`<ng-container [ngTemplateOutlet]="tpl"></ng-container>`;
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('foo');
}));
it('should clear content if TemplateRef becomes `null`', async(() => {
const template = `<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs>` +
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
it('should clear content if TemplateRef becomes null', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
@ -63,8 +70,7 @@ export function main() {
it('should swap content if TemplateRef changes', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs>` +
`<ng-container [ngTemplateOutlet]="currentTplRef"></ng-container>`;
`<tpl-refs #refs="tplRefs"><template>foo</template><template>bar</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef"></template>`;
fixture = createTestComponent(template);
fixture.detectChanges();
@ -77,47 +83,70 @@ export function main() {
detectChangesAndExpectText('bar');
}));
it('should display template if context is `null`', async(() => {
const template = `<template #tpl>foo</template>` +
`<ng-container *ngTemplateOutlet="tpl; context: null"></ng-container>`;
it('should display template if context is null', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template>foo</template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="null"></template>`;
fixture = createTestComponent(template);
detectChangesAndExpectText('');
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('foo');
}));
it('should reflect initial context and changes', async(() => {
const template = `<template let-foo="foo" #tpl>{{foo}}</template>` +
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
const template =
`<tpl-refs #refs="tplRefs"><template let-foo="foo"><span>{{foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
detectChangesAndExpectText('bar');
fixture.componentInstance.context.foo = 'alter-bar';
detectChangesAndExpectText('alter-bar');
}));
it('should reflect user defined `$implicit` property in the context', async(() => {
const template = `<template let-ctx #tpl>{{ctx.foo}}</template>` +
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
it('should reflect user defined $implicit property in the context', async(() => {
const template =
`<tpl-refs #refs="tplRefs"><template let-ctx><span>{{ctx.foo}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
fixture = createTestComponent(template);
fixture.componentInstance.context = {$implicit: {foo: 'bra'}};
detectChangesAndExpectText('bra');
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
fixture.componentInstance.context = {$implicit: fixture.componentInstance.context};
detectChangesAndExpectText('bar');
}));
it('should reflect context re-binding', async(() => {
const template = `<template let-shawshank="shawshank" #tpl>{{shawshank}}</template>` +
`<ng-container *ngTemplateOutlet="tpl; context: context"></ng-container>`;
const template =
`<tpl-refs #refs="tplRefs"><template let-shawshank="shawshank"><span>{{shawshank}}</span></template></tpl-refs><template [ngTemplateOutlet]="currentTplRef" [ngOutletContext]="context"></template>`;
fixture = createTestComponent(template);
fixture.detectChanges();
const refs = fixture.debugElement.children[0].references['refs'];
setTplRef(refs.tplRefs.first);
fixture.componentInstance.context = {shawshank: 'brooks'};
detectChangesAndExpectText('brooks');
fixture.componentInstance.context = {shawshank: 'was here'};
detectChangesAndExpectText('was here');
}));
});
}
@Directive({selector: 'tpl-refs', exportAs: 'tplRefs'})
class CaptureTplRefs {
@ContentChildren(TemplateRef) tplRefs: QueryList<TemplateRef<any>>;

View File

@ -1,67 +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 {LowerCasePipe, TitleCasePipe, UpperCasePipe} from '@angular/common';
export function main() {
describe('LowerCasePipe', () => {
let pipe: LowerCasePipe;
beforeEach(() => { pipe = new LowerCasePipe(); });
it('should return lowercase', () => { expect(pipe.transform('FOO')).toEqual('foo'); });
it('should lowercase when there is a new value', () => {
expect(pipe.transform('FOO')).toEqual('foo');
expect(pipe.transform('BAr')).toEqual('bar');
});
it('should not support other objects',
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
});
describe('TitleCasePipe', () => {
let pipe: TitleCasePipe;
beforeEach(() => { pipe = new TitleCasePipe(); });
it('should return titlecase', () => { expect(pipe.transform('foo')).toEqual('Foo'); });
it('should return titlecase for subsequent words',
() => { expect(pipe.transform('one TWO Three fouR')).toEqual('One Two Three Four'); });
it('should support empty strings', () => { expect(pipe.transform('')).toEqual(''); });
it('should persist whitespace',
() => { expect(pipe.transform('one two')).toEqual('One Two'); });
it('should titlecase when there is a new value', () => {
expect(pipe.transform('bar')).toEqual('Bar');
expect(pipe.transform('foo')).toEqual('Foo');
});
it('should not support other objects',
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
});
describe('UpperCasePipe', () => {
let pipe: UpperCasePipe;
beforeEach(() => { pipe = new UpperCasePipe(); });
it('should return uppercase', () => { expect(pipe.transform('foo')).toEqual('FOO'); });
it('should uppercase when there is a new value', () => {
expect(pipe.transform('foo')).toEqual('FOO');
expect(pipe.transform('bar')).toEqual('BAR');
});
it('should not support other objects',
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
});
}

View File

@ -0,0 +1,42 @@
/**
* @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 {LowerCasePipe} from '@angular/common';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
export function main() {
describe('LowerCasePipe', () => {
let upper: string;
let lower: string;
let pipe: LowerCasePipe;
beforeEach(() => {
lower = 'something';
upper = 'SOMETHING';
pipe = new LowerCasePipe();
});
describe('transform', () => {
it('should return lowercase', () => {
const val = pipe.transform(upper);
expect(val).toEqual(lower);
});
it('should lowercase when there is a new value', () => {
const val = pipe.transform(upper);
expect(val).toEqual(lower);
const val2 = pipe.transform('WAT');
expect(val2).toEqual('wat');
});
it('should not support other objects',
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
});
});
}

View File

@ -0,0 +1,43 @@
/**
* @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 {UpperCasePipe} from '@angular/common';
import {beforeEach, describe, expect, it} from '@angular/core/testing/testing_internal';
export function main() {
describe('UpperCasePipe', () => {
let upper: string;
let lower: string;
let pipe: UpperCasePipe;
beforeEach(() => {
lower = 'something';
upper = 'SOMETHING';
pipe = new UpperCasePipe();
});
describe('transform', () => {
it('should return uppercase', () => {
const val = pipe.transform(lower);
expect(val).toEqual(upper);
});
it('should uppercase when there is a new value', () => {
const val = pipe.transform(lower);
expect(val).toEqual(upper);
const val2 = pipe.transform('wat');
expect(val2).toEqual('WAT');
});
it('should not support other objects',
() => { expect(() => pipe.transform(<any>{})).toThrowError(); });
});
});
}

View File

@ -1,23 +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 {Component, NgModule} from '@angular/core';
import {RouterModule} from '@angular/router';
@Component({selector: 'lazy-feature-comp', template: 'lazy feature, nested!'})
export class LazyFeatureNestedComponent {
}
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyFeatureNestedComponent, pathMatch: 'full'},
])],
declarations: [LazyFeatureNestedComponent]
})
export class LazyFeatureNestedModule {
}

View File

@ -16,10 +16,7 @@ export class LazyFeatureComponent {
@NgModule({
imports: [RouterModule.forChild([
{path: '', component: LazyFeatureComponent, pathMatch: 'full'},
{path: 'feature', loadChildren: './feature.module#FeatureModule'}, {
path: 'nested-feature',
loadChildren: './lazy-feature-nested.module#LazyFeatureNestedModule'
}
{path: 'feature', loadChildren: './feature.module#FeatureModule'}
])],
declarations: [LazyFeatureComponent]
})

View File

@ -8,10 +8,6 @@
import {AUTO_STYLE, Component, animate, state, style, transition, trigger} from '@angular/core';
export function anyToAny(stateA: string, stateB: string): boolean {
return Math.random() != Math.random();
}
@Component({
selector: 'animate-cmp',
animations: [trigger(
@ -20,7 +16,7 @@ export function anyToAny(stateA: string, stateB: string): boolean {
state('*', style({height: AUTO_STYLE, color: 'black', borderColor: 'black'})),
state('closed, void', style({height: '0px', color: 'maroon', borderColor: 'maroon'})),
state('open', style({height: AUTO_STYLE, borderColor: 'green', color: 'green'})),
transition(anyToAny, animate('1s')), transition('* => *', animate(500))
transition('* => *', animate(500))
])],
template: `
<button (click)="setAsOpen()">Open</button>

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Inject, InjectionToken} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Inject, OpaqueToken} from '@angular/core';
import {BasicComp} from './basic';
@ -15,7 +15,7 @@ export class CompWithEntryComponents {
constructor(public cfr: ComponentFactoryResolver) {}
}
export const SOME_TOKEN = new InjectionToken('someToken');
export const SOME_TOKEN = new OpaqueToken('someToken');
export function provideValueWithEntryComponents(value: any) {
return [

View File

@ -7,21 +7,21 @@
*/
import * as common from '@angular/common';
import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, InjectionToken, NgModule, Output} from '@angular/core';
import {CUSTOM_ELEMENTS_SCHEMA, Component, Directive, EventEmitter, Inject, NgModule, OpaqueToken, Output} from '@angular/core';
import {Observable} from 'rxjs/Observable';
import {wrapInArray} from './funcs';
export const SOME_INJECTON_TOKEN = new InjectionToken('injectionToken');
export const SOME_OPAQUE_TOKEN = new OpaqueToken('opaqueToken');
@Component({
selector: 'comp-providers',
template: '',
providers: [
{provide: 'strToken', useValue: 'strValue'},
{provide: SOME_INJECTON_TOKEN, useValue: 10},
{provide: SOME_OPAQUE_TOKEN, useValue: 10},
{provide: 'reference', useValue: common.NgIf},
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_INJECTON_TOKEN]}},
{provide: 'complexToken', useValue: {a: 1, b: ['test', SOME_OPAQUE_TOKEN]}},
]
})
export class CompWithProviders {

View File

@ -7,7 +7,7 @@
*/
import {LowerCasePipe, NgIf} from '@angular/common';
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, InjectionToken, Input, ModuleWithProviders, NgModule, Pipe} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, Component, ComponentFactoryResolver, Directive, Inject, Injectable, Input, ModuleWithProviders, NgModule, OpaqueToken, Pipe} from '@angular/core';
@Injectable()
export class SomeService {
@ -48,7 +48,7 @@ export class CompUsingRootModuleDirectiveAndPipe {
export class CompUsingLibModuleDirectiveAndPipe {
}
export const SOME_TOKEN = new InjectionToken('someToken');
export const SOME_TOKEN = new OpaqueToken('someToken');
export function provideValueWithEntryComponents(value: any) {
return [

View File

@ -191,8 +191,6 @@ function lazyRoutesTest() {
'./lazy.module#LazyModule': 'lazy.module.ts',
'./feature/feature.module#FeatureModule': 'feature/feature.module.ts',
'./feature/lazy-feature.module#LazyFeatureModule': 'feature/lazy-feature.module.ts',
'./feature.module#FeatureModule': 'feature/feature.module.ts',
'./lazy-feature-nested.module#LazyFeatureNestedModule': 'feature/lazy-feature-nested.module.ts',
'feature2/feature2.module#Feature2Module': 'feature2/feature2.module.ts',
'./default.module': 'feature2/default.module.ts',
'feature/feature.module#FeatureModule': 'feature/feature.module.ts'

View File

@ -15,8 +15,6 @@
"declaration": true,
"lib": ["es6", "dom"],
"baseUrl": ".",
"outDir": "../node_modules/third_party",
// Prevent scanning up the directory tree for types
"typeRoots": ["node_modules/@types"]
"outDir": "../node_modules/third_party"
}
}

View File

@ -14,9 +14,7 @@
"rootDir": "",
"declaration": true,
"lib": ["es6", "dom"],
"baseUrl": ".",
// Prevent scanning up the directory tree for types
"typeRoots": ["node_modules/@types"]
"baseUrl": "."
},
"files": [

View File

@ -9,7 +9,7 @@
"ng-xi18n": "./src/extract_i18n.js"
},
"dependencies": {
"@angular/tsc-wrapped": "4.0.0-beta.2",
"@angular/tsc-wrapped": "0.5.0",
"reflect-metadata": "^0.1.2",
"minimist": "^1.2.0"
},

View File

@ -11,6 +11,7 @@
* Intended to be used in a build step.
*/
import * as compiler from '@angular/compiler';
import {ViewEncapsulation} from '@angular/core';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import {readFileSync} from 'fs';
import * as path from 'path';
@ -18,6 +19,7 @@ import * as ts from 'typescript';
import {CompilerHost, CompilerHostContext, ModuleResolutionHostAdapter} from './compiler_host';
import {PathMappedCompilerHost} from './path_mapped_compiler_host';
import {Console} from './private_import_core';
const GENERATED_META_FILES = /\.json$/;
@ -36,6 +38,29 @@ export class CodeGenerator {
public host: ts.CompilerHost, private compiler: compiler.AotCompiler,
private ngCompilerHost: CompilerHost) {}
// Write codegen in a directory structure matching the sources.
private calculateEmitPath(filePath: string): string {
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
codegen(): Promise<any> {
return this.compiler
.compileAll(this.program.getSourceFiles().map(
@ -43,7 +68,7 @@ export class CodeGenerator {
.then(generatedModules => {
generatedModules.forEach(generatedModule => {
const sourceFile = this.program.getSourceFile(generatedModule.srcFileUrl);
const emitPath = this.ngCompilerHost.calculateEmitPath(generatedModule.genFileUrl);
const emitPath = this.calculateEmitPath(generatedModule.genFileUrl);
const source = GENERATED_META_FILES.test(emitPath) ? generatedModule.source :
PREAMBLE + generatedModule.source;
this.host.writeFile(emitPath, source, false, () => {}, [sourceFile]);

View File

@ -261,29 +261,6 @@ export class CompilerHost implements AotCompilerHost {
this.options.generateCodeForLibraries === false ? GENERATED_OR_DTS_FILES : GENERATED_FILES;
return !excludeRegex.test(filePath);
}
calculateEmitPath(filePath: string): string {
// Write codegen in a directory structure matching the sources.
let root = this.options.basePath;
for (const eachRootDir of this.options.rootDirs || []) {
if (this.options.trace) {
console.error(`Check if ${filePath} is under rootDirs element ${eachRootDir}`);
}
if (path.relative(eachRootDir, filePath).indexOf('.') !== 0) {
root = eachRootDir;
}
}
// transplant the codegen path to be inside the `genDir`
let relativePath: string = path.relative(root, filePath);
while (relativePath.startsWith('..' + path.sep)) {
// Strip out any `..` path such as: `../node_modules/@foo` as we want to put everything
// into `genDir`.
relativePath = relativePath.substr(3);
}
return path.join(this.options.genDir, relativePath);
}
}
export class CompilerHostContextAdapter {

View File

@ -15,6 +15,8 @@
import {AotCompilerHost, StaticReflector, StaticSymbol} from '@angular/compiler';
import {NgModule} from '@angular/core';
// We cannot depend directly to @angular/router.
type Route = any;
const ROUTER_MODULE_PATH = '@angular/router/src/router_config_loader';
@ -61,37 +63,29 @@ export function listLazyRoutesOfModule(
const className = entryRouteDef.className;
// List loadChildren of this single module.
const appStaticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
const staticSymbol = reflector.findDeclaration(modulePath, className, containingFile);
const ROUTES = reflector.findDeclaration(ROUTER_MODULE_PATH, ROUTER_ROUTES_SYMBOL_NAME);
const lazyRoutes: LazyRoute[] =
_extractLazyRoutesFromStaticModule(appStaticSymbol, reflector, host, ROUTES);
_extractLazyRoutesFromStaticModule(staticSymbol, reflector, host, ROUTES);
const routes: LazyRouteMap = {};
const allLazyRoutes = lazyRoutes.reduce(
function includeLazyRouteAndSubRoutes(allRoutes: LazyRouteMap, lazyRoute: LazyRoute):
LazyRouteMap {
lazyRoutes.forEach((lazyRoute: LazyRoute) => {
const route: string = lazyRoute.routeDef.toString();
_assertRoute(allRoutes, lazyRoute);
allRoutes[route] = lazyRoute;
// StaticReflector does not support discovering annotations like `NgModule` on default
// exports
// Which means: if a default export NgModule was lazy-loaded, we can discover it, but,
// we cannot parse its routes to see if they have loadChildren or not.
if (!lazyRoute.routeDef.className) {
return allRoutes;
}
_assertRoute(routes, lazyRoute);
routes[route] = lazyRoute;
const lazyModuleSymbol = reflector.findDeclaration(
lazyRoute.absoluteFilePath, lazyRoute.routeDef.className || 'default');
const subRoutes = _extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
const subRoutes =
_extractLazyRoutesFromStaticModule(lazyModuleSymbol, reflector, host, ROUTES);
// Populate the map using the routes we just found.
subRoutes.forEach(subRoute => {
_assertRoute(routes, subRoute);
routes[subRoute.routeDef.toString()] = subRoute;
});
});
return subRoutes.reduce(includeLazyRouteAndSubRoutes, allRoutes);
},
{});
return allLazyRoutes;
return routes;
}
@ -198,7 +192,7 @@ function _collectRoutes(
*/
function _collectLoadChildren(routes: Route[]): string[] {
return routes.reduce((m, r) => {
if (r.loadChildren && typeof r.loadChildren === 'string') {
if (r.loadChildren) {
return m.concat(r.loadChildren);
} else if (Array.isArray(r)) {
return m.concat(_collectLoadChildren(r));

View File

@ -9,10 +9,10 @@
import {__core_private__ as r} from '@angular/core';
export type ReflectorReader = typeof r._ReflectorReader;
export const ReflectorReader: typeof r.ReflectorReader = r.ReflectorReader;
export var ReflectorReader: typeof r.ReflectorReader = r.ReflectorReader;
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
export const ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export type Console = typeof r._Console;
export const Console: typeof r.Console = r.Console;
export var Console: typeof r.Console = r.Console;

View File

@ -122,9 +122,11 @@ export class MockCompilerHost implements ts.CompilerHost {
return ts.getDefaultLibFileName(options);
}
writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.writeFile(fileName, text); };
writeFile: ts.WriteFileCallback = (fileName, text) => { this.context.writeFile(fileName, text); }
getCurrentDirectory(): string { return this.context.currentDirectory; }
getCurrentDirectory(): string {
return this.context.currentDirectory;
}
getCanonicalFileName(fileName: string): string { return fileName; }

View File

@ -9,5 +9,5 @@
import {__core_private__ as r} from '@angular/core';
export type ReflectionCapabilities = typeof r._ReflectionCapabilities;
export const ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export const reflector: typeof r.reflector = r.reflector;
export var ReflectionCapabilities: typeof r.ReflectionCapabilities = r.ReflectionCapabilities;
export var reflector: typeof r.reflector = r.reflector;

View File

@ -5,7 +5,6 @@
* 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 {StaticSymbol} from '../aot/static_symbol';
export abstract class AnimationAst {
public startTime: number = 0;
@ -50,10 +49,6 @@ export class AnimationStateTransitionExpression {
constructor(public fromState: string, public toState: string) {}
}
export class AnimationStateTransitionFnExpression extends AnimationStateTransitionExpression {
constructor(public fn: Function|StaticSymbol) { super(null, null); }
}
export class AnimationStateTransitionAst extends AnimationStateAst {
constructor(
public stateChanges: AnimationStateTransitionExpression[],

View File

@ -12,7 +12,7 @@ import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {ANY_STATE, DEFAULT_STATE, EMPTY_STATE} from '../private_import_core';
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionFnExpression, AnimationStepAst, AnimationStylesAst} from './animation_ast';
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
export class AnimationEntryCompileResult {
constructor(public name: string, public statements: o.Statement[], public fnExp: o.Expression) {}
@ -66,7 +66,7 @@ class _AnimationBuilder implements AnimationAstVisitor {
ast.styles.forEach(entry => {
const entries =
Object.keys(entry).map((key): [string, o.Expression] => [key, o.literal(entry[key])]);
stylesArr.push(o.literalMap(entries, null, true));
stylesArr.push(o.literalMap(entries));
});
return o.importExpr(createIdentifier(Identifiers.AnimationStyles)).instantiate([
@ -162,11 +162,6 @@ class _AnimationBuilder implements AnimationAstVisitor {
const stateChangePreconditions: o.Expression[] = [];
ast.stateChanges.forEach(stateChange => {
if (stateChange instanceof AnimationStateTransitionFnExpression) {
stateChangePreconditions.push(o.importExpr({reference: stateChange.fn}).callFn([
_ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR
]));
} else {
stateChangePreconditions.push(
_compareToAnimationStateExpr(_ANIMATION_CURRENT_STATE_VAR, stateChange.fromState)
.and(_compareToAnimationStateExpr(_ANIMATION_NEXT_STATE_VAR, stateChange.toState)));
@ -178,7 +173,6 @@ class _AnimationBuilder implements AnimationAstVisitor {
if (stateChange.toState != ANY_STATE) {
context.stateMap.registerState(stateChange.toState);
}
}
});
const animationPlayerExpr = ast.animation.visit(this, context);
@ -300,8 +294,8 @@ class _AnimationBuilder implements AnimationAstVisitor {
statements.push(new o.ReturnStatement(
o.importExpr(createIdentifier(Identifiers.AnimationTransition)).instantiate([
_ANIMATION_PLAYER_VAR, _ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
_ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR, _ANIMATION_TIME_VAR
_ANIMATION_PLAYER_VAR, _ANIMATION_CURRENT_STATE_VAR, _ANIMATION_NEXT_STATE_VAR,
_ANIMATION_TIME_VAR
])));
return o.fn(
@ -328,13 +322,12 @@ class _AnimationBuilder implements AnimationAstVisitor {
if (isPresent(value)) {
const styleMap: any[] = [];
Object.keys(value).forEach(key => { styleMap.push([key, o.literal(value[key])]); });
variableValue = o.literalMap(styleMap, null, true);
variableValue = o.literalMap(styleMap);
}
lookupMap.push([stateName, variableValue]);
});
const compiledStatesMapStmt =
this._statesMapVar.set(o.literalMap(lookupMap, null, true)).toDeclStmt();
const compiledStatesMapStmt = this._statesMapVar.set(o.literalMap(lookupMap)).toDeclStmt();
const statements: o.Statement[] = [compiledStatesMapStmt, fnStatement];
return new AnimationEntryCompileResult(this.animationName, statements, fnVariable);

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticSymbol} from '../aot/static_symbol';
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata, identifierName} from '../compile_metadata';
import {StringMapWrapper} from '../facade/collection';
import {isBlank, isPresent} from '../facade/lang';
@ -15,7 +14,7 @@ import {ParseError} from '../parse_util';
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStateTransitionFnExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
import {StylesCollection} from './styles_collection';
const _INITIAL_KEYFRAME = 0;
@ -111,12 +110,9 @@ function _parseAnimationStateTransition(
errors: AnimationParseError[]): AnimationStateTransitionAst {
const styles = new StylesCollection();
const transitionExprs: AnimationStateTransitionExpression[] = [];
const stateChangeExpr = transitionStateMetadata.stateChangeExpr;
const transitionStates: Array<Function|StaticSymbol|string> = typeof stateChangeExpr == 'string' ?
(<string>stateChangeExpr).split(/\s*,\s*/) :
[<Function|StaticSymbol>stateChangeExpr];
const transitionStates = transitionStateMetadata.stateChangeExpr.split(/\s*,\s*/);
transitionStates.forEach(
expr => transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)));
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
const entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
const animation = _normalizeStyleSteps(entry, stateStyles, schema, errors);
const animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
@ -145,11 +141,8 @@ function _parseAnimationAlias(alias: string, errors: AnimationParseError[]): str
}
function _parseAnimationTransitionExpr(
transitionValue: string | Function | StaticSymbol,
errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
eventStr: string, errors: AnimationParseError[]): AnimationStateTransitionExpression[] {
const expressions: AnimationStateTransitionExpression[] = [];
if (typeof transitionValue == 'string') {
let eventStr = <string>transitionValue;
if (eventStr[0] == ':') {
eventStr = _parseAnimationAlias(eventStr, errors);
}
@ -168,10 +161,6 @@ function _parseAnimationTransitionExpr(
if (separator[0] == '<' && !isFullAnyStateExpr) {
expressions.push(new AnimationStateTransitionExpression(toState, fromState));
}
} else {
expressions.push(
new AnimationStateTransitionFnExpression(<Function|StaticSymbol>transitionValue));
}
return expressions;
}

View File

@ -6,10 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {SchemaMetadata} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, componentFactoryName, createHostComponentMeta, identifierName} from '../compile_metadata';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTypeSummary, createHostComponentMeta, identifierModuleUrl, identifierName} from '../compile_metadata';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper} from '../facade/collection';
import {Identifiers, createIdentifier, createIdentifierToken} from '../identifiers';
import {CompileMetadataResolver} from '../metadata_resolver';
@ -19,14 +22,13 @@ import * as o from '../output/output_ast';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {ViewCompiler} from '../view_compiler/view_compiler';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
import {AotCompilerHost} from './compiler_host';
import {GeneratedFile} from './generated_file';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {serializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, splitTypescriptSuffix, summaryFileName} from './util';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
import {serializeSummaries, summaryFileName} from './summary_serializer';
export class AotCompiler {
private _animationCompiler = new AnimationCompiler();
@ -63,13 +65,12 @@ export class AotCompiler {
srcFileUrl: string, ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
directives: StaticSymbol[], pipes: StaticSymbol[], ngModules: StaticSymbol[],
injectables: StaticSymbol[]): GeneratedFile[] {
const fileSuffix = splitTypescriptSuffix(srcFileUrl)[1];
const fileSuffix = _splitTypescriptSuffix(srcFileUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const generatedFiles: GeneratedFile[] = [];
generatedFiles.push(this._createSummary(
srcFileUrl, directives, pipes, ngModules, injectables, statements, exportedVars));
generatedFiles.push(this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables));
// compile all ng modules
exportedVars.push(
@ -108,7 +109,7 @@ export class AotCompiler {
});
if (statements.length > 0) {
const srcModule = this._codegenSourceModule(
srcFileUrl, ngfactoryFilePath(srcFileUrl), statements, exportedVars);
srcFileUrl, _ngfactoryModuleUrl(srcFileUrl), statements, exportedVars);
generatedFiles.unshift(srcModule);
}
return generatedFiles;
@ -116,8 +117,7 @@ export class AotCompiler {
private _createSummary(
srcFileUrl: string, directives: StaticSymbol[], pipes: StaticSymbol[],
ngModules: StaticSymbol[], injectables: StaticSymbol[], targetStatements: o.Statement[],
targetExportedVars: string[]): GeneratedFile {
ngModules: StaticSymbol[], injectables: StaticSymbol[]): GeneratedFile {
const symbolSummaries = this._symbolResolver.getSymbolsOf(srcFileUrl)
.map(symbol => this._symbolResolver.resolveSymbol(symbol));
const typeSummaries = [
@ -126,13 +126,8 @@ export class AotCompiler {
...pipes.map(ref => this._metadataResolver.getPipeSummary(ref)),
...injectables.map(ref => this._metadataResolver.getInjectableSummary(ref))
];
const {json, exportAs} = serializeSummaries(
this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
exportAs.forEach((entry) => {
targetStatements.push(
o.variable(entry.exportAs).set(o.importExpr({reference: entry.symbol})).toDeclStmt());
targetExportedVars.push(entry.exportAs);
});
const json = serializeSummaries(
this._host, this._summaryResolver, this._symbolResolver, symbolSummaries, typeSummaries);
return new GeneratedFile(srcFileUrl, summaryFileName(srcFileUrl), json);
}
@ -155,6 +150,12 @@ export class AotCompiler {
}
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.reference = this._symbolResolver.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(dep.comp)), _componentFactoryName(dep.comp));
});
targetStatements.push(...appCompileResult.statements);
return appCompileResult.ngModuleFactoryVar;
}
@ -171,12 +172,13 @@ export class AotCompiler {
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, ngModule: CompileNgModuleMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
const hostType = this._metadataResolver.getHostComponentType(compMeta.type.reference);
const hostMeta = createHostComponentMeta(
hostType, compMeta, this._metadataResolver.getHostComponentViewClass(hostType));
this._symbolResolver.getStaticSymbol(
identifierModuleUrl(compMeta.type), `${identifierName(compMeta.type)}_Host`),
compMeta);
const hostViewFactoryVar = this._compileComponent(
hostMeta, ngModule, [compMeta.type], null, fileSuffix, targetStatements);
const compFactoryVar = componentFactoryName(compMeta.type.reference);
const compFactoryVar = _componentFactoryName(compMeta.type);
targetStatements.push(
o.variable(compFactoryVar)
.set(o.importExpr(
@ -217,7 +219,7 @@ export class AotCompiler {
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
}
compiledAnimations.forEach(entry => targetStatements.push(...entry.statements));
targetStatements.push(...viewResult.statements);
targetStatements.push(..._resolveViewStatements(this._symbolResolver, viewResult));
return viewResult.viewClassVar;
}
@ -239,6 +241,27 @@ export class AotCompiler {
}
}
function _resolveViewStatements(
reflector: StaticSymbolResolver, compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
vfd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(vfd.comp)), dep.name);
} else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.reference = reflector.getStaticSymbol(
_ngfactoryModuleUrl(identifierModuleUrl(cfd.comp)), _componentFactoryName(cfd.comp));
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference =
reflector.getStaticSymbol(_ngfactoryModuleUrl(identifierModuleUrl(dwd.dir)), dwd.name);
}
});
return compileResult.statements;
}
function _resolveStyleStatements(
reflector: StaticSymbolResolver, compileResult: CompiledStylesheet,
fileSuffix: string): o.Statement[] {
@ -249,6 +272,15 @@ function _resolveStyleStatements(
return compileResult.statements;
}
function _ngfactoryModuleUrl(dirUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}
function _componentFactoryName(comp: CompileIdentifierMetadata): string {
return `${identifierName(comp)}NgFactory`;
}
function _stylesModuleUrl(stylesheetUrl: string, shim: boolean, suffix: string): string {
return `${stylesheetUrl}${shim ? '.shim' : ''}.ngstyle${suffix}`;
}
@ -260,6 +292,20 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
}
}
function _splitTypescriptSuffix(path: string): string[] {
if (path.endsWith('.d.ts')) {
return [path.slice(0, -5), '.ts'];
}
const lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
}
return [path, ''];
}
export interface NgAnalyzedModules {
ngModules: CompileNgModuleMetadata[];
ngModuleByPipeOrDirective: Map<StaticSymbol, CompileNgModuleMetadata>;

View File

@ -34,12 +34,11 @@ import {AotCompilerHost} from './compiler_host';
import {AotCompilerOptions} from './compiler_options';
import {StaticAndDynamicReflectionCapabilities} from './static_reflection_capabilities';
import {StaticReflector} from './static_reflector';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {StaticSymbolCache} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {AotSummaryResolver} from './summary_resolver';
/**
* Creates a new AotCompiler based on options and a host.
*/
@ -70,19 +69,13 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
symbolCache, staticReflector);
staticReflector);
// TODO(vicb): do not pass options.i18nFormat here
const importResolver = {
getImportAs: (symbol: StaticSymbol) => symbolResolver.getImportAs(symbol),
fileNameToModuleName: (fileName: string, containingFilePath: string) =>
compilerHost.fileNameToModuleName(fileName, containingFilePath)
};
const compiler = new AotCompiler(
compilerHost, resolver, tmplParser, new StyleCompiler(urlResolver),
new ViewCompiler(config, elementSchemaRegistry),
new DirectiveWrapperCompiler(config, expressionParser, elementSchemaRegistry, console),
new NgModuleCompiler(), new TypeScriptEmitter(importResolver), summaryResolver,
options.locale, options.i18nFormat, new AnimationParser(elementSchemaRegistry),
symbolResolver);
new NgModuleCompiler(), new TypeScriptEmitter(compilerHost), summaryResolver, options.locale,
options.i18nFormat, new AnimationParser(elementSchemaRegistry), symbolResolver);
return {compiler, reflector: staticReflector};
}

View File

@ -6,23 +6,19 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ImportResolver} from '../output/path_util';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolverHost} from './static_symbol_resolver';
import {AotSummaryResolverHost} from './summary_resolver';
import {AotSummarySerializerHost} from './summary_serializer';
/**
* The host of the AotCompiler disconnects the implementation from TypeScript / other language
* services and from underlying file systems.
*/
export interface AotCompilerHost extends StaticSymbolResolverHost, AotSummaryResolverHost {
/**
* Converts a file path to a module name that can be used as an `import.
* I.e. `path/to/importedFile.ts` should be imported by `path/to/containingFile.ts`.
*
* See ImportResolver.
*/
fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
/*|null*/;
export interface AotCompilerHost extends StaticSymbolResolverHost, ImportResolver,
AotSummaryResolverHost, AotSummarySerializerHost {
/**
* Loads a resource (e.g. html / css)
*/

View File

@ -10,15 +10,14 @@ import {Attribute, Component, ContentChild, ContentChildren, Directive, Host, Ho
import {ReflectorReader} from '../private_import_core';
import {StaticSymbol} from './static_symbol';
import {StaticSymbolResolver} from './static_symbol_resolver';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
const ANGULAR_IMPORT_LOCATIONS = {
coreDecorators: '@angular/core/src/metadata',
diDecorators: '@angular/core/src/di/metadata',
diMetadata: '@angular/core/src/di/metadata',
diInjectionToken: '@angular/core/src/di/injection_token',
diOpaqueToken: '@angular/core/src/di/injection_token',
diOpaqueToken: '@angular/core/src/di/opaque_token',
animationMetadata: '@angular/core/src/animation/metadata',
provider: '@angular/core/src/di/provider'
};
@ -35,7 +34,6 @@ export class StaticReflector implements ReflectorReader {
private parameterCache = new Map<StaticSymbol, any[]>();
private methodCache = new Map<StaticSymbol, {[key: string]: boolean}>();
private conversionMap = new Map<StaticSymbol, (context: StaticSymbol, args: any[]) => any>();
private injectionToken: StaticSymbol;
private opaqueToken: StaticSymbol;
constructor(
@ -85,12 +83,9 @@ export class StaticReflector implements ReflectorReader {
annotations = [];
const classMetadata = this.getTypeMetadata(type);
if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType && (parentType instanceof StaticSymbol)) {
const parentAnnotations = this.annotations(parentType);
const parentAnnotations = this.annotations(this.simplify(type, classMetadata['extends']));
annotations.push(...parentAnnotations);
}
}
if (classMetadata['decorators']) {
const ownAnnotations: any[] = this.simplify(type, classMetadata['decorators']);
annotations.push(...ownAnnotations);
@ -106,14 +101,11 @@ export class StaticReflector implements ReflectorReader {
const classMetadata = this.getTypeMetadata(type);
propMetadata = {};
if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
const parentPropMetadata = this.propMetadata(parentType);
const parentPropMetadata = this.propMetadata(this.simplify(type, classMetadata['extends']));
Object.keys(parentPropMetadata).forEach((parentProp) => {
propMetadata[parentProp] = parentPropMetadata[parentProp];
});
}
}
const members = classMetadata['members'] || {};
Object.keys(members).forEach((propName) => {
@ -164,10 +156,7 @@ export class StaticReflector implements ReflectorReader {
parameters.push(nestedResult);
});
} else if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
parameters = this.parameters(parentType);
}
parameters = this.parameters(this.simplify(type, classMetadata['extends']));
}
if (!parameters) {
parameters = [];
@ -187,14 +176,11 @@ export class StaticReflector implements ReflectorReader {
const classMetadata = this.getTypeMetadata(type);
methodNames = {};
if (classMetadata['extends']) {
const parentType = this.simplify(type, classMetadata['extends']);
if (parentType instanceof StaticSymbol) {
const parentMethodNames = this._methodNames(parentType);
const parentMethodNames = this._methodNames(this.simplify(type, classMetadata['extends']));
Object.keys(parentMethodNames).forEach((parentProp) => {
methodNames[parentProp] = parentMethodNames[parentProp];
});
}
}
const members = classMetadata['members'] || {};
Object.keys(members).forEach((propName) => {
@ -231,10 +217,9 @@ export class StaticReflector implements ReflectorReader {
}
private initializeConversionMap(): void {
const {coreDecorators, diDecorators, diMetadata, diInjectionToken,
diOpaqueToken, animationMetadata, provider} = ANGULAR_IMPORT_LOCATIONS;
this.injectionToken = this.findDeclaration(diInjectionToken, 'InjectionToken');
this.opaqueToken = this.findDeclaration(diInjectionToken, 'OpaqueToken');
const {coreDecorators, diDecorators, diMetadata, diOpaqueToken, animationMetadata, provider} =
ANGULAR_IMPORT_LOCATIONS;
this.opaqueToken = this.findDeclaration(diOpaqueToken, 'OpaqueToken');
this._registerDecoratorOrConstructor(this.findDeclaration(diDecorators, 'Host'), Host);
this._registerDecoratorOrConstructor(
@ -325,7 +310,6 @@ export class StaticReflector implements ReflectorReader {
if (value && (depth != 0 || value.__symbolic != 'error')) {
const parameters: string[] = targetFunction['parameters'];
const defaults: any[] = targetFunction.defaults;
args = args.map(arg => simplifyInContext(context, arg, depth + 1));
if (defaults && defaults.length > args.length) {
args.push(...defaults.slice(args.length).map((value: any) => simplify(value)));
}
@ -385,8 +369,7 @@ export class StaticReflector implements ReflectorReader {
}
if (expression instanceof StaticSymbol) {
// Stop simplification at builtin symbols
if (expression === self.injectionToken || expression === self.opaqueToken ||
self.conversionMap.has(expression)) {
if (expression === self.opaqueToken || self.conversionMap.has(expression)) {
return expression;
} else {
const staticSymbol = expression;
@ -510,21 +493,21 @@ export class StaticReflector implements ReflectorReader {
// Determine if the function is a built-in conversion
staticSymbol = simplifyInContext(context, expression['expression'], depth + 1);
if (staticSymbol instanceof StaticSymbol) {
if (staticSymbol === self.injectionToken || staticSymbol === self.opaqueToken) {
// if somebody calls new InjectionToken, don't create an InjectionToken,
// but rather return the symbol to which the InjectionToken is assigned to.
if (staticSymbol === self.opaqueToken) {
// if somebody calls new OpaqueToken, don't create an OpaqueToken,
// but rather return the symbol to which the OpaqueToken is assigned to.
return context;
}
const argExpressions: any[] = expression['arguments'] || [];
let converter = self.conversionMap.get(staticSymbol);
if (converter) {
const args =
argExpressions.map(arg => simplifyInContext(context, arg, depth + 1));
let converter = self.conversionMap.get(staticSymbol);
if (converter) {
return converter(context, args);
} else {
// Determine if the function is one we can simplify.
const targetFunction = resolveReferenceValue(staticSymbol);
return simplifyCall(staticSymbol, targetFunction, argExpressions);
return simplifyCall(staticSymbol, targetFunction, args);
}
}
break;
@ -668,6 +651,10 @@ class PopulatedScope extends BindingScope {
}
}
function sameSymbol(a: StaticSymbol, b: StaticSymbol): boolean {
return a === b;
}
function shouldIgnore(value: any): boolean {
return value && value.__symbolic == 'ignore';
}

View File

@ -12,14 +12,7 @@
* This token is unique for a filePath and name and can be used as a hash table key.
*/
export class StaticSymbol {
constructor(public filePath: string, public name: string, public members: string[]) {}
assertNoMembers() {
if (this.members.length) {
throw new Error(
`Illegal state: symbol without members expected, but got ${JSON.stringify(this)}.`);
}
}
constructor(public filePath: string, public name: string, public members?: string[]) {}
}
/**

View File

@ -45,18 +45,11 @@ const SUPPORTED_SCHEMA_VERSION = 3;
/**
* This class is responsible for loading metadata per symbol,
* and normalizing references between symbols.
*
* Internally, it only uses symbols without members,
* and deduces the values for symbols with members based
* on these symbols.
*/
export class StaticSymbolResolver {
private metadataCache = new Map<string, {[key: string]: any}>();
// Note: this will only contain StaticSymbols without members!
private resolvedSymbols = new Map<StaticSymbol, ResolvedStaticSymbol>();
private resolvedFilePaths = new Set<string>();
// Note: this will only contain StaticSymbols without members!
private importAs = new Map<StaticSymbol, StaticSymbol>();
constructor(
private host: StaticSymbolResolverHost, private staticSymbolCache: StaticSymbolCache,
@ -67,33 +60,13 @@ export class StaticSymbolResolver {
if (staticSymbol.members.length > 0) {
return this._resolveSymbolMembers(staticSymbol);
}
let result = this.resolvedSymbols.get(staticSymbol);
if (result) {
return result;
}
result = this._resolveSymbolFromSummary(staticSymbol);
if (result) {
return result;
}
let result = this._resolveSymbolFromSummary(staticSymbol);
if (!result) {
// Note: Some users use libraries that were not compiled with ngc, i.e. they don't
// have summaries, only .d.ts files. So we always need to check both, the summary
// and metadata.
this._createSymbolsOf(staticSymbol.filePath);
result = this.resolvedSymbols.get(staticSymbol);
return result;
}
getImportAs(staticSymbol: StaticSymbol): StaticSymbol {
if (staticSymbol.members.length) {
const baseSymbol = this.getStaticSymbol(staticSymbol.filePath, staticSymbol.name);
const baseImportAs = this.getImportAs(baseSymbol);
return baseImportAs ?
this.getStaticSymbol(baseImportAs.filePath, baseImportAs.name, staticSymbol.members) :
null;
}
let result = this.summaryResolver.getImportAs(staticSymbol);
if (!result) {
result = this.importAs.get(staticSymbol);
}
return result;
}
@ -162,13 +135,10 @@ export class StaticSymbolResolver {
const metadata = this.getModuleMetadata(filePath);
if (metadata['metadata']) {
// handle direct declarations of the symbol
const topLevelSymbolNames =
new Set<string>(Object.keys(metadata['metadata']).map(unescapeIdentifier));
Object.keys(metadata['metadata']).forEach((metadataKey) => {
const symbolMeta = metadata['metadata'][metadataKey];
resolvedSymbols.push(this.createResolvedSymbol(
this.getStaticSymbol(filePath, unescapeIdentifier(metadataKey)), topLevelSymbolNames,
symbolMeta));
Object.keys(metadata['metadata']).forEach((symbolName) => {
const symbolMeta = metadata['metadata'][symbolName];
resolvedSymbols.push(
this.createResolvedSymbol(this.getStaticSymbol(filePath, symbolName), symbolMeta));
});
}
@ -184,16 +154,15 @@ export class StaticSymbolResolver {
} else {
symbolName = exportSymbol.as;
}
symbolName = unescapeIdentifier(symbolName);
let symName = symbolName;
if (typeof exportSymbol !== 'string') {
symName = unescapeIdentifier(exportSymbol.name);
symName = exportSymbol.name;
}
const resolvedModule = this.resolveModule(moduleExport.from, filePath);
if (resolvedModule) {
const targetSymbol = this.getStaticSymbol(resolvedModule, symName);
const sourceSymbol = this.getStaticSymbol(filePath, symbolName);
resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
}
});
} else {
@ -203,7 +172,7 @@ export class StaticSymbolResolver {
const nestedExports = this.getSymbolsOf(resolvedModule);
nestedExports.forEach((targetSymbol) => {
const sourceSymbol = this.getStaticSymbol(filePath, targetSymbol.name);
resolvedSymbols.push(this.createExport(sourceSymbol, targetSymbol));
resolvedSymbols.push(new ResolvedStaticSymbol(sourceSymbol, targetSymbol));
});
}
}
@ -213,9 +182,7 @@ export class StaticSymbolResolver {
(resolvedSymbol) => this.resolvedSymbols.set(resolvedSymbol.symbol, resolvedSymbol));
}
private createResolvedSymbol(
sourceSymbol: StaticSymbol, topLevelSymbolNames: Set<string>,
metadata: any): ResolvedStaticSymbol {
private createResolvedSymbol(sourceSymbol: StaticSymbol, metadata: any): ResolvedStaticSymbol {
const self = this;
class ReferenceTransformer extends ValueTransformer {
@ -229,7 +196,7 @@ export class StaticSymbolResolver {
return result;
} else if (symbolic === 'reference') {
const module = map['module'];
const name = map['name'] ? unescapeIdentifier(map['name']) : map['name'];
const name = map['name'];
if (!name) {
return null;
}
@ -242,41 +209,26 @@ export class StaticSymbolResolver {
message: `Could not resolve ${module} relative to ${sourceSymbol.filePath}.`
};
}
} else {
const isFunctionParam = functionParams.indexOf(name) >= 0;
if (!isFunctionParam) {
filePath = sourceSymbol.filePath;
}
}
if (filePath) {
return self.getStaticSymbol(filePath, name);
} else if (functionParams.indexOf(name) >= 0) {
} else {
// reference to a function parameter
return {__symbolic: 'reference', name: name};
} else {
if (topLevelSymbolNames.has(name)) {
return self.getStaticSymbol(sourceSymbol.filePath, name);
}
// ambient value
null;
}
} else {
return super.visitStringMap(map, functionParams);
}
}
}
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
if (transformedMeta instanceof StaticSymbol) {
return this.createExport(sourceSymbol, transformedMeta);
}
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
}
private createExport(sourceSymbol: StaticSymbol, targetSymbol: StaticSymbol):
ResolvedStaticSymbol {
sourceSymbol.assertNoMembers();
targetSymbol.assertNoMembers();
if (this.summaryResolver.isLibraryFile(sourceSymbol.filePath)) {
// This case is for an ng library importing symbols from a plain ts library
// transitively.
// Note: We rely on the fact that we discover symbols in the direction
// from source files to library files
this.importAs.set(targetSymbol, this.getImportAs(sourceSymbol) || sourceSymbol);
}
return new ResolvedStaticSymbol(sourceSymbol, targetSymbol);
const transformedMeta = visitValue(metadata, new ReferenceTransformer(), []);
return new ResolvedStaticSymbol(sourceSymbol, transformedMeta);
}
private reportError(error: Error, context: StaticSymbol, path?: string) {
@ -335,9 +287,3 @@ export class StaticSymbolResolver {
}
}
}
// Remove extra underscore from escaped identifier.
// See https://github.com/Microsoft/TypeScript/blob/master/src/compiler/utilities.ts
export function unescapeIdentifier(identifier: string): string {
return identifier.startsWith('___') ? identifier.substr(1) : identifier;
}

View File

@ -5,12 +5,14 @@
* 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 {CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {deserializeSummaries} from './summary_serializer';
import {ngfactoryFilePath, stripNgFactory, summaryFileName} from './util';
import {ResolvedStaticSymbol} from './static_symbol_resolver';
import {deserializeSummaries, summaryFileName} from './summary_serializer';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummaryResolverHost {
/**
@ -22,34 +24,23 @@ export interface AotSummaryResolverHost {
* Returns whether a file is a source file or not.
*/
isSourceFile(sourceFilePath: string): boolean;
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/
getOutputFileName(sourceFilePath: string): string;
}
export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
// Note: this will only contain StaticSymbols without members!
private summaryCache = new Map<StaticSymbol, Summary<StaticSymbol>>();
private loadedFilePaths = new Set<string>();
// Note: this will only contain StaticSymbols without members!
private importAs = new Map<StaticSymbol, StaticSymbol>();
constructor(private host: AotSummaryResolverHost, private staticSymbolCache: StaticSymbolCache) {}
isLibraryFile(filePath: string): boolean {
// Note: We need to strip the .ngfactory. file path,
// so this method also works for generated files
// (for which host.isSourceFile will always return false).
return !this.host.isSourceFile(stripNgFactory(filePath));
private _assertNoMembers(symbol: StaticSymbol) {
if (symbol.members.length) {
throw new Error(
`Internal state: StaticSymbols in summaries can't have members! ${JSON.stringify(symbol)}`);
}
}
getLibraryFileName(filePath: string) { return this.host.getOutputFileName(filePath); }
resolveSummary(staticSymbol: StaticSymbol): Summary<StaticSymbol> {
staticSymbol.assertNoMembers();
this._assertNoMembers(staticSymbol);
let summary = this.summaryCache.get(staticSymbol);
if (!summary) {
this._loadSummaryFile(staticSymbol.filePath);
@ -63,17 +54,12 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
return Array.from(this.summaryCache.keys()).filter((symbol) => symbol.filePath === filePath);
}
getImportAs(staticSymbol: StaticSymbol): StaticSymbol {
staticSymbol.assertNoMembers();
return this.importAs.get(staticSymbol);
}
private _loadSummaryFile(filePath: string) {
if (this.loadedFilePaths.has(filePath)) {
return;
}
this.loadedFilePaths.add(filePath);
if (this.isLibraryFile(filePath)) {
if (!this.host.isSourceFile(filePath)) {
const summaryFilePath = summaryFileName(filePath);
let json: string;
try {
@ -83,13 +69,8 @@ export class AotSummaryResolver implements SummaryResolver<StaticSymbol> {
throw e;
}
if (json) {
const {summaries, importAs} = deserializeSummaries(this.staticSymbolCache, json);
summaries.forEach((summary) => this.summaryCache.set(summary.symbol, summary));
importAs.forEach((importAs) => {
this.importAs.set(
importAs.symbol,
this.staticSymbolCache.get(ngfactoryFilePath(filePath), importAs.importAs));
});
const readSummaries = deserializeSummaries(this.staticSymbolCache, json);
readSummaries.forEach((summary) => { this.summaryCache.set(summary.symbol, summary); });
}
}
}

View File

@ -5,19 +5,35 @@
* 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 {CompileNgModuleSummary, CompileSummaryKind, CompileTypeSummary} from '../compile_metadata';
import {CompileDirectiveSummary, CompileIdentifierMetadata, CompileNgModuleSummary, CompilePipeSummary, CompileSummaryKind, CompileTypeMetadata, CompileTypeSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
import {Summary, SummaryResolver} from '../summary_resolver';
import {ValueTransformer, visitValue} from '../util';
import {GeneratedFile} from './generated_file';
import {StaticSymbol, StaticSymbolCache} from './static_symbol';
import {ResolvedStaticSymbol, StaticSymbolResolver} from './static_symbol_resolver';
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export interface AotSummarySerializerHost {
/**
* Returns the output file path of a source file.
* E.g.
* `some_file.ts` -> `some_file.d.ts`
*/
getOutputFileName(sourceFilePath: string): string;
/**
* Returns whether a file is a source file or not.
*/
isSourceFile(sourceFilePath: string): boolean;
}
export function serializeSummaries(
summaryResolver: SummaryResolver<StaticSymbol>, symbolResolver: StaticSymbolResolver,
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]):
{json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
const serializer = new Serializer(symbolResolver, summaryResolver);
host: AotSummarySerializerHost, summaryResolver: SummaryResolver<StaticSymbol>,
symbolResolver: StaticSymbolResolver,
symbols: ResolvedStaticSymbol[], types: CompileTypeSummary[]): string {
const serializer = new Serializer(host);
// for symbols, we use everything except for the class metadata itself
// (we keep the statics though), as the class metadata is contained in the
@ -30,7 +46,7 @@ export function serializeSummaries(
// we execute the loop!
for (let processedIndex = 0; processedIndex < serializer.symbols.length; processedIndex++) {
const symbol = serializer.symbols[processedIndex];
if (summaryResolver.isLibraryFile(symbol.filePath)) {
if (!host.isSourceFile(symbol.filePath)) {
let summary = summaryResolver.resolveSummary(symbol);
if (!summary) {
// some symbols might originate from a plain typescript library
@ -58,11 +74,8 @@ export function serializeSummaries(
const ngModuleSummary = <CompileNgModuleSummary>typeSummary;
ngModuleSummary.exportedDirectives.concat(ngModuleSummary.exportedPipes).forEach((id) => {
const symbol: StaticSymbol = id.reference;
if (summaryResolver.isLibraryFile(symbol.filePath)) {
const summary = summaryResolver.resolveSummary(symbol);
if (summary) {
serializer.addOrMergeSummary(summary);
}
if (!host.isSourceFile(symbol.filePath)) {
serializer.addOrMergeSummary(summaryResolver.resolveSummary(symbol));
}
});
}
@ -70,14 +83,18 @@ export function serializeSummaries(
return serializer.serialize();
}
export function deserializeSummaries(symbolCache: StaticSymbolCache, json: string):
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
export function deserializeSummaries(
symbolCache: StaticSymbolCache, json: string): Summary<StaticSymbol>[] {
const deserializer = new Deserializer(symbolCache);
return deserializer.deserialize(json);
}
export function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}
class Serializer extends ValueTransformer {
// Note: This only contains symbols without members.
symbols: StaticSymbol[] = [];
private indexBySymbol = new Map<StaticSymbol, number>();
// This now contains a `__symbol: number` in the place of
@ -85,11 +102,7 @@ class Serializer extends ValueTransformer {
private processedSummaryBySymbol = new Map<StaticSymbol, any>();
private processedSummaries: any[] = [];
constructor(
private symbolResolver: StaticSymbolResolver,
private summaryResolver: SummaryResolver<StaticSymbol>) {
super();
}
constructor(private host: AotSummarySerializerHost) { super(); }
addOrMergeSummary(summary: Summary<StaticSymbol>) {
let symbolMeta = summary.metadata;
@ -116,44 +129,34 @@ class Serializer extends ValueTransformer {
}
}
serialize(): {json: string, exportAs: {symbol: StaticSymbol, exportAs: string}[]} {
const exportAs: {symbol: StaticSymbol, exportAs: string}[] = [];
const json = JSON.stringify({
serialize(): string {
return JSON.stringify({
summaries: this.processedSummaries,
symbols: this.symbols.map((symbol, index) => {
symbol.assertNoMembers();
let importAs: string;
if (this.summaryResolver.isLibraryFile(symbol.filePath)) {
importAs = `${symbol.name}_${index}`;
exportAs.push({symbol, exportAs: importAs});
}
return {
__symbol: index,
name: symbol.name,
// We convert the source filenames tinto output filenames,
// as the generated summary file will be used when teh current
// compilation unit is used as a library
filePath: this.summaryResolver.getLibraryFileName(symbol.filePath),
importAs: importAs
filePath: this.host.getOutputFileName(symbol.filePath)
};
})
});
return {json, exportAs};
}
private processValue(value: any): any { return visitValue(value, this, null); }
visitOther(value: any, context: any): any {
if (value instanceof StaticSymbol) {
const baseSymbol = this.symbolResolver.getStaticSymbol(value.filePath, value.name);
let index = this.indexBySymbol.get(baseSymbol);
let index = this.indexBySymbol.get(value);
// Note: == by purpose to compare with undefined!
if (index == null) {
index = this.indexBySymbol.size;
this.indexBySymbol.set(baseSymbol, index);
this.symbols.push(baseSymbol);
this.indexBySymbol.set(value, index);
this.symbols.push(value);
}
return {__symbol: index, members: value.members};
return {__symbol: index};
}
}
}
@ -163,28 +166,16 @@ class Deserializer extends ValueTransformer {
constructor(private symbolCache: StaticSymbolCache) { super(); }
deserialize(json: string):
{summaries: Summary<StaticSymbol>[], importAs: {symbol: StaticSymbol, importAs: string}[]} {
deserialize(json: string): Summary<StaticSymbol>[] {
const data: {summaries: any[], symbols: any[]} = JSON.parse(json);
const importAs: {symbol: StaticSymbol, importAs: string}[] = [];
this.symbols = [];
data.symbols.forEach((serializedSymbol) => {
const symbol = this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name);
this.symbols.push(symbol);
if (serializedSymbol.importAs) {
importAs.push({symbol: symbol, importAs: serializedSymbol.importAs});
}
});
const summaries = visitValue(data.summaries, this, null);
return {summaries, importAs};
this.symbols = data.symbols.map(
serializedSymbol => this.symbolCache.get(serializedSymbol.filePath, serializedSymbol.name));
return visitValue(data.summaries, this, null);
}
visitStringMap(map: {[key: string]: any}, context: any): any {
if ('__symbol' in map) {
const baseSymbol = this.symbols[map['__symbol']];
const members = map['members'];
return members.length ? this.symbolCache.get(baseSymbol.filePath, baseSymbol.name, members) :
baseSymbol;
return this.symbols[map['__symbol']];
} else {
return super.visitStringMap(map, context);
}

View File

@ -1,37 +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
*/
const STRIP_SRC_FILE_SUFFIXES = /(\.ts|\.d\.ts|\.js|\.jsx|\.tsx)$/;
export function ngfactoryFilePath(filePath: string): string {
const urlWithSuffix = splitTypescriptSuffix(filePath);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}
export function stripNgFactory(filePath: string): string {
return filePath.replace(/\.ngfactory\./, '.');
}
export function splitTypescriptSuffix(path: string): string[] {
if (path.endsWith('.d.ts')) {
return [path.slice(0, -5), '.ts'];
}
const lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
}
return [path, ''];
}
export function summaryFileName(fileName: string): string {
const fileNameWithoutSuffix = fileName.replace(STRIP_SRC_FILE_SUFFIXES, '');
return `${fileNameWithoutSuffix}.ngsummary.json`;
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectionStrategy, ComponentFactory, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {ChangeDetectionStrategy, SchemaMetadata, Type, ViewEncapsulation} from '@angular/core';
import {StaticSymbol} from './aot/static_symbol';
import {ListWrapper} from './facade/collection';
@ -39,11 +39,7 @@ export class CompileAnimationStateDeclarationMetadata extends CompileAnimationSt
}
export class CompileAnimationStateTransitionMetadata extends CompileAnimationStateMetadata {
constructor(
public stateChangeExpr: string|StaticSymbol|((stateA: string, stateB: string) => boolean),
public steps: CompileAnimationMetadata) {
super();
}
constructor(public stateChangeExpr: string, public steps: CompileAnimationMetadata) { super(); }
}
export abstract class CompileAnimationMetadata {}
@ -116,24 +112,6 @@ export function identifierModuleUrl(compileIdentifier: CompileIdentifierMetadata
return reflector.importUri(ref);
}
export function viewClassName(compType: any, embeddedTemplateIndex: number): string {
return `View_${identifierName({reference: compType})}_${embeddedTemplateIndex}`;
}
export function hostViewClassName(compType: any): string {
return `HostView_${identifierName({reference: compType})}`;
}
export function dirWrapperClassName(dirType: any) {
return `Wrapper_${identifierName({reference: dirType})}`;
}
export function componentFactoryName(compType: any): string {
return `${identifierName({reference: compType})}NgFactory`;
}
export interface ProxyClass { setDelegate(delegate: any): void; }
export interface CompileIdentifierMetadata { reference: any; }
export enum CompileSummaryKind {
@ -263,7 +241,7 @@ export class CompileTemplateMetadata {
externalStylesheets?: CompileStylesheetMetadata[],
ngContentSelectors?: string[],
animations?: CompileAnimationEntryMetadata[],
interpolation?: [string, string],
interpolation?: [string, string]
} = {}) {
this.encapsulation = encapsulation;
this.template = template;
@ -283,16 +261,11 @@ export class CompileTemplateMetadata {
return {
animations: this.animations.map(anim => anim.name),
ngContentSelectors: this.ngContentSelectors,
encapsulation: this.encapsulation,
encapsulation: this.encapsulation
};
}
}
export interface CompileEntryComponentMetadata {
componentType: any;
componentFactory: StaticSymbol|ComponentFactory<any>;
}
// Note: This should only use interfaces as nested data types
// as we need to be able to serialize this from/to JSON!
export interface CompileDirectiveSummary extends CompileTypeSummary {
@ -308,12 +281,9 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
providers: CompileProviderMetadata[];
viewProviders: CompileProviderMetadata[];
queries: CompileQueryMetadata[];
entryComponents: CompileEntryComponentMetadata[];
entryComponents: CompileIdentifierMetadata[];
changeDetection: ChangeDetectionStrategy;
template: CompileTemplateSummary;
wrapperType: StaticSymbol|ProxyClass;
componentViewType: StaticSymbol|ProxyClass;
componentFactory: StaticSymbol|ComponentFactory<any>;
}
/**
@ -322,8 +292,7 @@ export interface CompileDirectiveSummary extends CompileTypeSummary {
export class CompileDirectiveMetadata {
static create(
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs, host,
providers, viewProviders, queries, viewQueries, entryComponents, template, wrapperType,
componentViewType, componentFactory}: {
providers, viewProviders, queries, viewQueries, entryComponents, template}: {
isHost?: boolean,
type?: CompileTypeMetadata,
isComponent?: boolean,
@ -337,11 +306,8 @@ export class CompileDirectiveMetadata {
viewProviders?: CompileProviderMetadata[],
queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileEntryComponentMetadata[],
template?: CompileTemplateMetadata,
wrapperType?: StaticSymbol|ProxyClass,
componentViewType?: StaticSymbol|ProxyClass,
componentFactory?: StaticSymbol|ComponentFactory<any>,
entryComponents?: CompileIdentifierMetadata[],
template?: CompileTemplateMetadata
} = {}): CompileDirectiveMetadata {
const hostListeners: {[key: string]: string} = {};
const hostProperties: {[key: string]: string} = {};
@ -393,9 +359,6 @@ export class CompileDirectiveMetadata {
viewQueries,
entryComponents,
template,
wrapperType,
componentViewType,
componentFactory,
});
}
isHost: boolean;
@ -413,18 +376,14 @@ export class CompileDirectiveMetadata {
viewProviders: CompileProviderMetadata[];
queries: CompileQueryMetadata[];
viewQueries: CompileQueryMetadata[];
entryComponents: CompileEntryComponentMetadata[];
entryComponents: CompileIdentifierMetadata[];
template: CompileTemplateMetadata;
wrapperType: StaticSymbol|ProxyClass;
componentViewType: StaticSymbol|ProxyClass;
componentFactory: StaticSymbol|ComponentFactory<any>;
constructor({isHost, type, isComponent, selector, exportAs,
changeDetection, inputs, outputs, hostListeners, hostProperties,
hostAttributes, providers, viewProviders, queries, viewQueries,
entryComponents, template, wrapperType, componentViewType, componentFactory}: {
constructor(
{isHost, type, isComponent, selector, exportAs, changeDetection, inputs, outputs,
hostListeners, hostProperties, hostAttributes, providers, viewProviders, queries,
viewQueries, entryComponents, template}: {
isHost?: boolean,
type?: CompileTypeMetadata,
isComponent?: boolean,
@ -440,11 +399,8 @@ export class CompileDirectiveMetadata {
viewProviders?: CompileProviderMetadata[],
queries?: CompileQueryMetadata[],
viewQueries?: CompileQueryMetadata[],
entryComponents?: CompileEntryComponentMetadata[],
entryComponents?: CompileIdentifierMetadata[],
template?: CompileTemplateMetadata,
wrapperType?: StaticSymbol|ProxyClass,
componentViewType?: StaticSymbol|ProxyClass,
componentFactory?: StaticSymbol|ComponentFactory<any>,
} = {}) {
this.isHost = !!isHost;
this.type = type;
@ -462,11 +418,8 @@ export class CompileDirectiveMetadata {
this.queries = _normalizeArray(queries);
this.viewQueries = _normalizeArray(viewQueries);
this.entryComponents = _normalizeArray(entryComponents);
this.template = template;
this.wrapperType = wrapperType;
this.componentViewType = componentViewType;
this.componentFactory = componentFactory;
this.template = template;
}
toSummary(): CompileDirectiveSummary {
@ -486,10 +439,7 @@ export class CompileDirectiveMetadata {
queries: this.queries,
entryComponents: this.entryComponents,
changeDetection: this.changeDetection,
template: this.template && this.template.toSummary(),
wrapperType: this.wrapperType,
componentViewType: this.componentViewType,
componentFactory: this.componentFactory
template: this.template && this.template.toSummary()
};
}
}
@ -498,12 +448,11 @@ export class CompileDirectiveMetadata {
* Construct {@link CompileDirectiveMetadata} from {@link ComponentTypeMetadata} and a selector.
*/
export function createHostComponentMeta(
hostTypeReference: any, compMeta: CompileDirectiveMetadata,
hostViewType: StaticSymbol | ProxyClass): CompileDirectiveMetadata {
typeReference: any, compMeta: CompileDirectiveMetadata): CompileDirectiveMetadata {
const template = CssSelector.parse(compMeta.selector)[0].getMatchingElementTemplate();
return CompileDirectiveMetadata.create({
isHost: true,
type: {reference: hostTypeReference, diDeps: [], lifecycleHooks: []},
type: {reference: typeReference, diDeps: [], lifecycleHooks: []},
template: new CompileTemplateMetadata({
encapsulation: ViewEncapsulation.None,
template: template,
@ -522,8 +471,7 @@ export function createHostComponentMeta(
providers: [],
viewProviders: [],
queries: [],
viewQueries: [],
componentViewType: hostViewType
viewQueries: []
});
}
@ -569,7 +517,7 @@ export interface CompileNgModuleSummary extends CompileTypeSummary {
exportedPipes: CompileIdentifierMetadata[];
// Note: This is transitive.
entryComponents: CompileEntryComponentMetadata[];
entryComponents: CompileIdentifierMetadata[];
// Note: This is transitive.
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[];
// Note: This is transitive.
@ -586,7 +534,7 @@ export class CompileNgModuleMetadata {
declaredPipes: CompileIdentifierMetadata[];
exportedPipes: CompileIdentifierMetadata[];
entryComponents: CompileEntryComponentMetadata[];
entryComponents: CompileIdentifierMetadata[];
bootstrapComponents: CompileIdentifierMetadata[];
providers: CompileProviderMetadata[];
@ -607,7 +555,7 @@ export class CompileNgModuleMetadata {
exportedDirectives?: CompileIdentifierMetadata[],
declaredPipes?: CompileIdentifierMetadata[],
exportedPipes?: CompileIdentifierMetadata[],
entryComponents?: CompileEntryComponentMetadata[],
entryComponents?: CompileIdentifierMetadata[],
bootstrapComponents?: CompileIdentifierMetadata[],
importedModules?: CompileNgModuleSummary[],
exportedModules?: CompileNgModuleSummary[],
@ -655,7 +603,7 @@ export class TransitiveCompileNgModuleMetadata {
modulesSet = new Set<any>();
modules: CompileTypeMetadata[] = [];
entryComponentsSet = new Set<any>();
entryComponents: CompileEntryComponentMetadata[] = [];
entryComponents: CompileIdentifierMetadata[] = [];
providers: {provider: CompileProviderMetadata, module: CompileIdentifierMetadata}[] = [];
@ -693,10 +641,10 @@ export class TransitiveCompileNgModuleMetadata {
this.modules.push(id);
}
}
addEntryComponent(ec: CompileEntryComponentMetadata) {
if (!this.entryComponentsSet.has(ec.componentType)) {
this.entryComponentsSet.add(ec.componentType);
this.entryComponents.push(ec);
addEntryComponent(id: CompileIdentifierMetadata) {
if (!this.entryComponentsSet.has(id.reference)) {
this.entryComponentsSet.add(id.reference);
this.entryComponents.push(id);
}
}
}

View File

@ -6,9 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast';
import {ConvertPropertyBindingResult} from './expression_converter';
export class CheckBindingField {
constructor(public expression: o.ReadPropExpr, public bindingId: string) {}
}
@ -18,14 +21,28 @@ export function createCheckBindingField(builder: ClassBuilder): CheckBindingFiel
const fieldExpr = createBindFieldExpr(bindingId);
// private is fine here as no child view will reference the cached value...
builder.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name).set(o.literal(undefined)).toStmt());
builder.ctorStmts.push(o.THIS_EXPR.prop(fieldExpr.name)
.set(o.importExpr(createIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
return new CheckBindingField(fieldExpr, bindingId);
}
export function createCheckBindingStmt(
evalResult: ConvertPropertyBindingResult, fieldExpr: o.ReadPropExpr,
throwOnChangeVar: o.Expression, actions: o.Statement[]): o.Statement[] {
let condition: o.Expression = o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
throwOnChangeVar, fieldExpr, evalResult.currValExpr
]);
if (evalResult.forceUpdate) {
condition = evalResult.forceUpdate.or(condition);
}
return [
...evalResult.stmts, new o.IfStmt(condition, actions.concat([
<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(evalResult.currValExpr).toStmt()
]))
];
}
function createBindFieldExpr(bindingId: string): o.ReadPropExpr {
return o.THIS_EXPR.prop(`_expr_${bindingId}`);
}
export function isFirstViewCheck(view: o.Expression): o.Expression {
return o.not(view.prop('numberOfChecks'));
}

View File

@ -8,7 +8,7 @@
import * as cdAst from '../expression_parser/ast';
import {isBlank} from '../facade/lang';
import {isBlank, isPresent} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import {ClassBuilder} from '../output/class_builder';
import * as o from '../output/output_ast';
@ -338,7 +338,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
const receiver = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (varExpr) {
if (isPresent(varExpr)) {
result = varExpr.callFn(args);
}
}
@ -374,7 +374,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
const receiver: o.Expression = this.visit(ast.receiver, _Mode.Expression);
if (receiver === this._implicitReceiver) {
const varExpr = this._getLocal(ast.name);
if (varExpr) {
if (isPresent(varExpr)) {
throw new Error('Cannot assign to a reference or variable!');
}
}

View File

@ -7,69 +7,75 @@
*/
import {SecurityContext} from '@angular/core';
import {isPresent} from '../facade/lang';
import {Identifiers, createIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
import {isFirstViewCheck} from './binding_util';
import {ConvertPropertyBindingResult} from './expression_converter';
import {createEnumExpression} from './identifier_util';
export function createCheckRenderBindingStmt(
view: o.Expression, renderElement: o.Expression, boundProp: BoundElementPropertyAst,
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult,
export function writeToRenderer(
view: o.Expression, boundProp: BoundElementPropertyAst, renderElement: o.Expression,
renderValue: o.Expression, logBindingUpdate: boolean,
securityContextExpression?: o.Expression): o.Statement[] {
const checkStmts: o.Statement[] = [...evalResult.stmts];
const securityContext = calcSecurityContext(boundProp, securityContextExpression);
const updateStmts: o.Statement[] = [];
const renderer = view.prop('renderer');
renderValue = sanitizedValue(view, boundProp, renderValue, securityContextExpression);
switch (boundProp.type) {
case PropertyBindingType.Property:
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderProperty))
.callFn([
view, renderElement, o.literal(boundProp.name), oldValue,
oldValue.set(evalResult.currValExpr),
evalResult.forceUpdate || o.literal(false), securityContext
])
if (logBindingUpdate) {
updateStmts.push(
o.importExpr(createIdentifier(Identifiers.setBindingDebugInfo))
.callFn([renderer, renderElement, o.literal(boundProp.name), renderValue])
.toStmt());
}
updateStmts.push(
renderer
.callMethod(
'setElementProperty', [renderElement, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Attribute:
checkStmts.push(o.importExpr(createIdentifier(Identifiers.checkRenderAttribute))
.callFn([
view, renderElement, o.literal(boundProp.name), oldValue,
oldValue.set(evalResult.currValExpr),
evalResult.forceUpdate || o.literal(false), securityContext
])
renderValue =
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
updateStmts.push(
renderer
.callMethod(
'setElementAttribute', [renderElement, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Class:
checkStmts.push(
o.importExpr(createIdentifier(Identifiers.checkRenderClass))
.callFn([
view, renderElement, o.literal(boundProp.name), oldValue,
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false)
])
updateStmts.push(
renderer
.callMethod(
'setElementClass', [renderElement, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Style:
checkStmts.push(
o.importExpr(createIdentifier(Identifiers.checkRenderStyle))
.callFn([
view, renderElement, o.literal(boundProp.name), o.literal(boundProp.unit), oldValue,
oldValue.set(evalResult.currValExpr), evalResult.forceUpdate || o.literal(false),
securityContext
])
let strValue: o.Expression = renderValue.callMethod('toString', []);
if (isPresent(boundProp.unit)) {
strValue = strValue.plus(o.literal(boundProp.unit));
}
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
updateStmts.push(
renderer
.callMethod(
'setElementStyle', [renderElement, o.literal(boundProp.name), renderValue])
.toStmt());
break;
case PropertyBindingType.Animation:
throw new Error('Illegal state: Should not come here!');
}
return checkStmts;
return updateStmts;
}
function calcSecurityContext(
boundProp: BoundElementPropertyAst, securityContextExpression?: o.Expression): o.Expression {
function sanitizedValue(
view: o.Expression, boundProp: BoundElementPropertyAst, renderValue: o.Expression,
securityContextExpression?: o.Expression): o.Expression {
if (boundProp.securityContext === SecurityContext.NONE) {
return o.NULL_EXPR; // No sanitization needed.
return renderValue; // No sanitization needed.
}
if (!boundProp.needsRuntimeSecurityContext) {
securityContextExpression =
@ -78,13 +84,15 @@ function calcSecurityContext(
if (!securityContextExpression) {
throw new Error(`internal error, no SecurityContext given ${boundProp.name}`);
}
return securityContextExpression;
const ctx = view.prop('viewUtils').prop('sanitizer');
const args = [securityContextExpression, renderValue];
return ctx.callMethod('sanitize', args);
}
export function createCheckAnimationBindingStmts(
export function triggerAnimation(
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult) {
renderValue: o.Expression, lastRenderValue: o.Expression) {
const detachStmts: o.Statement[] = [];
const updateStmts: o.Statement[] = [];
@ -96,20 +104,21 @@ export function createCheckAnimationBindingStmts(
// it's important to normalize the void value as `void` explicitly
// so that the styles data can be obtained from the stringmap
const emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
const unitializedValue = o.importExpr(createIdentifier(Identifiers.UNINITIALIZED));
const animationTransitionVar = o.variable('animationTransition_' + animationName);
updateStmts.push(
animationTransitionVar
.set(animationFnExpr.callFn([
view, renderElement, isFirstViewCheck(view).conditional(emptyStateValue, oldValue),
evalResult.currValExpr
view, renderElement,
lastRenderValue.equals(unitializedValue).conditional(emptyStateValue, lastRenderValue),
renderValue.equals(unitializedValue).conditional(emptyStateValue, renderValue)
]))
.toDeclStmt());
updateStmts.push(oldValue.set(evalResult.currValExpr).toStmt());
detachStmts.push(animationTransitionVar
.set(animationFnExpr.callFn(
[view, renderElement, evalResult.currValExpr, emptyStateValue]))
detachStmts.push(
animationTransitionVar
.set(animationFnExpr.callFn([view, renderElement, lastRenderValue, emptyStateValue]))
.toDeclStmt());
const registerStmts: o.Statement[] = [];
@ -142,14 +151,5 @@ export function createCheckAnimationBindingStmts(
updateStmts.push(...registerStmts);
detachStmts.push(...registerStmts);
const checkUpdateStmts: o.Statement[] = [
...evalResult.stmts,
new o.IfStmt(
o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
view, oldValue, evalResult.currValExpr, evalResult.forceUpdate || o.literal(false)
]),
updateStmts)
];
const checkDetachStmts: o.Statement[] = [...evalResult.stmts, ...detachStmts];
return {checkUpdateStmts, checkDetachStmts};
return {updateStmts, detachStmts};
}

View File

@ -6,11 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ViewEncapsulation} from '@angular/core';
import {Component, ViewEncapsulation} from '@angular/core';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata} from './compile_metadata';
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileStylesheetMetadata, CompileTemplateMetadata, CompileTypeMetadata} from './compile_metadata';
import {CompilerConfig} from './config';
import {stringify} from './facade/lang';
import {isBlank, isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import * as html from './ml_parser/ast';
import {HtmlParser} from './ml_parser/html_parser';
@ -41,9 +41,9 @@ export class DirectiveNormalizer {
private _resourceLoader: ResourceLoader, private _urlResolver: UrlResolver,
private _htmlParser: HtmlParser, private _config: CompilerConfig) {}
clearCache(): void { this._resourceLoaderCache.clear(); }
clearCache() { this._resourceLoaderCache.clear(); }
clearCacheFor(normalizedDirective: CompileDirectiveMetadata): void {
clearCacheFor(normalizedDirective: CompileDirectiveMetadata) {
if (!normalizedDirective.isComponent) {
return;
}
@ -65,18 +65,10 @@ export class DirectiveNormalizer {
SyncAsyncResult<CompileTemplateMetadata> {
let normalizedTemplateSync: CompileTemplateMetadata = null;
let normalizedTemplateAsync: Promise<CompileTemplateMetadata>;
if (prenormData.template != null) {
if (typeof prenormData.template !== 'string') {
throw new SyntaxError(
`The template specified for component ${stringify(prenormData.componentType)} is not a string`);
}
if (isPresent(prenormData.template)) {
normalizedTemplateSync = this.normalizeTemplateSync(prenormData);
normalizedTemplateAsync = Promise.resolve(normalizedTemplateSync);
} else if (prenormData.templateUrl) {
if (typeof prenormData.templateUrl !== 'string') {
throw new SyntaxError(
`The templateUrl specified for component ${stringify(prenormData.componentType)} is not a string`);
}
normalizedTemplateAsync = this.normalizeTemplateAsync(prenormData);
} else {
throw new SyntaxError(
@ -110,12 +102,11 @@ export class DirectiveNormalizer {
templateAbsUrl: string): CompileTemplateMetadata {
const interpolationConfig = InterpolationConfig.fromArray(prenomData.interpolation);
const rootNodesAndErrors = this._htmlParser.parse(
template, stringify(prenomData.componentType), true, interpolationConfig);
template, stringify(prenomData.componentType), false, interpolationConfig);
if (rootNodesAndErrors.errors.length > 0) {
const errorString = rootNodesAndErrors.errors.join('\n');
throw new SyntaxError(`Template parse errors:\n${errorString}`);
}
const templateMetadataStyles = this.normalizeStylesheet(new CompileStylesheetMetadata({
styles: prenomData.styles,
styleUrls: prenomData.styleUrls,
@ -128,7 +119,7 @@ export class DirectiveNormalizer {
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
let encapsulation = prenomData.encapsulation;
if (encapsulation == null) {
if (isBlank(encapsulation)) {
encapsulation = this._config.defaultEncapsulation;
}
@ -237,13 +228,9 @@ class TemplatePreparseVisitor implements html.Visitor {
return null;
}
visitExpansion(ast: html.Expansion, context: any): any { html.visitAll(this, ast.cases); }
visitExpansionCase(ast: html.ExpansionCase, context: any): any {
html.visitAll(this, ast.expression);
}
visitComment(ast: html.Comment, context: any): any { return null; }
visitAttribute(ast: html.Attribute, context: any): any { return null; }
visitText(ast: html.Text, context: any): any { return null; }
visitExpansion(ast: html.Expansion, context: any): any { return null; }
visitExpansionCase(ast: html.ExpansionCase, context: any): any { return null; }
}

View File

@ -14,6 +14,8 @@ import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
import {splitAtColon} from './util';
/*
* Resolve a `Type` for {@link Directive}.
*

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata';
import {createCheckBindingField, isFirstViewCheck} from './compiler_util/binding_util';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import {createCheckBindingField, createCheckBindingStmt} from './compiler_util/binding_util';
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from './compiler_util/render_util';
import {triggerAnimation, writeToRenderer} from './compiler_util/render_util';
import {CompilerConfig} from './config';
import {Parser} from './expression_parser/parser';
import {Identifiers, createIdentifier} from './identifiers';
@ -18,7 +18,7 @@ import {DEFAULT_INTERPOLATION_CONFIG} from './ml_parser/interpolation_config';
import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast';
import {ParseError, ParseErrorLevel, ParseLocation, ParseSourceFile, ParseSourceSpan} from './parse_util';
import {Console, LifecycleHooks} from './private_import_core';
import {Console, LifecycleHooks, isDefaultChangeDetectionStrategy} from './private_import_core';
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {BindingParser} from './template_parser/binding_parser';
import {BoundElementPropertyAst, BoundEventAst} from './template_parser/template_ast';
@ -32,8 +32,8 @@ const CHANGES_FIELD_NAME = '_changes';
const CHANGED_FIELD_NAME = '_changed';
const EVENT_HANDLER_FIELD_NAME = '_eventHandler';
const CHANGE_VAR = o.variable('change');
const CURR_VALUE_VAR = o.variable('currValue');
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
const VIEW_VAR = o.variable('view');
const COMPONENT_VIEW_VAR = o.variable('componentView');
@ -52,6 +52,10 @@ const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap
*/
@CompilerInjectable()
export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) {
return `Wrapper_${identifierName(id)}`;
}
constructor(
private compilerConfig: CompilerConfig, private _exprParser: Parser,
private _schemaRegistry: ElementSchemaRegistry, private _console: Console) {}
@ -124,14 +128,14 @@ class DirectiveWrapperBuilder implements ClassBuilder {
new o.ClassMethod('ngOnDestroy', [], this.destroyStmts),
];
const fields: o.ClassField[] = [
new o.ClassField(EVENT_HANDLER_FIELD_NAME, o.FUNCTION_TYPE, [o.StmtModifier.Private]),
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(this.dirMeta.type)),
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE, [o.StmtModifier.Private]),
];
const ctorStmts: o.Statement[] = [
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt(),
];
const ctorStmts: o.Statement[] =
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
if (this.genChanges) {
fields.push(new o.ClassField(
CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE), [o.StmtModifier.Private]));
@ -145,7 +149,7 @@ class DirectiveWrapperBuilder implements ClassBuilder {
.toStmt());
return createClassStmt({
name: dirWrapperClassName(this.dirMeta.type.reference),
name: DirectiveWrapperCompiler.dirWrapperClassName(this.dirMeta.type),
ctorParams: dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
builders: [{fields, ctorStmts, methods}, this]
});
@ -180,14 +184,14 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
if (builder.ngOnInit) {
lifecycleStmts.push(new o.IfStmt(
isFirstViewCheck(VIEW_VAR),
VIEW_VAR.prop('numberOfChecks').identical(new o.LiteralExpr(0)),
[o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnInit', []).toStmt()]));
}
if (builder.ngDoCheck) {
lifecycleStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngDoCheck', []).toStmt());
}
if (lifecycleStmts.length > 0) {
stmts.push(new o.IfStmt(o.not(VIEW_VAR.prop('throwOnChange')), lifecycleStmts));
stmts.push(new o.IfStmt(o.not(THROW_ON_CHANGE_VAR), lifecycleStmts));
}
stmts.push(new o.ReturnStatement(changedVar));
@ -197,6 +201,7 @@ function addNgDoCheckMethod(builder: DirectiveWrapperBuilder) {
new o.FnParam(
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
],
stmts, o.BOOL_TYPE));
}
@ -206,35 +211,24 @@ function addCheckInputMethod(input: string, builder: DirectiveWrapperBuilder) {
const onChangeStatements: o.Statement[] = [
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
field.expression.set(CURR_VALUE_VAR).toStmt()
];
let methodBody: o.Statement[];
if (builder.genChanges) {
onChangeStatements.push(
o.THIS_EXPR.prop(CHANGES_FIELD_NAME).key(o.literal(input)).set(CHANGE_VAR).toStmt());
methodBody = [
CHANGE_VAR
.set(o.importExpr(createIdentifier(Identifiers.checkBindingChange)).callFn([
VIEW_VAR, field.expression, CURR_VALUE_VAR, FORCE_UPDATE_VAR
]))
.toDeclStmt(),
new o.IfStmt(CHANGE_VAR, onChangeStatements)
];
} else {
methodBody = [new o.IfStmt(
o.importExpr(createIdentifier(Identifiers.checkBinding)).callFn([
VIEW_VAR, field.expression, CURR_VALUE_VAR, FORCE_UPDATE_VAR
]),
onChangeStatements)];
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
.key(o.literal(input))
.set(o.importExpr(createIdentifier(Identifiers.SimpleChange))
.instantiate([field.expression, CURR_VALUE_VAR]))
.toStmt());
}
const methodBody: o.Statement[] = createCheckBindingStmt(
{currValExpr: CURR_VALUE_VAR, forceUpdate: FORCE_UPDATE_VAR, stmts: []}, field.expression,
THROW_ON_CHANGE_VAR, onChangeStatements);
builder.methods.push(new o.ClassMethod(
`check_${input}`,
[
new o.FnParam(
VIEW_VAR.name, o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(CURR_VALUE_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE)
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE),
],
methodBody));
}
@ -250,6 +244,7 @@ function addCheckHostMethod(
COMPONENT_VIEW_VAR.name,
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
];
hostProps.forEach((hostProp, hostPropIdx) => {
const field = createCheckBindingField(builder);
@ -264,18 +259,23 @@ function addCheckHostMethod(
methodParams.push(new o.FnParam(
securityContextExpr.name, o.importType(createIdentifier(Identifiers.SecurityContext))));
}
let checkBindingStmts: o.Statement[];
if (hostProp.isAnimation) {
const {checkUpdateStmts, checkDetachStmts} = createCheckAnimationBindingStmts(
const {updateStmts, detachStmts} = triggerAnimation(
VIEW_VAR, COMPONENT_VIEW_VAR, hostProp, hostEvents,
o.THIS_EXPR.prop(EVENT_HANDLER_FIELD_NAME)
.or(o.importExpr(createIdentifier(Identifiers.noop))),
RENDER_EL_VAR, field.expression, evalResult);
builder.detachStmts.push(...checkDetachStmts);
stmts.push(...checkUpdateStmts);
RENDER_EL_VAR, evalResult.currValExpr, field.expression);
checkBindingStmts = updateStmts;
builder.detachStmts.push(...detachStmts);
} else {
stmts.push(...createCheckRenderBindingStmt(
VIEW_VAR, RENDER_EL_VAR, hostProp, field.expression, evalResult, securityContextExpr));
checkBindingStmts = writeToRenderer(
VIEW_VAR, hostProp, RENDER_EL_VAR, evalResult.currValExpr,
builder.compilerConfig.logBindingUpdate, securityContextExpr);
}
stmts.push(...createCheckBindingStmt(
evalResult, field.expression, THROW_ON_CHANGE_VAR, checkBindingStmts));
});
builder.methods.push(new o.ClassMethod('checkHost', methodParams, stmts));
}
@ -385,19 +385,20 @@ export class DirectiveWrapperExpressions {
return dirWrapper.prop(CONTEXT_FIELD_NAME);
}
static ngDoCheck(dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression, ):
o.Expression {
return dirWrapper.callMethod('ngDoCheck', [view, renderElement]);
static ngDoCheck(
dirWrapper: o.Expression, view: o.Expression, renderElement: o.Expression,
throwOnChange: o.Expression): o.Expression {
return dirWrapper.callMethod('ngDoCheck', [view, renderElement, throwOnChange]);
}
static checkHost(
hostProps: BoundElementPropertyAst[], dirWrapper: o.Expression, view: o.Expression,
componentView: o.Expression, renderElement: o.Expression,
componentView: o.Expression, renderElement: o.Expression, throwOnChange: o.Expression,
runtimeSecurityContexts: o.Expression[]): o.Statement[] {
if (hostProps.length) {
return [dirWrapper
.callMethod(
'checkHost',
[view, componentView, renderElement].concat(runtimeSecurityContexts))
'checkHost', [view, componentView, renderElement, throwOnChange].concat(
runtimeSecurityContexts))
.toStmt()];
} else {
return [];

View File

@ -7,7 +7,7 @@
*/
import * as chars from '../chars';
import {NumberWrapper} from '../facade/lang';
import {NumberWrapper, isPresent} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
export enum TokenType {
@ -120,7 +120,7 @@ function newErrorToken(index: number, message: string): Token {
return new Token(index, TokenType.Error, 0, message);
}
export const EOF: Token = new Token(-1, TokenType.Character, 0, '');
export var EOF: Token = new Token(-1, TokenType.Character, 0, '');
class _Scanner {
length: number;
@ -241,7 +241,7 @@ class _Scanner {
this.advance();
str += two;
}
if (threeCode != null && this.peek == threeCode) {
if (isPresent(threeCode) && this.peek == threeCode) {
this.advance();
str += three;
}

View File

@ -9,13 +9,13 @@
import * as i18n from './i18n_ast';
export function digest(message: i18n.Message): string {
return message.id || sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
return sha1(serializeNodes(message.nodes).join('') + `[${message.meaning}]`);
}
export function decimalDigest(message: i18n.Message): string {
const visitor = new _SerializerIgnoreIcuExpVisitor();
const parts = message.nodes.map(a => a.visit(visitor, null));
return message.id || computeMsgId(parts.join(''), message.meaning);
return computeMsgId(parts.join(''), message.meaning);
}
/**

View File

@ -109,7 +109,7 @@ export class Extractor {
const resolver = new CompileMetadataResolver(
new NgModuleResolver(staticReflector), new DirectiveResolver(staticReflector),
new PipeResolver(staticReflector), summaryResolver, elementSchemaRegistry, normalizer,
symbolCache, staticReflector);
staticReflector);
// TODO(vicb): implicit tags & attributes
const messageBundle = new MessageBundle(htmlParser, [], {});

View File

@ -18,8 +18,6 @@ import {TranslationBundle} from './translation_bundle';
const _I18N_ATTR = 'i18n';
const _I18N_ATTR_PREFIX = 'i18n-';
const _I18N_COMMENT_PREFIX_REGEXP = /^i18n:?/;
const MEANING_SEPARATOR = '|';
const ID_SEPARATOR = '@@';
/**
* Extract translatable messages from an html AST
@ -55,22 +53,20 @@ enum _VisitorMode {
* @internal
*/
class _Visitor implements html.Visitor {
private _depth: number;
// <el i18n>...</el>
private _inI18nNode: boolean;
private _depth: number;
private _inImplicitNode: boolean;
// <!--i18n-->...<!--/i18n-->
private _inI18nBlock: boolean;
private _blockMeaningAndDesc: string;
private _blockChildren: html.Node[];
private _blockStartDepth: number;
private _inI18nBlock: boolean;
// {<icu message>}
private _inIcu: boolean;
// set to void 0 when not in a section
private _msgCountAtSectionStart: number;
private _errors: I18nError[];
private _mode: _VisitorMode;
@ -81,7 +77,7 @@ class _Visitor implements html.Visitor {
// _VisitorMode.Merge only
private _translations: TranslationBundle;
private _createI18nMessage:
(msg: html.Node[], meaning: string, description: string, id: string) => i18n.Message;
(msg: html.Node[], meaning: string, description: string) => i18n.Message;
constructor(private _implicitTags: string[], private _implicitAttrs: {[k: string]: string[]}) {}
@ -212,31 +208,50 @@ class _Visitor implements html.Visitor {
this._depth++;
const wasInI18nNode = this._inI18nNode;
const wasInImplicitNode = this._inImplicitNode;
let childNodes: html.Node[] = [];
let translatedChildNodes: html.Node[];
let childNodes: html.Node[];
// Extract:
// - top level nodes with the (implicit) "i18n" attribute if not already in a section
// - ICU messages
// Extract only top level nodes with the (implicit) "i18n" attribute if not in a block or an ICU
// message
const i18nAttr = _getI18nAttr(el);
const i18nMeta = i18nAttr ? i18nAttr.value : '';
const isImplicit = this._implicitTags.some(tag => el.name === tag) && !this._inIcu &&
!this._isInTranslatableSection;
const isTopLevelImplicit = !wasInImplicitNode && isImplicit;
this._inImplicitNode = wasInImplicitNode || isImplicit;
this._inImplicitNode = this._inImplicitNode || isImplicit;
if (!this._isInTranslatableSection && !this._inIcu) {
if (i18nAttr || isTopLevelImplicit) {
if (i18nAttr) {
// explicit translation
this._inI18nNode = true;
const message = this._addMessage(el.children, i18nMeta);
translatedChildNodes = this._translateMessage(el, message);
const message = this._addMessage(el.children, i18nAttr.value);
childNodes = this._translateMessage(el, message);
} else if (isTopLevelImplicit) {
// implicit translation
this._inI18nNode = true;
const message = this._addMessage(el.children);
childNodes = this._translateMessage(el, message);
}
if (this._mode == _VisitorMode.Extract) {
const isTranslatable = i18nAttr || isTopLevelImplicit;
if (isTranslatable) this._openTranslatableSection(el);
if (isTranslatable) {
this._openTranslatableSection(el);
}
html.visitAll(this, el.children);
if (isTranslatable) this._closeTranslatableSection(el, el.children);
if (isTranslatable) {
this._closeTranslatableSection(el, el.children);
}
}
if (this._mode === _VisitorMode.Merge && !i18nAttr && !isTopLevelImplicit) {
childNodes = [];
el.children.forEach(child => {
const visited = child.visit(this, context);
if (visited && !this._isInTranslatableSection) {
// Do not add the children from translatable sections (= i18n blocks here)
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
childNodes = childNodes.concat(visited);
}
});
}
} else {
if (i18nAttr || isTopLevelImplicit) {
@ -248,19 +263,20 @@ class _Visitor implements html.Visitor {
// Descend into child nodes for extraction
html.visitAll(this, el.children);
}
}
if (this._mode === _VisitorMode.Merge) {
const visitNodes = translatedChildNodes || el.children;
visitNodes.forEach(child => {
if (this._mode == _VisitorMode.Merge) {
// Translate attributes in ICU messages
childNodes = [];
el.children.forEach(child => {
const visited = child.visit(this, context);
if (visited && !this._isInTranslatableSection) {
// Do not add the children from translatable sections (= i18n blocks here)
// They will be added later in this loop when the block closes (i.e. on `<!-- /i18n -->`)
// They will be added when the section is close (i.e. on `<!-- /i18n -->`)
childNodes = childNodes.concat(visited);
}
});
}
}
this._visitAttributesOf(el);
@ -269,6 +285,7 @@ class _Visitor implements html.Visitor {
this._inImplicitNode = wasInImplicitNode;
if (this._mode === _VisitorMode.Merge) {
// There are no childNodes in translatable sections - those nodes will be replace anyway
const translatedAttrs = this._translateAttributes(el);
return new html.Element(
el.name, translatedAttrs, childNodes, el.sourceSpan, el.startSourceSpan,
@ -313,15 +330,15 @@ class _Visitor implements html.Visitor {
}
// add a translatable message
private _addMessage(ast: html.Node[], msgMeta?: string): i18n.Message {
private _addMessage(ast: html.Node[], meaningAndDesc?: string): i18n.Message {
if (ast.length == 0 ||
ast.length == 1 && ast[0] instanceof html.Attribute && !(<html.Attribute>ast[0]).value) {
// Do not create empty messages
return;
}
const {meaning, description, id} = _parseMessageMeta(msgMeta);
const message = this._createI18nMessage(ast, meaning, description, id);
const [meaning, description] = _splitMeaningAndDesc(meaningAndDesc);
const message = this._createI18nMessage(ast, meaning, description);
this._messages.push(message);
return message;
}
@ -351,7 +368,7 @@ class _Visitor implements html.Visitor {
attributes.forEach(attr => {
if (attr.name.startsWith(_I18N_ATTR_PREFIX)) {
i18nAttributeMeanings[attr.name.slice(_I18N_ATTR_PREFIX.length)] =
_parseMessageMeta(attr.value).meaning;
_splitMeaningAndDesc(attr.value)[0];
}
});
@ -365,7 +382,7 @@ class _Visitor implements html.Visitor {
if (attr.value && attr.value != '' && i18nAttributeMeanings.hasOwnProperty(attr.name)) {
const meaning = i18nAttributeMeanings[attr.name];
const message: i18n.Message = this._createI18nMessage([attr], meaning, '', '');
const message: i18n.Message = this._createI18nMessage([attr], meaning, '');
const nodes = this._translations.get(message);
if (nodes) {
if (nodes[0] instanceof html.Text) {
@ -403,7 +420,7 @@ class _Visitor implements html.Visitor {
}
/**
* Marks the start of a section, see `_closeTranslatableSection`
* Marks the start of a section, see `_endSection`
*/
private _openTranslatableSection(node: html.Node): void {
if (this._isInTranslatableSection) {
@ -415,7 +432,7 @@ class _Visitor implements html.Visitor {
/**
* A translatable section could be:
* - the content of translatable element,
* - a translatable element,
* - nodes between `<!-- i18n -->` and `<!-- /i18n -->` comments
*/
private get _isInTranslatableSection(): boolean {
@ -479,16 +496,8 @@ function _getI18nAttr(p: html.Element): html.Attribute {
return p.attrs.find(attr => attr.name === _I18N_ATTR) || null;
}
function _parseMessageMeta(i18n: string): {meaning: string, description: string, id: string} {
if (!i18n) return {meaning: '', description: '', id: ''};
const idIndex = i18n.indexOf(ID_SEPARATOR);
const descIndex = i18n.indexOf(MEANING_SEPARATOR);
const [meaningAndDesc, id] =
(idIndex > -1) ? [i18n.slice(0, idIndex), i18n.slice(idIndex + 2)] : [i18n, ''];
const [meaning, description] = (descIndex > -1) ?
[meaningAndDesc.slice(0, descIndex), meaningAndDesc.slice(descIndex + 1)] :
['', meaningAndDesc];
return {meaning, description, id};
function _splitMeaningAndDesc(i18n: string): [string, string] {
if (!i18n) return ['', ''];
const pipeIndex = i18n.indexOf('|');
return pipeIndex == -1 ? ['', i18n] : [i18n.slice(0, pipeIndex), i18n.slice(pipeIndex + 1)];
}

View File

@ -15,12 +15,11 @@ export class Message {
* @param placeholderToMessage maps placeholder names to messages (used for nested ICU messages)
* @param meaning
* @param description
* @param id
*/
constructor(
public nodes: Node[], public placeholders: {[phName: string]: string},
public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
public description: string, public id: string) {}
public description: string) {}
}
export interface Node {

View File

@ -22,11 +22,11 @@ const _expParser = new ExpressionParser(new ExpressionLexer());
* Returns a function converting html nodes to an i18n Message given an interpolationConfig
*/
export function createI18nMessageFactory(interpolationConfig: InterpolationConfig): (
nodes: html.Node[], meaning: string, description: string, id: string) => i18n.Message {
nodes: html.Node[], meaning: string, description: string) => i18n.Message {
const visitor = new _I18nVisitor(_expParser, interpolationConfig);
return (nodes: html.Node[], meaning: string, description: string, id: string) =>
visitor.toI18nMessage(nodes, meaning, description, id);
return (nodes: html.Node[], meaning: string, description: string) =>
visitor.toI18nMessage(nodes, meaning, description);
}
class _I18nVisitor implements html.Visitor {
@ -40,8 +40,7 @@ class _I18nVisitor implements html.Visitor {
private _expressionParser: ExpressionParser,
private _interpolationConfig: InterpolationConfig) {}
public toI18nMessage(nodes: html.Node[], meaning: string, description: string, id: string):
i18n.Message {
public toI18nMessage(nodes: html.Node[], meaning: string, description: string): i18n.Message {
this._isIcu = nodes.length == 1 && nodes[0] instanceof html.Expansion;
this._icuDepth = 0;
this._placeholderRegistry = new PlaceholderRegistry();
@ -51,7 +50,7 @@ class _I18nVisitor implements html.Visitor {
const i18nodes: i18n.Node[] = html.visitAll(this, nodes, {});
return new i18n.Message(
i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description, id);
i18nodes, this._placeholderToContent, this._placeholderToMessage, meaning, description);
}
visitElement(el: html.Element, context: any): i18n.Node {
@ -116,7 +115,7 @@ class _I18nVisitor implements html.Visitor {
// TODO(vicb): add a html.Node -> i18n.Message cache to avoid having to re-create the msg
const phName = this._placeholderRegistry.getPlaceholderName('ICU', icu.sourceSpan.toString());
const visitor = new _I18nVisitor(this._expressionParser, this._interpolationConfig);
this._placeholderToMessage[phName] = visitor.toI18nMessage([icu], '', '', '');
this._placeholderToMessage[phName] = visitor.toI18nMessage([icu], '', '');
return new i18n.IcuPlaceholder(i18nIcu, phName, icu.sourceSpan);
}

View File

@ -6,10 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
import {StaticSymbol} from './aot/static_symbol';
import {CompileIdentifierMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
const VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
@ -160,6 +161,8 @@ export class Identifiers {
};
static SimpleChange:
IdentifierSpec = {name: 'SimpleChange', moduleUrl: CD_MODULE_URL, runtime: SimpleChange};
static UNINITIALIZED:
IdentifierSpec = {name: 'UNINITIALIZED', moduleUrl: CD_MODULE_URL, runtime: UNINITIALIZED};
static ChangeDetectorStatus: IdentifierSpec = {
name: 'ChangeDetectorStatus',
moduleUrl: CD_MODULE_URL,
@ -170,36 +173,6 @@ export class Identifiers {
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkBinding
};
static checkBindingChange: IdentifierSpec = {
name: 'checkBindingChange',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkBindingChange
};
static checkRenderText: IdentifierSpec = {
name: 'checkRenderText',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkRenderText
};
static checkRenderProperty: IdentifierSpec = {
name: 'checkRenderProperty',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkRenderProperty
};
static checkRenderAttribute: IdentifierSpec = {
name: 'checkRenderAttribute',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkRenderAttribute
};
static checkRenderClass: IdentifierSpec = {
name: 'checkRenderClass',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkRenderClass
};
static checkRenderStyle: IdentifierSpec = {
name: 'checkRenderStyle',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.checkRenderStyle
};
static devModeEqual:
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
static inlineInterpolate: IdentifierSpec = {

View File

@ -6,12 +6,13 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core';
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta, identifierName} from '../compile_metadata';
import {CompilerConfig} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {stringify} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
@ -20,11 +21,10 @@ import {NgModuleCompiler} from '../ng_module_compiler';
import * as ir from '../output/output_ast';
import {interpretStatements} from '../output/output_interpreter';
import {jitStatements} from '../output/output_jit';
import {view_utils} from '../private_import_core';
import {CompiledStylesheet, StyleCompiler} from '../style_compiler';
import {TemplateParser} from '../template_parser/template_parser';
import {SyncAsyncResult} from '../util';
import {ViewCompiler} from '../view_compiler/view_compiler';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewClassDependency, ViewCompiler} from '../view_compiler/view_compiler';
@ -130,6 +130,10 @@ export class JitCompiler implements Compiler {
const extraProviders = [this._metadataResolver.getProviderMetadata(new ProviderMeta(
Compiler, {useFactory: () => new ModuleBoundCompiler(this, moduleMeta.type.reference)}))];
const compileResult = this._ngModuleCompiler.compile(moduleMeta, extraProviders);
compileResult.dependencies.forEach((dep) => {
dep.placeholder.reference =
this._assertComponentKnown(dep.comp.reference, true).proxyComponentFactory;
});
if (!this._compilerConfig.useJit) {
ngModuleFactory =
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
@ -164,7 +168,7 @@ export class JitCompiler implements Compiler {
const template =
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
templates.add(template);
allComponentFactories.push(<ComponentFactory<any>>dirMeta.componentFactory);
allComponentFactories.push(template.proxyComponentFactory);
}
}
});
@ -176,16 +180,15 @@ export class JitCompiler implements Compiler {
const dirMeta = this._metadataResolver.getDirectiveMetadata(dirIdentifier.reference);
if (dirMeta.isComponent) {
dirMeta.entryComponents.forEach((entryComponentType) => {
const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
templates.add(
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
});
}
});
localModuleMeta.entryComponents.forEach((entryComponentType) => {
const moduleMeta = moduleByDirective.get(entryComponentType.componentType);
templates.add(
this._createCompiledHostTemplate(entryComponentType.componentType, moduleMeta));
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
});
});
templates.forEach((template) => this._compileTemplate(template));
@ -219,12 +222,12 @@ export class JitCompiler implements Compiler {
const compMeta = this._metadataResolver.getDirectiveMetadata(compType);
assertComponent(compMeta);
const componentFactory = <ComponentFactory<any>>compMeta.componentFactory;
const hostClass = this._metadataResolver.getHostComponentType(compType);
const hostMeta = createHostComponentMeta(
hostClass, compMeta, <any>view_utils.getComponentFactoryViewClass(componentFactory));
compiledTemplate =
new CompiledTemplate(true, compMeta.type, hostMeta, ngModule, [compMeta.type]);
const HostClass = function HostClass() {};
(<any>HostClass).overriddenName = `${identifierName(compMeta.type)}_Host`;
const hostMeta = createHostComponentMeta(HostClass, compMeta);
compiledTemplate = new CompiledTemplate(
true, compMeta.selector, compMeta.type, hostMeta, ngModule, [compMeta.type]);
this._compiledHostTemplateCache.set(compType, compiledTemplate);
}
return compiledTemplate;
@ -236,12 +239,32 @@ export class JitCompiler implements Compiler {
if (!compiledTemplate) {
assertComponent(compMeta);
compiledTemplate = new CompiledTemplate(
false, compMeta.type, compMeta, ngModule, ngModule.transitiveModule.directives);
false, compMeta.selector, compMeta.type, compMeta, ngModule,
ngModule.transitiveModule.directives);
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
}
return compiledTemplate;
}
private _assertComponentKnown(compType: any, isHost: boolean): CompiledTemplate {
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
this._compiledTemplateCache.get(compType);
if (!compiledTemplate) {
throw new Error(
`Illegal state: Compiled view for component ${stringify(compType)} (host: ${isHost}) does not exist!`);
}
return compiledTemplate;
}
private _assertDirectiveWrapper(dirType: any): Type<any> {
const dirWrapper = this._compiledDirectiveWrapperCache.get(dirType);
if (!dirWrapper) {
throw new Error(
`Illegal state: Directive wrapper for ${stringify(dirType)} has not been compiled!`);
}
return dirWrapper;
}
private _compileDirectiveWrapper(
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
@ -254,7 +277,6 @@ export class JitCompiler implements Compiler {
`/${identifierName(moduleMeta.type)}/${identifierName(dirMeta.type)}/wrapper.ngfactory.js`,
statements, compileResult.dirWrapperClassVar);
}
(<ProxyClass>dirMeta.wrapperType).setDelegate(directiveWrapperClass);
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
}
@ -282,6 +304,21 @@ export class JitCompiler implements Compiler {
const compileResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
pipes, compiledAnimations);
compileResult.dependencies.forEach((dep) => {
let depTemplate: CompiledTemplate;
if (dep instanceof ViewClassDependency) {
const vfd = <ViewClassDependency>dep;
depTemplate = this._assertComponentKnown(vfd.comp.reference, false);
vfd.placeholder.reference = depTemplate.proxyViewClass;
} else if (dep instanceof ComponentFactoryDependency) {
const cfd = <ComponentFactoryDependency>dep;
depTemplate = this._assertComponentKnown(cfd.comp.reference, true);
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
}
});
const statements = stylesCompileResult.componentStylesheet.statements
.concat(...compiledAnimations.map(ca => ca.statements))
.concat(compileResult.statements);
@ -321,16 +358,30 @@ export class JitCompiler implements Compiler {
class CompiledTemplate {
private _viewClass: Function = null;
proxyViewClass: Type<any>;
proxyComponentFactory: ComponentFactory<any>;
isCompiled = false;
constructor(
public isHost: boolean, public compType: CompileIdentifierMetadata,
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
public compMeta: CompileDirectiveMetadata, public ngModule: CompileNgModuleMetadata,
public directives: CompileIdentifierMetadata[]) {}
public directives: CompileIdentifierMetadata[]) {
const self = this;
this.proxyViewClass = <any>function() {
if (!self._viewClass) {
throw new Error(
`Illegal state: CompiledTemplate for ${stringify(self.compType)} is not compiled yet!`);
}
return self._viewClass.apply(this, arguments);
};
this.proxyComponentFactory = isHost ?
new ComponentFactory<any>(selector, this.proxyViewClass, compType.reference) :
null;
}
compiled(viewClass: Function) {
this._viewClass = viewClass;
(<ProxyClass>this.compMeta.componentViewType).setDelegate(viewClass);
this.proxyViewClass.prototype = viewClass.prototype;
this.isCompiled = true;
}
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config';
@ -40,8 +40,6 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
`No ResourceLoader implementation has been provided. Can't read the url "${url}"`);}
};
const baseHtmlParser = new InjectionToken('HtmlParser');
/**
* A set of providers that provide `JitCompiler` and its dependencies to use for
* template compilation.
@ -54,24 +52,17 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
Console,
Lexer,
Parser,
{
provide: baseHtmlParser,
useClass: HtmlParser,
},
HtmlParser,
{
provide: i18n.I18NHtmlParser,
useFactory: (parser: HtmlParser, translations: string, format: string) =>
new i18n.I18NHtmlParser(parser, translations, format),
deps: [
baseHtmlParser,
HtmlParser,
[new Optional(), new Inject(TRANSLATIONS)],
[new Optional(), new Inject(TRANSLATIONS_FORMAT)],
]
},
{
provide: HtmlParser,
useExisting: i18n.I18NHtmlParser,
},
TemplateParser,
DirectiveNormalizer,
CompileMetadataResolver,

View File

@ -6,16 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, ComponentFactory, Directive, Host, Inject, Injectable, InjectionToken, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Directive, Host, Inject, Injectable, ModuleWithProviders, OpaqueToken, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util';
import {StaticSymbol} from './aot/static_symbol';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver';
import {stringify} from './facade/lang';
import {Identifiers, resolveIdentifier} from './identifiers';
import {ListWrapper, StringMapWrapper} from './facade/collection';
import {isBlank, isPresent, stringify} from './facade/lang';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver';
@ -24,10 +24,10 @@ import {ComponentStillLoadingError, LIFECYCLE_HOOKS_VALUES, ReflectorReader, ref
import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
import {MODULE_SUFFIX, SyntaxError, ValueTransformer, visitValue} from './util';
import {MODULE_SUFFIX, SyncAsyncResult, SyntaxError, ValueTransformer, visitValue} from './util';
export type ErrorCollector = (error: any, type?: any) => void;
export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');
export const ERROR_COLLECTOR_TOKEN = new OpaqueToken('ErrorCollector');
// Design notes:
// - don't lazily create metadata:
@ -38,8 +38,6 @@ export const ERROR_COLLECTOR_TOKEN = new InjectionToken('ErrorCollector');
// to wait correctly.
@CompilerInjectable()
export class CompileMetadataResolver {
private _nonNormalizedDirectiveCache =
new Map<Type<any>, {annotation: Directive, metadata: cpl.CompileDirectiveMetadata}>();
private _directiveCache = new Map<Type<any>, cpl.CompileDirectiveMetadata>();
private _summaryCache = new Map<Type<any>, cpl.CompileTypeSummary>();
private _pipeCache = new Map<Type<any>, cpl.CompilePipeMetadata>();
@ -51,14 +49,12 @@ export class CompileMetadataResolver {
private _pipeResolver: PipeResolver, private _summaryResolver: SummaryResolver<any>,
private _schemaRegistry: ElementSchemaRegistry,
private _directiveNormalizer: DirectiveNormalizer,
@Optional() private _staticSymbolCache: StaticSymbolCache,
private _reflector: ReflectorReader = reflector,
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {}
clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type);
this._directiveCache.delete(type);
this._nonNormalizedDirectiveCache.delete(type);
this._summaryCache.delete(type);
this._pipeCache.delete(type);
this._ngModuleOfTypes.delete(type);
@ -69,9 +65,8 @@ export class CompileMetadataResolver {
}
}
clearCache(): void {
clearCache() {
this._directiveCache.clear();
this._nonNormalizedDirectiveCache.clear();
this._summaryCache.clear();
this._pipeCache.clear();
this._ngModuleCache.clear();
@ -79,66 +74,6 @@ export class CompileMetadataResolver {
this._directiveNormalizer.clearCache();
}
private _createProxyClass(baseType: any, name: string): cpl.ProxyClass {
let delegate: any = null;
const proxyClass: cpl.ProxyClass = <any>function() {
if (!delegate) {
throw new Error(
`Illegal state: Class ${name} for type ${stringify(baseType)} is not compiled yet!`);
}
return delegate.apply(this, arguments);
};
proxyClass.setDelegate = (d) => {
delegate = d;
(<any>proxyClass).prototype = d.prototype;
};
// Make stringify work correctly
(<any>proxyClass).overriddenName = name;
return proxyClass;
}
private getGeneratedClass(dirType: any, name: string): StaticSymbol|cpl.ProxyClass {
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(ngfactoryFilePath(dirType.filePath), name);
} else {
return this._createProxyClass(dirType, name);
}
}
private getDirectiveWrapperClass(dirType: any): StaticSymbol|cpl.ProxyClass {
return this.getGeneratedClass(dirType, cpl.dirWrapperClassName(dirType));
}
private getComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass {
return this.getGeneratedClass(dirType, cpl.viewClassName(dirType, 0));
}
getHostComponentViewClass(dirType: any): StaticSymbol|cpl.ProxyClass {
return this.getGeneratedClass(dirType, cpl.hostViewClassName(dirType));
}
getHostComponentType(dirType: any): StaticSymbol|Type<any> {
const name = `${cpl.identifierName({reference: dirType})}_Host`;
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(dirType.filePath, name);
} else {
const HostClass = <any>function HostClass() {};
HostClass.overriddenName = name;
return HostClass;
}
}
private getComponentFactory(selector: string, dirType: any): StaticSymbol|ComponentFactory<any> {
if (dirType instanceof StaticSymbol) {
return this._staticSymbolCache.get(
ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType));
} else {
const hostView = this.getHostComponentViewClass(dirType);
return new ComponentFactory(selector, <any>hostView, dirType);
}
}
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
@ -227,9 +162,6 @@ export class CompileMetadataResolver {
queries: metadata.queries,
viewQueries: metadata.viewQueries,
entryComponents: metadata.entryComponents,
wrapperType: metadata.wrapperType,
componentViewType: metadata.componentViewType,
componentFactory: metadata.componentFactory,
template: templateMetadata
});
this._directiveCache.set(directiveType, normalizedDirMeta);
@ -269,14 +201,7 @@ export class CompileMetadataResolver {
getNonNormalizedDirectiveMetadata(directiveType: any):
{annotation: Directive, metadata: cpl.CompileDirectiveMetadata} {
directiveType = resolveForwardRef(directiveType);
if (!directiveType) {
return null;
}
let cacheEntry = this._nonNormalizedDirectiveCache.get(directiveType);
if (cacheEntry) {
return cacheEntry;
}
const dirMeta = this._directiveResolver.resolve(directiveType, false);
const dirMeta = this._directiveResolver.resolve(directiveType);
if (!dirMeta) {
return null;
}
@ -305,7 +230,7 @@ export class CompileMetadataResolver {
let changeDetectionStrategy: ChangeDetectionStrategy = null;
let viewProviders: cpl.CompileProviderMetadata[] = [];
let entryComponentMetadata: cpl.CompileEntryComponentMetadata[] = [];
let entryComponentMetadata: cpl.CompileIdentifierMetadata[] = [];
let selector = dirMeta.selector;
if (dirMeta instanceof Component) {
@ -318,7 +243,7 @@ export class CompileMetadataResolver {
}
if (dirMeta.entryComponents) {
entryComponentMetadata = flattenAndDedupeArray(dirMeta.entryComponents)
.map((type) => this._getEntryComponentMetadata(type))
.map((type) => this._getIdentifierMetadata(type))
.concat(entryComponentMetadata);
}
if (!selector) {
@ -336,14 +261,14 @@ export class CompileMetadataResolver {
}
let providers: cpl.CompileProviderMetadata[] = [];
if (dirMeta.providers != null) {
if (isPresent(dirMeta.providers)) {
providers = this._getProvidersMetadata(
dirMeta.providers, entryComponentMetadata,
`providers for "${stringifyType(directiveType)}"`, [], directiveType);
}
let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = [];
if (dirMeta.queries != null) {
if (isPresent(dirMeta.queries)) {
queries = this._getQueriesMetadata(dirMeta.queries, false, directiveType);
viewQueries = this._getQueriesMetadata(dirMeta.queries, true, directiveType);
}
@ -362,17 +287,9 @@ export class CompileMetadataResolver {
viewProviders: viewProviders,
queries: queries,
viewQueries: viewQueries,
entryComponents: entryComponentMetadata,
wrapperType: this.getDirectiveWrapperClass(directiveType),
componentViewType: nonNormalizedTemplateMetadata ? this.getComponentViewClass(directiveType) :
undefined,
componentFactory: nonNormalizedTemplateMetadata ?
this.getComponentFactory(selector, directiveType) :
undefined
entryComponents: entryComponentMetadata
});
cacheEntry = {metadata, annotation: dirMeta};
this._nonNormalizedDirectiveCache.set(directiveType, cacheEntry);
return cacheEntry;
return {metadata, annotation: dirMeta};
}
/**
@ -454,7 +371,7 @@ export class CompileMetadataResolver {
const importedModules: cpl.CompileNgModuleSummary[] = [];
const exportedModules: cpl.CompileNgModuleSummary[] = [];
const providers: cpl.CompileProviderMetadata[] = [];
const entryComponents: cpl.CompileEntryComponentMetadata[] = [];
const entryComponents: cpl.CompileIdentifierMetadata[] = [];
const bootstrapComponents: cpl.CompileIdentifierMetadata[] = [];
const schemas: SchemaMetadata[] = [];
@ -571,7 +488,7 @@ export class CompileMetadataResolver {
if (meta.entryComponents) {
entryComponents.push(...flattenAndDedupeArray(meta.entryComponents)
.map(type => this._getEntryComponentMetadata(type)));
.map(type => this._getIdentifierMetadata(type)));
}
if (meta.bootstrap) {
@ -587,8 +504,7 @@ export class CompileMetadataResolver {
});
}
entryComponents.push(
...bootstrapComponents.map(type => this._getEntryComponentMetadata(type.reference)));
entryComponents.push(...bootstrapComponents);
if (meta.schemas) {
schemas.push(...flattenAndDedupeArray(meta.schemas));
@ -806,14 +722,14 @@ export class CompileMetadataResolver {
token = paramEntry.attributeName;
} else if (paramEntry instanceof Inject) {
token = paramEntry.token;
} else if (isValidType(paramEntry) && token == null) {
} else if (isValidType(paramEntry) && isBlank(token)) {
token = paramEntry;
}
});
} else {
token = param;
}
if (token == null) {
if (isBlank(token)) {
hasUnknownDeps = true;
return null;
}
@ -853,7 +769,7 @@ export class CompileMetadataResolver {
}
private _getProvidersMetadata(
providers: Provider[], targetEntryComponents: cpl.CompileEntryComponentMetadata[],
providers: Provider[], targetEntryComponents: cpl.CompileIdentifierMetadata[],
debugInfo?: string, compileProviders: cpl.CompileProviderMetadata[] = [],
type?: any): cpl.CompileProviderMetadata[] {
providers.forEach((provider: any, providerIdx: number) => {
@ -862,14 +778,10 @@ export class CompileMetadataResolver {
} else {
provider = resolveForwardRef(provider);
let providerMeta: cpl.ProviderMeta;
if (provider && typeof provider === 'object' && provider.hasOwnProperty('provide')) {
this._validateProvider(provider);
if (provider && typeof provider == 'object' && provider.hasOwnProperty('provide')) {
providerMeta = new cpl.ProviderMeta(provider.provide, provider);
} else if (isValidType(provider)) {
providerMeta = new cpl.ProviderMeta(provider, {useClass: provider});
} else if (provider === void 0) {
this._reportError(new SyntaxError(
`Encountered undefined provider! Usually this means you have a circular dependencies (might be caused by using 'barrel' index.ts files.`));
} else {
const providersInfo =
(<string[]>providers.reduce(
@ -900,19 +812,9 @@ export class CompileMetadataResolver {
return compileProviders;
}
private _validateProvider(provider: any): void {
if (provider.hasOwnProperty('useClass') && provider.useClass == null) {
this._reportError(new SyntaxError(
`Invalid provider for ${stringifyType(provider.provide)}. useClass cannot be ${provider.useClass}.
Usually it happens when:
1. There's a circular dependency (might be caused by using index.ts (barrel) files).
2. Class was used before it was declared. Use forwardRef in this case.`));
}
}
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta, type?: any):
cpl.CompileEntryComponentMetadata[] {
const components: cpl.CompileEntryComponentMetadata[] = [];
cpl.CompileIdentifierMetadata[] {
const components: cpl.CompileIdentifierMetadata[] = [];
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
if (provider.useFactory || provider.useExisting || provider.useClass) {
@ -930,27 +832,14 @@ export class CompileMetadataResolver {
extractIdentifiers(provider.useValue, collectedIdentifiers);
collectedIdentifiers.forEach((identifier) => {
const entry = this._getEntryComponentMetadata(identifier.reference);
if (entry) {
components.push(entry);
if (this._directiveResolver.isDirective(identifier.reference) ||
this._loadSummary(identifier.reference, cpl.CompileSummaryKind.Directive)) {
components.push(identifier);
}
});
return components;
}
private _getEntryComponentMetadata(dirType: any): cpl.CompileEntryComponentMetadata {
const dirMeta = this.getNonNormalizedDirectiveMetadata(dirType);
if (dirMeta) {
return {componentType: dirType, componentFactory: dirMeta.metadata.componentFactory};
} else {
const dirSummary =
<cpl.CompileDirectiveSummary>this._loadSummary(dirType, cpl.CompileSummaryKind.Directive);
if (dirSummary) {
return {componentType: dirType, componentFactory: dirSummary.componentFactory};
}
}
}
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
let compileDeps: cpl.CompileDiDependencyMetadata[];
let compileTypeMetadata: cpl.CompileTypeMetadata = null;

View File

@ -283,7 +283,7 @@ class _TreeBuilder {
const tagDef = this.getTagDefinition(el.name);
const {parent, container} = this._getParentElementSkippingContainers();
if (parent && tagDef.requireExtraParent(parent.name)) {
if (isPresent(parent) && tagDef.requireExtraParent(parent.name)) {
const newParent = new html.Element(
tagDef.parentToAdd, [], [], el.sourceSpan, el.startSourceSpan, el.endSourceSpan);
this._insertBeforeContainer(parent, container, newParent);

View File

@ -9,7 +9,7 @@
import {CompileDiDependencyMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenName, tokenReference} from './compile_metadata';
import {createDiTokenExpression} from './compiler_util/identifier_util';
import {isPresent} from './facade/lang';
import {Identifiers, createIdentifier, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifier, createIdentifierToken, resolveIdentifier} from './identifiers';
import {CompilerInjectable} from './injectable';
import {ClassBuilder, createClassStmt} from './output/class_builder';
import * as o from './output/output_ast';
@ -19,12 +19,9 @@ import {LifecycleHooks} from './private_import_core';
import {NgModuleProviderAnalyzer} from './provider_analyzer';
import {ProviderAst} from './template_parser/template_ast';
/**
* This is currently not read, but will probably be used in the future.
* We keep it as we already pass it through all the rigth places...
*/
export class ComponentFactoryDependency {
constructor(public compType: any) {}
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class NgModuleCompileResult {
@ -49,12 +46,13 @@ export class NgModuleCompiler {
const bootstrapComponentFactories: CompileIdentifierMetadata[] = [];
const entryComponentFactories =
ngModuleMeta.transitiveModule.entryComponents.map((entryComponent) => {
const id: CompileIdentifierMetadata = {reference: null};
if (ngModuleMeta.bootstrapComponents.some(
(id) => id.reference === entryComponent.componentType)) {
bootstrapComponentFactories.push({reference: entryComponent.componentFactory});
(id) => id.reference === entryComponent.reference)) {
bootstrapComponentFactories.push(id);
}
deps.push(new ComponentFactoryDependency(entryComponent.componentType));
return {reference: entryComponent.componentFactory};
deps.push(new ComponentFactoryDependency(entryComponent, id));
return id;
});
const builder = new _InjectorBuilder(
ngModuleMeta, entryComponentFactories, bootstrapComponentFactories, sourceSpan);

View File

@ -9,7 +9,7 @@
import {NgModule, Type} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {stringify} from './facade/lang';
import {isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
@ -30,7 +30,7 @@ export class NgModuleResolver {
const ngModuleMeta: NgModule =
ListWrapper.findLast(this._reflector.annotations(type), _isNgModuleMetadata);
if (ngModuleMeta) {
if (isPresent(ngModuleMeta)) {
return ngModuleMeta;
} else {
if (throwIfNotFound) {

View File

@ -7,9 +7,8 @@
*/
import {StaticSymbol} from '../aot/static_symbol';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {isBlank} from '../facade/lang';
import {identifierModuleUrl, identifierName} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang';
import {EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
import {AbstractJsEmitterVisitor} from './abstract_js_emitter';
@ -17,17 +16,17 @@ import * as o from './output_ast';
import {ImportResolver} from './path_util';
export class JavaScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(genFilePath, this._importResolver);
constructor(private _importGenerator: ImportResolver) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new JsEmitterVisitor(moduleUrl);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`var ${prefix} = req` +
`uire('${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}');`);
`uire('${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}');`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
@ -37,23 +36,20 @@ export class JavaScriptEmitter implements OutputEmitter {
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
importsWithPrefixes = new Map<string, string>();
constructor(private _genFilePath: string, private _importResolver: ImportResolver) { super(); }
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
const reference = value.reference;
if (!(reference instanceof StaticSymbol)) {
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
}
return this._importResolver.getImportAs(reference) || reference;
}
constructor(private _moduleUrl: string) { super(); }
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
const {name, filePath} = this._resolveStaticSymbol(ast.value);
if (filePath != this._genFilePath) {
let prefix = this.importsWithPrefixes.get(filePath);
const name = identifierName(ast.value);
const moduleUrl = identifierModuleUrl(ast.value);
if (isBlank(name)) {
console.error('>>>', ast.value);
throw new Error(`Internal error: unknown identifier ${ast.value}`);
}
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(moduleUrl);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(filePath, prefix);
this.importsWithPrefixes.set(moduleUrl, prefix);
}
ctx.print(`${prefix}.`);
}

View File

@ -68,13 +68,13 @@ export class MapType extends Type {
visitType(visitor: TypeVisitor, context: any): any { return visitor.visitMapType(this, context); }
}
export const DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
export const BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
export const INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
export const NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
export const STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
export const FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
export const NULL_TYPE = new BuiltinType(BuiltinTypeName.Null);
export var DYNAMIC_TYPE = new BuiltinType(BuiltinTypeName.Dynamic);
export var BOOL_TYPE = new BuiltinType(BuiltinTypeName.Bool);
export var INT_TYPE = new BuiltinType(BuiltinTypeName.Int);
export var NUMBER_TYPE = new BuiltinType(BuiltinTypeName.Number);
export var STRING_TYPE = new BuiltinType(BuiltinTypeName.String);
export var FUNCTION_TYPE = new BuiltinType(BuiltinTypeName.Function);
export var NULL_TYPE = new BuiltinType(BuiltinTypeName.Null);
export interface TypeVisitor {
visitBuiltintType(type: BuiltinType, context: any): any;
@ -451,12 +451,12 @@ export interface ExpressionVisitor {
visitLiteralMapExpr(ast: LiteralMapExpr, context: any): any;
}
export const THIS_EXPR = new ReadVarExpr(BuiltinVar.This);
export const SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super);
export const CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError);
export const CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack);
export const NULL_EXPR = new LiteralExpr(null, null);
export const TYPED_NULL_EXPR = new LiteralExpr(null, NULL_TYPE);
export var THIS_EXPR = new ReadVarExpr(BuiltinVar.This);
export var SUPER_EXPR = new ReadVarExpr(BuiltinVar.Super);
export var CATCH_ERROR_VAR = new ReadVarExpr(BuiltinVar.CatchError);
export var CATCH_STACK_VAR = new ReadVarExpr(BuiltinVar.CatchStack);
export var NULL_EXPR = new LiteralExpr(null, null);
export var TYPED_NULL_EXPR = new LiteralExpr(null, NULL_TYPE);
//// Statements
export enum StmtModifier {
@ -894,10 +894,8 @@ export function literalArr(values: Expression[], type: Type = null): LiteralArra
return new LiteralArrayExpr(values, type);
}
export function literalMap(
values: [string, Expression][], type: MapType = null, quoted: boolean = false): LiteralMapExpr {
return new LiteralMapExpr(
values.map(entry => new LiteralMapEntry(entry[0], entry[1], quoted)), type);
export function literalMap(values: [string, Expression][], type: MapType = null): LiteralMapExpr {
return new LiteralMapExpr(values.map(entry => new LiteralMapEntry(entry[0], entry[1])), type);
}
export function not(expr: Expression): NotExpr {

View File

@ -6,8 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StaticSymbol} from '../aot/static_symbol';
/**
* Interface that defines how import statements should be generated.
*/
@ -18,10 +16,4 @@ export abstract class ImportResolver {
*/
abstract fileNameToModuleName(importedFilePath: string, containingFilePath: string): string
/*|null*/;
/**
* Converts the given StaticSymbol into another StaticSymbol that should be used
* to generate the import from.
*/
abstract getImportAs(symbol: StaticSymbol): StaticSymbol /*|null*/;
}

View File

@ -7,22 +7,18 @@
*/
import {StaticSymbol} from '../aot/static_symbol';
import {CompileIdentifierMetadata} from '../compile_metadata';
import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from '../compile_metadata';
import {isBlank, isPresent} from '../facade/lang';
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, OutputEmitter} from './abstract_emitter';
import * as o from './output_ast';
import {ImportResolver} from './path_util';
const _debugFilePath = '/debug/lib';
const _debugModuleUrl = '/debug/lib';
export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.Type | any[]):
string {
const converter = new _TsEmitterVisitor(_debugFilePath, {
fileNameToModuleName(filePath: string, containingFilePath: string) { return filePath; },
getImportAs(symbol: StaticSymbol) { return null; }
});
const converter = new _TsEmitterVisitor(_debugModuleUrl);
const ctx = EmitterVisitorContext.createRoot([]);
const asts: any[] = Array.isArray(ast) ? ast : [ast];
@ -41,23 +37,17 @@ export function debugOutputAstAsTypeScript(ast: o.Statement | o.Expression | o.T
}
export class TypeScriptEmitter implements OutputEmitter {
constructor(private _importResolver: ImportResolver) {}
emitStatements(genFilePath: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new _TsEmitterVisitor(genFilePath, this._importResolver);
constructor(private _importGenerator: ImportResolver) {}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
const converter = new _TsEmitterVisitor(moduleUrl);
const ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
const srcParts: string[] = [];
converter.reexports.forEach((reexports, exportedFilePath) => {
const reexportsCode =
reexports.map(reexport => `${reexport.name} as ${reexport.as}`).join(',');
srcParts.push(
`export {${reexportsCode}} from '${this._importResolver.fileNameToModuleName(exportedFilePath, genFilePath)}';`);
});
converter.importsWithPrefixes.forEach((prefix, importedFilePath) => {
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`imp` +
`ort * as ${prefix} from '${this._importResolver.fileNameToModuleName(importedFilePath, genFilePath)}';`);
`ort * as ${prefix} from '${this._importGenerator.fileNameToModuleName(importedModuleUrl, moduleUrl)}';`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
@ -65,12 +55,9 @@ export class TypeScriptEmitter implements OutputEmitter {
}
class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor {
constructor(private _genFilePath: string, private _importResolver: ImportResolver) {
super(false);
}
constructor(private _moduleUrl: string) { super(false); }
importsWithPrefixes = new Map<string, string>();
reexports = new Map<string, {name: string, as: string}[]>();
visitType(t: o.Type, ctx: EmitterVisitorContext, defaultType: string = 'any') {
if (isPresent(t)) {
@ -111,19 +98,6 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
if (ctx.isExportedVar(stmt.name) && stmt.value instanceof o.ExternalExpr && !stmt.type) {
// check for a reexport
const {name, filePath, members} = this._resolveStaticSymbol(stmt.value.value);
if (members.length === 0 && filePath !== this._genFilePath) {
let reexports = this.reexports.get(filePath);
if (!reexports) {
reexports = [];
this.reexports.set(filePath, reexports);
}
reexports.push({name, as: stmt.name});
return null;
}
}
if (ctx.isExportedVar(stmt.name)) {
ctx.print(`export `);
}
@ -346,29 +320,25 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
}, params, ctx, ',');
}
private _resolveStaticSymbol(value: CompileIdentifierMetadata): StaticSymbol {
const reference = value.reference;
if (!(reference instanceof StaticSymbol)) {
throw new Error(`Internal error: unknown identifier ${JSON.stringify(value)}`);
}
return this._importResolver.getImportAs(reference) || reference;
}
private _visitIdentifier(
value: CompileIdentifierMetadata, typeParams: o.Type[], ctx: EmitterVisitorContext): void {
const {name, filePath, members} = this._resolveStaticSymbol(value);
if (filePath != this._genFilePath) {
let prefix = this.importsWithPrefixes.get(filePath);
const name = identifierName(value);
const moduleUrl = identifierModuleUrl(value);
if (isBlank(name)) {
throw new Error(`Internal error: unknown identifier ${value}`);
}
if (isPresent(moduleUrl) && moduleUrl != this._moduleUrl) {
let prefix = this.importsWithPrefixes.get(moduleUrl);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(filePath, prefix);
this.importsWithPrefixes.set(moduleUrl, prefix);
}
ctx.print(`${prefix}.`);
}
if (members.length) {
ctx.print(name);
if (value.reference && value.reference.members && value.reference.members.length) {
ctx.print(value.reference.name);
ctx.print('.');
ctx.print(members.join('.'));
ctx.print(value.reference.members.join('.'));
} else {
ctx.print(name);
}

View File

@ -7,6 +7,7 @@
*/
import {CompileIdentifierMetadata} from '../compile_metadata';
import {ValueTransformer, visitValue} from '../util';
import * as o from './output_ast';

View File

@ -9,7 +9,7 @@
import {Pipe, Type, resolveForwardRef} from '@angular/core';
import {ListWrapper} from './facade/collection';
import {stringify} from './facade/lang';
import {isPresent, stringify} from './facade/lang';
import {CompilerInjectable} from './injectable';
import {ReflectorReader, reflector} from './private_import_core';
@ -38,9 +38,9 @@ export class PipeResolver {
*/
resolve(type: Type<any>, throwIfNotFound = true): Pipe {
const metas = this._reflector.annotations(resolveForwardRef(type));
if (metas) {
if (isPresent(metas)) {
const annotation = ListWrapper.findLast(metas, _isPipeMetadata);
if (annotation) {
if (isPresent(annotation)) {
return annotation;
}
}

View File

@ -32,6 +32,7 @@ export const view_utils: typeof r.view_utils = r.view_utils;
export const DebugContext: typeof r.DebugContext = r.DebugContext;
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
export const UNINITIALIZED: typeof r.UNINITIALIZED = r.UNINITIALIZED;
export const ValueUnwrapper: typeof r.ValueUnwrapper = r.ValueUnwrapper;
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
export type RenderDebugInfo = typeof r._RenderDebugInfo;

View File

@ -9,7 +9,7 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang';
import {Identifiers, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast';
@ -114,7 +114,7 @@ export class ProviderElementContext {
let queries: CompileQueryMetadata[];
while (currentEl !== null) {
queries = currentEl._contentQueries.get(tokenReference(token));
if (queries) {
if (isPresent(queries)) {
result.push(...queries.filter((query) => query.descendants || distance <= 1));
}
if (currentEl._directiveAsts.length > 0) {
@ -123,7 +123,7 @@ export class ProviderElementContext {
currentEl = currentEl._parent;
}
queries = this.viewContext.viewQueries.get(tokenReference(token));
if (queries) {
if (isPresent(queries)) {
result.push(...queries);
}
return result;
@ -143,7 +143,7 @@ export class ProviderElementContext {
return null;
}
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
if (transformedProviderAst) {
if (isPresent(transformedProviderAst)) {
return transformedProviderAst;
}
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
@ -165,11 +165,11 @@ export class ProviderElementContext {
transformedUseExisting = null;
transformedUseValue = existingDiDep.value;
}
} else if (provider.useFactory) {
} else if (isPresent(provider.useFactory)) {
const deps = provider.deps || provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
} else if (provider.useClass) {
} else if (isPresent(provider.useClass)) {
const deps = provider.deps || provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep, eager));
@ -235,7 +235,7 @@ export class ProviderElementContext {
}
} else {
// check parent elements
while (!result && currElement._parent) {
while (!result && isPresent(currElement._parent)) {
const prevElement = currElement;
currElement = currElement._parent;
if (prevElement._isViewRoot) {
@ -301,7 +301,7 @@ export class NgModuleProviderAnalyzer {
return null;
}
let transformedProviderAst = this._transformedProviders.get(tokenReference(token));
if (transformedProviderAst) {
if (isPresent(transformedProviderAst)) {
return transformedProviderAst;
}
if (isPresent(this._seenProviders.get(tokenReference(token)))) {
@ -324,11 +324,11 @@ export class NgModuleProviderAnalyzer {
transformedUseExisting = null;
transformedUseValue = existingDiDep.value;
}
} else if (provider.useFactory) {
} else if (isPresent(provider.useFactory)) {
const deps = provider.deps || provider.useFactory.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
} else if (provider.useClass) {
} else if (isPresent(provider.useClass)) {
const deps = provider.deps || provider.useClass.diDeps;
transformedDeps =
deps.map((dep) => this._getDependency(dep, eager, resolvedProvider.sourceSpan));
@ -454,7 +454,7 @@ function _resolveProviders(
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> {
const viewQueries = new Map<any, CompileQueryMetadata[]>();
if (component.viewQueries) {
if (isPresent(component.viewQueries)) {
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query));
}
return viewQueries;
@ -464,7 +464,7 @@ function _getContentQueries(directives: CompileDirectiveSummary[]):
Map<any, CompileQueryMetadata[]> {
const contentQueries = new Map<any, CompileQueryMetadata[]>();
directives.forEach(directive => {
if (directive.queries) {
if (isPresent(directive.queries)) {
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query));
}
});

Some files were not shown because too many files have changed in this diff Show More