Compare commits
107 Commits
2.0.0-beta
...
2.0.0-beta
Author | SHA1 | Date | |
---|---|---|---|
4945e73588 | |||
8a00a863ac | |||
db87baeb98 | |||
c4c43f5a77 | |||
0ae77753f3 | |||
5f0baaac73 | |||
b5b6ece65a | |||
4282297c24 | |||
9c96b8affc | |||
132829e5e2 | |||
4a414420e9 | |||
fb6335ab60 | |||
89bd008445 | |||
caafb41eb5 | |||
31b81a7439 | |||
f7b1973358 | |||
32f01da49a | |||
59684c97b0 | |||
a32a0a3a97 | |||
96f5b0929d | |||
8e6cf7fca8 | |||
fdbe8741c9 | |||
775fb2c340 | |||
b60f594798 | |||
cc49790bdb | |||
a4bc19c530 | |||
f7985dbdb7 | |||
0bdcb5c1e0 | |||
a0d25db4a5 | |||
625474c4e2 | |||
1cd2a6328a | |||
d6bafe4fe3 | |||
6a2ef15355 | |||
a8ca560503 | |||
ad361808ec | |||
c47639f2b1 | |||
ba90a85f7b | |||
d3b569557f | |||
c9090ffa31 | |||
341bf39d23 | |||
3778ac26aa | |||
6cfc6f5bb2 | |||
47a3b4d56b | |||
c72ed991ad | |||
78bfdf78ea | |||
a24ee6add4 | |||
df3074fdfe | |||
f7424d5aeb | |||
a593ffa6f3 | |||
761c6d0df7 | |||
3e65d1458e | |||
a4b5cb8376 | |||
c785a1e474 | |||
3adc472f06 | |||
e7081b8b7c | |||
9b3a548f6f | |||
90b3502bb8 | |||
e19b31db29 | |||
bd015f14e8 | |||
ca7ba12fc6 | |||
ae05ec69c4 | |||
92dc3b91d8 | |||
8bd697b316 | |||
eda4c3eb4c | |||
4d0c2ed1f6 | |||
eda6a5d52a | |||
c1c54ed0f2 | |||
6b73d09ba1 | |||
ac85cbb28a | |||
b0cebdba6b | |||
933a9112da | |||
8c37b7e8f2 | |||
c8e909f8c9 | |||
69ae3634c7 | |||
95248f46a1 | |||
b3c7df1783 | |||
c56679e8e1 | |||
041c599511 | |||
6343f71be5 | |||
89f32f808f | |||
7ae23adaff | |||
a08f50badd | |||
0b6e75a85e | |||
4291758079 | |||
b44d36cf95 | |||
a038bb9ae3 | |||
9d28147acb | |||
d116861c8e | |||
9a70f1a1d9 | |||
8516473340 | |||
cab69f689f | |||
822e83ebb0 | |||
b2bc50dbd1 | |||
e748adda2e | |||
630d93150a | |||
76f1f9f1e3 | |||
c47d85b038 | |||
197cf09689 | |||
e67ebb7f70 | |||
3524946581 | |||
9276dad42c | |||
2a2f9a9a19 | |||
909e70bd61 | |||
8ac9719832 | |||
3eff7c6f59 | |||
17fbbfba91 | |||
b232dded77 |
40
.travis.yml
40
.travis.yml
@ -1,11 +1,11 @@
|
||||
language: node_js
|
||||
sudo: false
|
||||
node_js:
|
||||
- '4.2.1'
|
||||
- '5.4.1'
|
||||
|
||||
branches:
|
||||
except:
|
||||
- g3sync
|
||||
- g3_v2_0
|
||||
|
||||
cache:
|
||||
directories:
|
||||
@ -37,8 +37,10 @@ env:
|
||||
# Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
||||
- MODE=dart DART_CHANNEL=stable DART_VERSION=$DART_STABLE_VERSION
|
||||
- MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=browserstack DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=browserstack_required DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=browserstack_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=js DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
- MODE=router DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION
|
||||
@ -48,8 +50,8 @@ env:
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- env: "MODE=saucelabs DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=browserstack DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=saucelabs_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=browserstack_optional DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
- env: "MODE=dart_experimental DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
# TODO(alxhub): remove when dartdoc #1039 is in dev channel
|
||||
- env: "MODE=dart DART_CHANNEL=dev DART_VERSION=$DART_DEV_VERSION"
|
||||
@ -58,6 +60,7 @@ addons:
|
||||
firefox: "38.0"
|
||||
|
||||
before_install:
|
||||
- npm install -g npm@3.5.3
|
||||
- node tools/analytics/build-analytics start ci job
|
||||
- node tools/analytics/build-analytics start ci before_install
|
||||
- echo ${TSDRC} > .tsdrc
|
||||
@ -75,7 +78,9 @@ install:
|
||||
# Check the size of caches
|
||||
- du -sh ./node_modules || true
|
||||
# Install npm dependecies
|
||||
- npm install
|
||||
# check-node-modules will exit(1) if we don't need to install
|
||||
# we need to manually kick off the postinstall script if check-node-modules exit(0)s
|
||||
- node tools/npm/check-node-modules --purge && npm install || npm run postinstall
|
||||
- node tools/analytics/build-analytics success ci install
|
||||
|
||||
before_script:
|
||||
@ -109,24 +114,3 @@ notifications:
|
||||
slack:
|
||||
secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
||||
|
||||
deploy:
|
||||
- provider: gcs
|
||||
# This is for project angular-github-babysitter
|
||||
access_key_id: GOOGIOQTDBEOPBUAWFZQ
|
||||
secret_access_key:
|
||||
secure: "MEDggllZ5fw4wI9CEUi8WR6jKsKXqdRF/DLxSNC2JpzM5RlVeBm0uqjntYT1Cf1dASvQ2/+vZCUikL/3A48NcoEYRHXGmxu8D6t/SvleQD8Xv434xFOdsa2QqP/HiCtqCLOI5jJz1JVoB5nNyKKZ33ogTUL1LV1TfcrAioyizW8="
|
||||
# this bucket has a lifecycle to delete after 90 days:
|
||||
# $ echo '{"rule": [{"action": {"type": "Delete"}, "condition": {"age": 90}}]}' > lifecycle.json
|
||||
# $ gsutil lifecycle set lifecycle.json gs://angular2-snapshots
|
||||
bucket: angular2-snapshots
|
||||
# don't delete generated files
|
||||
skip_cleanup: true
|
||||
# serve to public at https://storage.googleapis.com/angular2-snapshots/SHA/dist.tgz
|
||||
acl: public-read
|
||||
# upload the .tgz archive created in scripts/ci/build_and_test.sh
|
||||
local-dir: deploy
|
||||
# create a "subdirectory" for each commit
|
||||
upload-dir: $TRAVIS_COMMIT
|
||||
on:
|
||||
repo: angular/angular
|
||||
condition: "$MODE = build_only"
|
||||
|
112
CHANGELOG.md
112
CHANGELOG.md
@ -1,5 +1,113 @@
|
||||
<a name="2.0.0-beta.2"></a>
|
||||
# 2.0.0-beta.2 (2016-01-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **bundles:** testing bundle should include browser platform ([4a41442](https://github.com/angular/angular/commit/4a41442)), closes [#6626](https://github.com/angular/angular/issues/6626)
|
||||
* **ChangeDetection:** chain expressions evaluate to the last expression (codegen) ([933a911](https://github.com/angular/angular/commit/933a911)), closes [#4782](https://github.com/angular/angular/issues/4782) [#5892](https://github.com/angular/angular/issues/5892)
|
||||
* **core:** always remove DOM listeners and stream subscriptions ([0ae7775](https://github.com/angular/angular/commit/0ae7775))
|
||||
* **Dart:** make some playground samples run with Dart Dev Compiler ([3e65d14](https://github.com/angular/angular/commit/3e65d14)), closes [#6441](https://github.com/angular/angular/issues/6441)
|
||||
* **dart/transform:** Ensure template codegen is completed sync ([5f0baaa](https://github.com/angular/angular/commit/5f0baaa)), closes [#6603](https://github.com/angular/angular/issues/6603)
|
||||
* **ddc:** router, compiler, web worker fixes for DDC ([db87bae](https://github.com/angular/angular/commit/db87bae)), closes [#6693](https://github.com/angular/angular/issues/6693)
|
||||
* **ddc:** type fixes necessary to bring DDC severe count to 0 ([4282297](https://github.com/angular/angular/commit/4282297))
|
||||
* **ddc:** use dynamic types in reflection typedefs ([c785a1e](https://github.com/angular/angular/commit/c785a1e)), closes [#6437](https://github.com/angular/angular/issues/6437)
|
||||
* **directive:** throw if output the same event more than once ([8c37b7e](https://github.com/angular/angular/commit/8c37b7e))
|
||||
* **HtmlLexer:** fix for unicode chars ([a24ee6a](https://github.com/angular/angular/commit/a24ee6a)), closes [#6036](https://github.com/angular/angular/issues/6036) [#6061](https://github.com/angular/angular/issues/6061)
|
||||
* **perf:** faster looseIdentical implementation ([761c6d0](https://github.com/angular/angular/commit/761c6d0)), closes [#6364](https://github.com/angular/angular/issues/6364)
|
||||
* **template_compiler:** Fix erroneous cycle detection ([eda4c3e](https://github.com/angular/angular/commit/eda4c3e)), closes [#6404](https://github.com/angular/angular/issues/6404) [#6474](https://github.com/angular/angular/issues/6474)
|
||||
* **testing:** remove test zone for now and rely on returned promises ([c72ed99](https://github.com/angular/angular/commit/c72ed99)), closes [#6359](https://github.com/angular/angular/issues/6359) [#6601](https://github.com/angular/angular/issues/6601)
|
||||
* **transformer:** record HostBinding annotations applied to getters ([a593ffa](https://github.com/angular/angular/commit/a593ffa)), closes [#6283](https://github.com/angular/angular/issues/6283)
|
||||
* **web_workers:** support @AngularEntrypoint in web workers ([ac85cbb](https://github.com/angular/angular/commit/ac85cbb)), closes [#6013](https://github.com/angular/angular/issues/6013)
|
||||
|
||||
### Features
|
||||
|
||||
* **core/application_ref:** Allow asyncronous app initializers. ([df3074f](https://github.com/angular/angular/commit/df3074f)), closes [#5929](https://github.com/angular/angular/issues/5929) [#6063](https://github.com/angular/angular/issues/6063)
|
||||
* **dart/transform:** DirectiveProcessor: do not process generated files ([78bfdf7](https://github.com/angular/angular/commit/78bfdf7)), closes [#6517](https://github.com/angular/angular/issues/6517)
|
||||
* **dart/transform:** Promote missing Directive warning to error ([47a3b4d](https://github.com/angular/angular/commit/47a3b4d)), closes [#6519](https://github.com/angular/angular/issues/6519) [#6568](https://github.com/angular/angular/issues/6568)
|
||||
* **test:** allow tests to specify the platform and application providers used ([b0cebdb](https://github.com/angular/angular/commit/b0cebdb)), closes [#5351](https://github.com/angular/angular/issues/5351) [#5585](https://github.com/angular/angular/issues/5585) [#5975](https://github.com/angular/angular/issues/5975)
|
||||
* **testability:** Expose function frameworkStabilizers ([69ae363](https://github.com/angular/angular/commit/69ae363)), closes [#5485](https://github.com/angular/angular/issues/5485)
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* there's a chance of breakage as router's Instruction constructor
|
||||
signature changed.
|
||||
|
||||
* `Renderer.listen` now has to return a function that
|
||||
removes the event listener.
|
||||
|
||||
* remove TemplateRef.elementRef setter
|
||||
|
||||
* Tests are now required to use `setBaseTestProviders`
|
||||
to set up. Assuming your tests are run on a browser, setup would change
|
||||
as follows.
|
||||
Before:
|
||||
```js
|
||||
// Somewhere in test setup
|
||||
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
|
||||
BrowserDomAdapter.makeCurrent
|
||||
```
|
||||
After:
|
||||
```js
|
||||
// Somewhere in the test setup
|
||||
import {setBaseTestProviders} from 'angular2/testing';
|
||||
import {
|
||||
TEST_BROWSER_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_APPLICATION_PROVIDERS
|
||||
} from 'angular2/platform/testing/browser';
|
||||
setBaseTestProviders(TEST_BROWSER_PLATFORM_PROVIDERS,
|
||||
TEST_BROWSER_APPLICATION_PROVIDERS);
|
||||
```
|
||||
|
||||
* This is very unlikely to be breaking, but I'm still marking just in case. The only change to the user should be that dev mode is driven by Dart's checked mode, like it was in the past.
|
||||
|
||||
<a name="2.0.0-beta.1"></a>
|
||||
# 2.0.0-beta.1 catamorphic-involution (2016-01-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **benchpress:** fix flake ([9d28147](https://github.com/angular/angular/commit/9d28147)), closes [#6161](https://github.com/angular/angular/issues/6161)
|
||||
* **CHANGELOG:** typo ([d116861](https://github.com/angular/angular/commit/d116861)), closes [#6075](https://github.com/angular/angular/issues/6075) [#6078](https://github.com/angular/angular/issues/6078)
|
||||
* **code size:** revert previous devMode change to restore size targets ([c47d85b](https://github.com/angular/angular/commit/c47d85b))
|
||||
* **core:** IE only supports parentNode ([630d931](https://github.com/angular/angular/commit/630d931)), closes [#5994](https://github.com/angular/angular/issues/5994)
|
||||
* **docs:** fix an import in TOOLS_DART.md ([3524946](https://github.com/angular/angular/commit/3524946)), closes [#5923](https://github.com/angular/angular/issues/5923)
|
||||
* **forms:** fix SelectControlValueAccessor not to call onChange twice ([b44d36c](https://github.com/angular/angular/commit/b44d36c)), closes [#5969](https://github.com/angular/angular/issues/5969)
|
||||
* **router:** correctly sort route matches with children by specificity ([b2bc50d](https://github.com/angular/angular/commit/b2bc50d)), closes [#5848](https://github.com/angular/angular/issues/5848) [#6011](https://github.com/angular/angular/issues/6011)
|
||||
* **router:** preserve specificity for redirects ([a038bb9](https://github.com/angular/angular/commit/a038bb9)), closes [#5933](https://github.com/angular/angular/issues/5933)
|
||||
* **TemplateParser:** do not match on attrs that are bindings ([9a70f1a](https://github.com/angular/angular/commit/9a70f1a)), closes [#5914](https://github.com/angular/angular/issues/5914)
|
||||
|
||||
### Features
|
||||
|
||||
* **core:** improve NoAnnotationError message ([197cf09](https://github.com/angular/angular/commit/197cf09)), closes [#4866](https://github.com/angular/angular/issues/4866) [#5927](https://github.com/angular/angular/issues/5927)
|
||||
* **core:** improve stringify for dart to handle closures ([e67ebb7](https://github.com/angular/angular/commit/e67ebb7))
|
||||
* **core:** speed up view creation via code gen for view factories. ([7ae23ad](https://github.com/angular/angular/commit/7ae23ad)), closes [#5993](https://github.com/angular/angular/issues/5993)
|
||||
* **router:** support links with just auxiliary routes ([2a2f9a9](https://github.com/angular/angular/commit/2a2f9a9)), closes [#5930](https://github.com/angular/angular/issues/5930)
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **dart/transform:** Avoid unnecessary reads for files with no view ([89f32f8](https://github.com/angular/angular/commit/89f32f8)), closes [#6183](https://github.com/angular/angular/issues/6183)
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Platform pipes can only contain types and arrays of types,
|
||||
but no bindings any more.
|
||||
* When using transformers, platform pipes need to be specified explicitly
|
||||
in the pubspec.yaml via the new config option
|
||||
`platform_pipes`.
|
||||
* `Compiler.compileInHost` now returns a `HostViewFactoryRef`
|
||||
* Component view is not yet created when component constructor is called.
|
||||
-> use `onInit` lifecycle callback to access the view of a component
|
||||
* `ViewRef#setLocal` has been moved to new type `EmbeddedViewRef`
|
||||
* `internalView` is gone, use `EmbeddedViewRef.rootNodes` to access
|
||||
the root nodes of an embedded view
|
||||
* `renderer.setElementProperty`, `..setElementStyle`, `..setElementAttribute` now
|
||||
take a native element instead of an ElementRef
|
||||
* `Renderer` interface now operates on plain native nodes,
|
||||
instead of `RenderElementRef`s or `RenderViewRef`s
|
||||
<a name="2.0.0-beta.0"></a>
|
||||
# 2.0.0-beta.0 sonambulent-inauguration (2015-12-15)
|
||||
# 2.0.0-beta.0 somnambulant-inauguration (2015-12-15)
|
||||
|
||||
**Enjoy!**
|
||||
|
||||
@ -154,7 +262,7 @@ Use imports from `angular2/compiler` instead.
|
||||
<my-cmp (myEvent)="action()">
|
||||
<my-cmp [(myProp)]="prop">
|
||||
<input #myInput>`,
|
||||
<template ngFor="#myItem" [ngForOf]=items #myIndex="index">
|
||||
<template ngFor "#myItem" [ngForOf]=items #myIndex="index">
|
||||
```
|
||||
|
||||
The full migration instruction can be found at [angular2/docs/migration/kebab-case.md](https://github.com/angular/angular/blob/master/modules/angular2/docs/migration/kebab-case.md).
|
||||
|
@ -11,8 +11,11 @@ Someone with committer access will do the rest.
|
||||
We have automated the process for merging pull requests into master. Our goal is to minimize the disruption for
|
||||
Angular committers and also prevent breakages on master.
|
||||
|
||||
When a PR is ready to merge, a project member in the CoreTeamMember list (see below) can add the special label,
|
||||
`PR: merge`.
|
||||
When a PR has `pr_state: LGTM` and is ready to merge, you should add the `pr_action: merge` label.
|
||||
Currently (late 2015), we need to ensure that each PR will cleanly merge into the Google-internal version control,
|
||||
so the caretaker reviews the changes manually.
|
||||
|
||||
After this review, the caretaker adds `zomg_admin: do_merge` which is restricted to admins only.
|
||||
A robot running as [mary-poppins](https://github.com/mary-poppins)
|
||||
is notified that the label was added by an authorized person,
|
||||
and will create a new branch in the angular project, using the convention `presubmit-{username}-pr-{number}`.
|
||||
@ -26,6 +29,6 @@ Finally, after merge `mary-poppins` removes the presubmit branch.
|
||||
|
||||
## Administration
|
||||
|
||||
The list of users who can trigger a merge by adding the label is stored in our appengine app datastore.
|
||||
The list of users who can trigger a merge by adding the `zomg_admin: do_merge` label is stored in our appengine app datastore.
|
||||
Edit the contents of the [CoreTeamMember Table](
|
||||
https://console.developers.google.com/project/angular2-automation/datastore/query?queryType=KindQuery&namespace=&kind=CoreTeamMember)
|
||||
|
@ -180,7 +180,8 @@ Must be one of the following:
|
||||
* **refactor**: A code change that neither fixes a bug nor adds a feature
|
||||
* **perf**: A code change that improves performance
|
||||
* **test**: Adding missing tests or correcting existing tests
|
||||
* **build** Changes that affect the build system, CI configuration or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **build**: Changes that affect the build system, CI configuration or external dependencies (example scopes: gulp, broccoli, npm)
|
||||
* **ci**: Any changes to our CI configuration files and scripts (Travis, Circle CI, BrowserStack, SauceLabs)
|
||||
* **chore**: Other changes that don't modify `src` or `test` files
|
||||
|
||||
### Scope
|
||||
|
30
DEVELOPER.md
30
DEVELOPER.md
@ -23,7 +23,16 @@ if you'd like to contribute to Angular.
|
||||
Before you can build and test Angular, you must install and configure the
|
||||
following products on your development machine:
|
||||
|
||||
* [Dart](https://www.dartlang.org) (version ` >=1.12.0 <2.0.0`), specifically the Dart-SDK and
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org), (version `>=5.4.1 <6`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(version `>=3.5.3 <4.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* *Optional*: [Dart](https://www.dartlang.org) (version ` >=1.13.2 <2.0.0`), specifically the Dart-SDK and
|
||||
Dartium (a version of [Chromium](http://www.chromium.org) with native support for Dart through
|
||||
the Dart VM). One of the **simplest** ways to get both is to install the **Dart Editor bundle**,
|
||||
which includes the editor, SDK and Dartium. See the [Dart tools](https://www.dartlang.org/tools)
|
||||
@ -33,19 +42,6 @@ following products on your development machine:
|
||||
to the `Path` (e.g. `path-to-dart-sdk-folder\bin`) and a new `DARTIUM_BIN` environment variable must be
|
||||
created, pointing to the executable (e.g. `path-to-dartium-folder\chrome.exe).`
|
||||
|
||||
* [Git](http://git-scm.com) and/or the **GitHub app** (for [Mac](http://mac.github.com) or
|
||||
[Windows](http://windows.github.com)); [GitHub's Guide to Installing
|
||||
Git](https://help.github.com/articles/set-up-git) is a good source of information.
|
||||
|
||||
* [Node.js](http://nodejs.org), (version `>=4.2.1 <5`) which is used to run a development web server,
|
||||
run tests, and generate distributable files. We also use Node's Package Manager, `npm`
|
||||
(version `>=2.14.7 <3.0`), which comes with Node. Depending on your system, you can install Node either from
|
||||
source or as a pre-packaged bundle.
|
||||
|
||||
* [Chrome Canary](https://www.google.com/chrome/browser/canary.html), a version of Chrome with
|
||||
bleeding edge functionality, built especially for developers (and early adopters).
|
||||
|
||||
* [Bower](http://bower.io/).
|
||||
|
||||
|
||||
## Getting the Sources
|
||||
@ -200,15 +196,15 @@ Then, in another terminal:
|
||||
export SAUCE_USERNAME='my_user'; export SAUCE_ACCESS_KEY='my_key';
|
||||
export BROWSER_STACK_USERNAME='my_user'; export BROWSER_STACK_ACCESS_KEY='my_key';
|
||||
```
|
||||
- Then run `gulp test.unit.js.(saucelabs|browserstack) --browsers=option1,option2,..,optionN`
|
||||
- Then run `gulp test.unit.js.(sauce|browserstack) --browsers=option1,option2,..,optionN`
|
||||
The options are any mix of browsers and aliases which are defined in the [browser-providers.conf.js](https://github.com/angular/angular/blob/master/browser-providers.conf.js) file.
|
||||
They are case insensitive, and the `SL_` or `BS_` prefix must not be added for browsers.
|
||||
|
||||
Some examples of commands:
|
||||
```
|
||||
gulp test.unit.js.saucelabs --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
||||
gulp test.unit.js.sauce --browsers=Safari8,ie11 //run in Sauce Labs with Safari 8 and IE11
|
||||
gulp test.unit.js.browserstack --browsers=Safari,IE //run in Browser Stack with Safari 7, Safari 8, Safari 9, IE 9, IE 10 and IE 11
|
||||
gulp test.unit.js.saucelabs --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
||||
gulp test.unit.js.sauce --browsers=IOS,safari8,android5.1 //run in Sauce Labs with iOS 7, iOS 8, iOs 9, Safari 8 and Android 5.1
|
||||
```
|
||||
|
||||
### E2E tests
|
||||
|
215
LICENSE
215
LICENSE
@ -1,202 +1,21 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
The MIT License
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
Copyright (c) 2014-2016 Google, Inc. http://angular.io
|
||||
|
||||
1. Definitions.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {yyyy} {name of copyright owner}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
13
README.md
13
README.md
@ -12,20 +12,12 @@ Angular
|
||||
Angular is a development platform for building mobile and desktop web applications. This is the
|
||||
repository for [Angular 2][ng2], both the JavaScript (JS) and [Dart][dart] versions.
|
||||
|
||||
Angular 2 is currently in **Developer Preview**. We recommend using Angular 1.X for production
|
||||
applications:
|
||||
|
||||
* [AngularJS][ngJS]: [angular/angular.js](http://github.com/angular/angular.js).
|
||||
* [AngularDart][ngDart]: [angular/angular.dart](http://github.com/angular/angular.dart).
|
||||
Angular 2 is currently in **Beta**.
|
||||
|
||||
## Quickstart
|
||||
|
||||
[Get started in 5 minutes][quickstart].
|
||||
|
||||
## Setup & Install Angular 2
|
||||
|
||||
Follow the instructions given on the [Angular download page][download].
|
||||
|
||||
|
||||
## Want to help?
|
||||
|
||||
@ -36,8 +28,7 @@ guidelines for [contributing][contributing] and then check out one of our issues
|
||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||
[dart]: http://www.dartlang.org
|
||||
[dartium]: http://www.dartlang.org/tools/dartium
|
||||
[download]: http://angular.io/download/
|
||||
[quickstart]: https://angular.io/docs/js/latest/quickstart.html
|
||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||
[ng2]: http://angular.io
|
||||
[ngDart]: http://angulardart.org
|
||||
[ngJS]: http://angularjs.org
|
||||
|
@ -21,7 +21,7 @@ By default the debugging tools are disabled.
|
||||
Enable the debugging tools as follows:
|
||||
|
||||
```dart
|
||||
import 'package:angular2/tools.dart';
|
||||
import 'package:angular2/platform/browser.dart';
|
||||
|
||||
main() async {
|
||||
var appRef = await bootstrap(Application);
|
||||
|
@ -1,3 +1,32 @@
|
||||
// Unique place to configure the browsers which are used in the different CI jobs in Sauce Labs (SL) and BrowserStack (BS).
|
||||
// If the target is set to null, then the browser is not run anywhere during CI.
|
||||
// If a category becomes empty (e.g. BS and required), then the corresponding job must be commented out in Travis configuration.
|
||||
var CIconfiguration = {
|
||||
'Chrome': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Firefox': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'ChromeBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxBeta': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'ChromeDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'FirefoxDev': { unitTest: {target: null, required: true}, e2e: {target: null, required: true}},
|
||||
'IE9': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'IE10': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'IE11': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Edge': { unitTest: {target: 'SL', required: true}, e2e: {target: null, required: true}},
|
||||
'Android4.1': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.2': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.3': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android4.4': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Android5': { unitTest: {target: 'SL', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
|
||||
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||
// TODO(mlaval): iOS9 deactivated as not reliable, reactivate after https://github.com/angular/angular/issues/5408
|
||||
'iOS9': { unitTest: {target: null, required: false}, e2e: {target: null, required: true}},
|
||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||
};
|
||||
|
||||
var customLaunchers = {
|
||||
'DartiumWithWebPlatform': {
|
||||
base: 'Dartium',
|
||||
@ -47,7 +76,7 @@ var customLaunchers = {
|
||||
platform: 'OS X 10.10',
|
||||
version: '8'
|
||||
},
|
||||
'SL_SAFARI9.0': {
|
||||
'SL_SAFARI9': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.11',
|
||||
@ -119,7 +148,7 @@ var customLaunchers = {
|
||||
platform: 'Linux',
|
||||
version: '4.4'
|
||||
},
|
||||
'SL_ANDROID5.1': {
|
||||
'SL_ANDROID5': {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'android',
|
||||
platform: 'Linux',
|
||||
@ -239,21 +268,18 @@ var customLaunchers = {
|
||||
}
|
||||
};
|
||||
|
||||
// iOS9 deactivated as not reliable in both providers
|
||||
// TODO(mlaval): reactivate after https://github.com/angular/angular/issues/5408
|
||||
|
||||
var sauceAliases = {
|
||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9.0'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1'],
|
||||
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9.0'],
|
||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||
'CI': ['SL_CHROME',' SL_FIREFOX', 'SL_CHROMEDEV', 'SL_FIREFOXBETA', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE',
|
||||
'SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5.1']
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'SL', false)
|
||||
};
|
||||
|
||||
var browserstackAliases = {
|
||||
@ -264,7 +290,8 @@ var browserstackAliases = {
|
||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
||||
'CI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_IOS7', 'BS_IOS8', 'BS_WINDOWSPHONE']
|
||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
@ -277,3 +304,14 @@ if (process.env.TRAVIS) {
|
||||
process.env.SAUCE_ACCESS_KEY = process.env.SAUCE_ACCESS_KEY.split('').reverse().join('');
|
||||
process.env.BROWSER_STACK_ACCESS_KEY = process.env.BROWSER_STACK_ACCESS_KEY.split('').reverse().join('');
|
||||
}
|
||||
|
||||
function buildConfiguration(type, target, required) {
|
||||
return Object.keys(CIconfiguration)
|
||||
.filter((item) => {
|
||||
var conf = CIconfiguration[item][type];
|
||||
return conf.required === required && conf.target === target;
|
||||
})
|
||||
.map((item) => {
|
||||
return target + '_' + item.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
6
circle.yml
Normal file
6
circle.yml
Normal file
@ -0,0 +1,6 @@
|
||||
machine:
|
||||
node:
|
||||
version: 5.4.1
|
||||
test:
|
||||
override:
|
||||
- npm run build
|
215
gulpfile.js
215
gulpfile.js
@ -3,10 +3,10 @@
|
||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
||||
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||
require('./tools/check-environment')(
|
||||
{requiredNpmVersion: '>=2.14.7 <3.0.0', requiredNodeVersion: '>=4.2.1 <5.0.0'});
|
||||
{requiredNpmVersion: '>=3.5.3 <4.0.0', requiredNodeVersion: '>=5.4.1 <6.0.0'});
|
||||
|
||||
|
||||
var del = require('del');
|
||||
var fse = require('fs-extra');
|
||||
var gulp = require('gulp');
|
||||
var gulpPlugins = require('gulp-load-plugins')();
|
||||
var merge = require('merge');
|
||||
@ -42,7 +42,7 @@ if (cliArgs.projects) {
|
||||
|
||||
// --projects=angular2,angular2_material => {angular2: true, angular2_material: true}
|
||||
var allProjects =
|
||||
'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground,bundle_deps';
|
||||
'angular1_router,angular2,angular2_material,benchmarks,benchmarks_external,benchpress,playground,payload_tests,bundle_deps';
|
||||
var cliArgsProjects = (cliArgs.projects || allProjects)
|
||||
.split(',')
|
||||
.reduce((map, projectName) => {
|
||||
@ -168,24 +168,36 @@ var BENCHPRESS_BUNDLE_CONFIG = {
|
||||
dest: CONFIG.dest.bundles.benchpress
|
||||
};
|
||||
|
||||
var PAYLOAD_TESTS_CONFIG = {
|
||||
ts: {
|
||||
bundleName: 'app-bundle-deps.min.js',
|
||||
cases: ['hello_world'],
|
||||
dist: function(caseName, packaging) {
|
||||
return path.join(__dirname, CONFIG.dest.js.prod.es5, 'payload_tests', caseName,
|
||||
'ts/' + packaging);
|
||||
},
|
||||
systemjs: {sizeLimits: {'uncompressed': 850 * 1024, 'gzip level=9': 165 * 1024}},
|
||||
webpack: {sizeLimits: {'uncompressed': 550 * 1024, 'gzip level=9': 120 * 1024}}
|
||||
}
|
||||
};
|
||||
|
||||
// ------------
|
||||
// clean
|
||||
|
||||
gulp.task('build/clean.tools', function() { del(path.join('dist', 'tools')); });
|
||||
gulp.task('build/clean.tools', (done) => fse.remove(path.join('dist', 'tools'), done));
|
||||
|
||||
gulp.task('build/clean.js', function(done) { del(CONFIG.dest.js.all, done); });
|
||||
gulp.task('build/clean.js', (done) => fse.remove(CONFIG.dest.js.all, done));
|
||||
|
||||
gulp.task('build/clean.dart', function(done) { del(CONFIG.dest.dart, done); });
|
||||
gulp.task('build/clean.dart', (done) => fse.remove(CONFIG.dest.dart, done));
|
||||
|
||||
gulp.task('build/clean.docs', function(done) { del(CONFIG.dest.docs, done); });
|
||||
gulp.task('build/clean.docs', (done) => fse.remove(CONFIG.dest.docs, done));
|
||||
|
||||
gulp.task('build/clean.docs_angular_io',
|
||||
function(done) { del(CONFIG.dest.docs_angular_io, done); });
|
||||
gulp.task('build/clean.docs_angular_io', (done) => fse.remove(CONFIG.dest.docs_angular_io, done));
|
||||
|
||||
gulp.task('build/clean.bundles', function(done) { del(CONFIG.dest.bundles.all, done); });
|
||||
gulp.task('build/clean.bundles', (done) => fse.remove(CONFIG.dest.bundles.all, done));
|
||||
|
||||
gulp.task('build/clean.bundles.benchpress',
|
||||
function(done) { del(CONFIG.dest.bundles.benchpress, done); });
|
||||
(done) => fse.remove(CONFIG.dest.bundles.benchpress, done));
|
||||
|
||||
// ------------
|
||||
// transpile
|
||||
@ -313,7 +325,9 @@ gulp.task('lint', ['build.tools'], function() {
|
||||
"requireParameterType": true,
|
||||
"requireReturnType": true,
|
||||
"semicolon": true,
|
||||
"variable-name": [true, "ban-keywords"]
|
||||
|
||||
// TODO: find a way to just screen for reserved names
|
||||
"variable-name": false
|
||||
}
|
||||
};
|
||||
return gulp.src(['modules/angular2/src/**/*.ts', '!modules/angular2/src/testing/**'])
|
||||
@ -369,7 +383,7 @@ function proxyServeDart() {
|
||||
|
||||
// ------------------
|
||||
// web servers
|
||||
gulp.task('serve.js.dev', ['build.js'], function(neverDone) {
|
||||
gulp.task('serve.js.dev', ['build.js.dev'], function(neverDone) {
|
||||
var watch = require('./tools/build/watch');
|
||||
|
||||
watch('modules/**', {ignoreInitial: true}, '!broccoli.js.dev');
|
||||
@ -638,7 +652,7 @@ gulp.task('test.unit.dart', function(done) {
|
||||
// This test will fail if the size of our hello_world app goes beyond one of
|
||||
// these values when compressed at the specified level.
|
||||
// Measure in bytes.
|
||||
var _DART_PAYLOAD_SIZE_LIMITS = {'uncompressed': 375 * 1024, 'gzip level=6': 105 * 1024};
|
||||
var _DART_PAYLOAD_SIZE_LIMITS = {'uncompressed': 320 * 1024, 'gzip level=9': 90 * 1024};
|
||||
gulp.task('test.payload.dart/ci', function(done) {
|
||||
runSequence('build/packages.dart', '!pubget.payload.dart', '!pubbuild.payload.dart',
|
||||
'!checkAndReport.payload.dart', done);
|
||||
@ -658,6 +672,103 @@ gulp.task('!checkAndReport.payload.dart', function() {
|
||||
{failConditions: _DART_PAYLOAD_SIZE_LIMITS, prefix: 'hello_world'});
|
||||
});
|
||||
|
||||
// JS payload size tracking
|
||||
gulp.task('test.payload.js/ci', function(done) {
|
||||
runSequence('build.payload.js', '!checkAndReport.payload.js', sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('build.payload.js', ['build.js'], function(done) {
|
||||
runSequence('!build.payload.js.webpack', '!build.payload.js.systemjs', sequenceComplete(done));
|
||||
});
|
||||
|
||||
gulp.task('!build.payload.js.webpack', function() {
|
||||
var q = require('q');
|
||||
var webpack = q.denodeify(require('webpack'));
|
||||
|
||||
var ES5_PROD_ROOT = __dirname + '/' + CONFIG.dest.js.prod.es5;
|
||||
|
||||
return q.all(PAYLOAD_TESTS_CONFIG.ts.cases.map(function(caseName) {
|
||||
var CASE_PATH = PAYLOAD_TESTS_CONFIG.ts.dist(caseName, 'webpack');
|
||||
|
||||
return webpack({
|
||||
// bundle app + framework
|
||||
entry: CASE_PATH + '/index.js',
|
||||
output: {path: CASE_PATH, filename: "app-bundle.js"},
|
||||
resolve: {
|
||||
extensions: ['', '.js'],
|
||||
packageAlias: '', // option added to ignore "broken" package.json in our dist folder
|
||||
root: [ES5_PROD_ROOT]
|
||||
}
|
||||
})
|
||||
.then(function() { // pad bundle with mandatory dependencies
|
||||
return new Promise(function(resolve, reject) {
|
||||
gulp.src([
|
||||
'node_modules/zone.js/dist/zone-microtask.js',
|
||||
'node_modules/zone.js/dist/long-stack-trace-zone.js',
|
||||
'node_modules/reflect-metadata/Reflect.js',
|
||||
CASE_PATH + '/app-bundle.js'
|
||||
])
|
||||
.pipe(gulpPlugins.concat(PAYLOAD_TESTS_CONFIG.ts.bundleName))
|
||||
.pipe(gulpPlugins.uglify())
|
||||
.pipe(gulp.dest(CASE_PATH))
|
||||
.on('end', resolve)
|
||||
.on('error', reject);
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('!build.payload.js.systemjs', function() {
|
||||
var bundler = require('./tools/build/bundle');
|
||||
|
||||
return Promise.all(PAYLOAD_TESTS_CONFIG.ts.cases.map(function(caseName) {
|
||||
var CASE_PATH = PAYLOAD_TESTS_CONFIG.ts.dist(caseName, 'systemjs');
|
||||
|
||||
return bundler
|
||||
.bundle(
|
||||
{
|
||||
paths: {'index': CASE_PATH + '/index.js'},
|
||||
meta: {'angular2/core': {build: false}, 'angular2/platform/browser': {build: false}}
|
||||
},
|
||||
'index', CASE_PATH + '/index.register.js', {})
|
||||
.then(function() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
gulp.src([
|
||||
'node_modules/systemjs/dist/system.src.js',
|
||||
'dist/js/prod/es5/bundle/angular2-polyfills.js',
|
||||
'dist/js/prod/es5/bundle/angular2.js',
|
||||
'dist/js/prod/es5//rxjs/bundles/Rx.js',
|
||||
CASE_PATH + '/index.register.js',
|
||||
'tools/build/systemjs/payload_tests_import.js'
|
||||
])
|
||||
.pipe(gulpPlugins.concat(PAYLOAD_TESTS_CONFIG.ts.bundleName))
|
||||
.pipe(gulpPlugins.uglify())
|
||||
.pipe(gulp.dest(CASE_PATH))
|
||||
.on('end', resolve)
|
||||
.on('error', reject);
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
gulp.task('!checkAndReport.payload.js', function() {
|
||||
var reportSize = require('./tools/analytics/reportsize');
|
||||
|
||||
function caseSizeStream(caseName, packaging) {
|
||||
return reportSize(PAYLOAD_TESTS_CONFIG.ts.dist(caseName, packaging) + '/' +
|
||||
PAYLOAD_TESTS_CONFIG.ts.bundleName,
|
||||
{
|
||||
failConditions: PAYLOAD_TESTS_CONFIG.ts[packaging].sizeLimits,
|
||||
prefix: caseName + '_' + packaging
|
||||
})
|
||||
}
|
||||
|
||||
return PAYLOAD_TESTS_CONFIG.ts.cases.reduce(function(sizeReportingStreams, caseName) {
|
||||
sizeReportingStreams.add(caseSizeStream(caseName, 'systemjs'));
|
||||
sizeReportingStreams.add(caseSizeStream(caseName, 'webpack'));
|
||||
}, merge2());
|
||||
});
|
||||
|
||||
gulp.task('watch.dart.dev', function(done) {
|
||||
runSequence('build/tree.dart', 'build/pure-packages.dart', '!build/pubget.angular2.dart',
|
||||
'!build/change_detect.dart', '!build/remove-pub-symlinks', 'build.dart.material.css',
|
||||
@ -720,12 +831,19 @@ gulp.task('test.unit.js/ci', function(done) {
|
||||
});
|
||||
|
||||
gulp.task('test.unit.js.sauce/ci', function(done) {
|
||||
launchKarmaWithExternalBrowsers(['dots', 'saucelabs'], browserProvidersConf.sauceAliases.CI,
|
||||
done);
|
||||
var browsers = browserProvidersConf.sauceAliases.CI_REQUIRED;
|
||||
if (cliArgs.mode && cliArgs.mode == 'saucelabs_optional') {
|
||||
browsers = browserProvidersConf.sauceAliases.CI_OPTIONAL;
|
||||
}
|
||||
launchKarmaWithExternalBrowsers(['dots', 'saucelabs'], browsers, done);
|
||||
});
|
||||
|
||||
gulp.task('test.unit.js.browserstack/ci', function(done) {
|
||||
launchKarmaWithExternalBrowsers(['dots'], browserProvidersConf.browserstackAliases.CI, done);
|
||||
var browsers = browserProvidersConf.browserstackAliases.CI_REQUIRED;
|
||||
if (cliArgs.mode && cliArgs.mode == 'browserstack_optional') {
|
||||
browsers = browserProvidersConf.browserstackAliases.CI_OPTIONAL;
|
||||
}
|
||||
launchKarmaWithExternalBrowsers(['dots'], browsers, done);
|
||||
});
|
||||
|
||||
gulp.task('test.unit.dart/ci', function(done) {
|
||||
@ -899,24 +1017,20 @@ gulp.task('build/pure-packages.dart/standalone', function() {
|
||||
.pipe(gulp.dest(CONFIG.dest.dart));
|
||||
});
|
||||
|
||||
gulp.task('build/pure-packages.dart/license',
|
||||
function() {
|
||||
return gulp.src(['LICENSE'])
|
||||
.pipe(gulp.dest(path.join(CONFIG.dest.dart, 'angular2_testing')));
|
||||
})
|
||||
gulp.task('build/pure-packages.dart/license', function() {
|
||||
return gulp.src(['LICENSE']).pipe(gulp.dest(path.join(CONFIG.dest.dart, 'angular2_testing')));
|
||||
});
|
||||
|
||||
|
||||
gulp.task('build/pure-packages.dart/angular2', function() {
|
||||
var yaml = require('js-yaml');
|
||||
|
||||
return gulp.src([
|
||||
'modules_dart/transform/**/*',
|
||||
'!modules_dart/transform/**/*.proto',
|
||||
'!modules_dart/transform/pubspec.yaml',
|
||||
'!modules_dart/transform/**/packages{,/**}',
|
||||
])
|
||||
.pipe(gulp.dest(path.join(CONFIG.dest.dart, 'angular2')));
|
||||
});
|
||||
gulp.task('build/pure-packages.dart/angular2', function() {
|
||||
return gulp.src([
|
||||
'modules_dart/transform/**/*',
|
||||
'!modules_dart/transform/**/*.proto',
|
||||
'!modules_dart/transform/pubspec.yaml',
|
||||
'!modules_dart/transform/**/packages{,/**}',
|
||||
])
|
||||
.pipe(gulp.dest(path.join(CONFIG.dest.dart, 'angular2')));
|
||||
});
|
||||
|
||||
// Builds all Dart packages, but does not compile them
|
||||
gulp.task('build/packages.dart', function(done) {
|
||||
@ -973,15 +1087,19 @@ gulp.task('!build.tools', function() {
|
||||
gulp.task('broccoli.js.dev', ['build.tools'],
|
||||
function(done) { runSequence('!broccoli.js.dev', sequenceComplete(done)); });
|
||||
|
||||
gulp.task(
|
||||
'!broccoli.js.dev',
|
||||
() => angularBuilder.rebuildBrowserDevTree(
|
||||
{generateEs6: generateEs6, projects: cliArgsProjects, noTypeChecks: cliArgs.noTypeChecks}));
|
||||
gulp.task('!broccoli.js.dev', () => angularBuilder.rebuildBrowserDevTree({
|
||||
generateEs6: generateEs6,
|
||||
projects: cliArgsProjects,
|
||||
noTypeChecks: cliArgs.noTypeChecks,
|
||||
useBundles: cliArgs.useBundles
|
||||
}));
|
||||
|
||||
gulp.task(
|
||||
'!broccoli.js.prod',
|
||||
() => angularBuilder.rebuildBrowserProdTree(
|
||||
{generateEs6: generateEs6, projects: cliArgsProjects, noTypeChecks: cliArgs.noTypeChecks}));
|
||||
gulp.task('!broccoli.js.prod', () => angularBuilder.rebuildBrowserProdTree({
|
||||
generateEs6: generateEs6,
|
||||
projects: cliArgsProjects,
|
||||
noTypeChecks: cliArgs.noTypeChecks,
|
||||
useBundles: cliArgs.useBundles
|
||||
}));
|
||||
|
||||
gulp.task('build.js.dev', ['build/clean.js'], function(done) {
|
||||
runSequence('broccoli.js.dev', 'build.css.material', sequenceComplete(done));
|
||||
@ -1004,9 +1122,12 @@ var firstBuildJsCjs = true;
|
||||
* private task
|
||||
*/
|
||||
gulp.task('!build.js.cjs', function() {
|
||||
return angularBuilder
|
||||
.rebuildNodeTree(
|
||||
{generateEs6: generateEs6, projects: cliArgsProjects, noTypeChecks: cliArgs.noTypeChecks})
|
||||
return angularBuilder.rebuildNodeTree({
|
||||
generateEs6: generateEs6,
|
||||
projects: cliArgsProjects,
|
||||
noTypeChecks: cliArgs.noTypeChecks,
|
||||
useBundles: cliArgs.useBundles
|
||||
})
|
||||
.then(function() {
|
||||
if (firstBuildJsCjs) {
|
||||
firstBuildJsCjs = false;
|
||||
@ -1113,8 +1234,8 @@ gulp.task('!bundle.testing', ['build.js.dev'], function() {
|
||||
{sourceMaps: true});
|
||||
});
|
||||
|
||||
gulp.task('!bundles.js.docs', function() {
|
||||
gulp.src('modules/angular2/docs/bundles/*').pipe(gulp.dest('dist/js/bundle'));
|
||||
gulp.task('!bundles.js.docs', ['clean'], function() {
|
||||
return gulp.src('modules/angular2/docs/bundles/*').pipe(gulp.dest('dist/js/bundle'));
|
||||
});
|
||||
|
||||
gulp.task('!bundles.js.umd', ['build.js.dev'], function() {
|
||||
@ -1262,7 +1383,7 @@ gulp.task('!bundle.copy', function() {
|
||||
|
||||
gulp.task('!bundles.js.checksize', function(done) {
|
||||
var reportSize = require('./tools/analytics/reportsize');
|
||||
return reportSize('dist/js/bundle/**', {printToConsole: ['gzip level=2']});
|
||||
return reportSize('dist/js/bundle/**/*.js', {printToConsole: ['gzip level=2']});
|
||||
});
|
||||
|
||||
gulp.task('bundles.js',
|
||||
|
@ -79,7 +79,7 @@ module.exports = function(config) {
|
||||
|
||||
if (process.env.TRAVIS) {
|
||||
var buildId = 'TRAVIS #' + process.env.TRAVIS_BUILD_NUMBER + ' (' + process.env.TRAVIS_BUILD_ID + ')';
|
||||
if (process.env.MODE === 'saucelabs') {
|
||||
if (process.env.MODE.startsWith('saucelabs')) {
|
||||
config.sauceLabs.build = buildId;
|
||||
config.sauceLabs.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
|
||||
@ -89,7 +89,7 @@ module.exports = function(config) {
|
||||
config.transports = ['polling'];
|
||||
}
|
||||
|
||||
if (process.env.MODE === 'browserstack') {
|
||||
if (process.env.MODE.startsWith('browserstack')) {
|
||||
config.browserStack.build = buildId;
|
||||
config.browserStack.tunnelIdentifier = process.env.TRAVIS_JOB_NUMBER;
|
||||
}
|
||||
|
@ -195,6 +195,10 @@ var ListWrapper = {
|
||||
return array[0];
|
||||
},
|
||||
|
||||
last: function(array) {
|
||||
return (array && array.length) > 0 ? array[array.length - 1] : null;
|
||||
},
|
||||
|
||||
map: function (l, fn) {
|
||||
return l.map(fn);
|
||||
},
|
||||
|
@ -5,8 +5,7 @@ library angular2;
|
||||
*
|
||||
* This library does not include `bootstrap`. Import `bootstrap.dart` instead.
|
||||
*/
|
||||
export 'package:angular2/core.dart'
|
||||
hide forwardRef, resolveForwardRef, ForwardRefFn;
|
||||
export 'package:angular2/core.dart';
|
||||
export 'package:angular2/common.dart';
|
||||
export 'package:angular2/instrumentation.dart';
|
||||
export 'package:angular2/src/core/angular_entrypoint.dart' show AngularEntrypoint;
|
||||
|
@ -1,5 +1,6 @@
|
||||
library angular2.core;
|
||||
|
||||
export './src/core/angular_entrypoint.dart' show AngularEntrypoint;
|
||||
export './src/core/metadata.dart';
|
||||
export './src/core/util.dart';
|
||||
export 'package:angular2/src/facade/lang.dart' show enableProdMode;
|
||||
|
@ -8,7 +8,7 @@
|
||||
# Modules, barrels and bundles
|
||||
|
||||
Angular2 source code is authored using the ES2015 standardized module format where one module corresponds to exactly one file. Multiple modules (files) can be logically grouped into so-called "barrels".
|
||||
A bundle is a file the contains all the code for one or more barrels.
|
||||
A bundle is a file that contains all the code for one or more barrels.
|
||||
|
||||
Most bundles come in several flavors:
|
||||
* regular and minified (got `.min` in their name);
|
||||
@ -89,4 +89,4 @@ Depending on if you are using Angular bundles or not you can either use RxJS bun
|
||||
|
||||
## ES6 shims (optional)
|
||||
|
||||
Users of pre-ES6 browsers might need to add an ES6 shim (e.g. [es6-shim](https://github.com/paulmillr/es6-shim))
|
||||
Users of pre-ES6 browsers might need to add an ES6 shim (e.g. [es6-shim](https://github.com/paulmillr/es6-shim))
|
||||
|
@ -2,7 +2,7 @@
|
||||
Bootstrapping
|
||||
@cheatsheetIndex 0
|
||||
@description
|
||||
{@target ts}`import {bootstrap} from 'angular2/angular2';`{@endtarget}
|
||||
{@target ts}`import {bootstrap} from 'angular2/platform/browser';`{@endtarget}
|
||||
{@target js}Available from the `ng.platform.browser` namespace.{@endtarget}
|
||||
{@target dart}`import 'package:angular2/bootstrap.dart';`{@endtarget}
|
||||
|
||||
|
@ -62,16 +62,16 @@ Creates a local variable `movieplayer` that provides access to the `video` eleme
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p *my-unless="myExpression">...</p>`|`*my-unless`
|
||||
`<p *myUnless="myExpression">...</p>`|`*myUnless`
|
||||
description:
|
||||
The `*` symbol means that the current element will be turned into an embedded template. Equivalent to:
|
||||
`<template [myless]="myExpression"><p>...</p></template>`
|
||||
`<template [myUnless]="myExpression"><p>...</p></template>`
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
`<p>Card No.: {{cardNumber | myCreditCardNumberFormatter}}</p>`|`{{cardNumber | myCreditCardNumberFormatter}}`
|
||||
description:
|
||||
Transforms the current value of expression `cardNumber` via the pipe called `creditCardNumberFormatter`.
|
||||
Transforms the current value of expression `cardNumber` via the pipe called `myCreditCardNumberFormatter`.
|
||||
|
||||
@cheatsheetItem
|
||||
syntax:
|
||||
|
@ -371,14 +371,14 @@ In TypeScript:
|
||||
import {platform, Provider, APP_INITIALIZER, Injector} from 'angular2/core';
|
||||
import {
|
||||
WORKER_RENDER_PLATFORM,
|
||||
WORKER_RENDER_APP_COMMON,
|
||||
WORKER_RENDER_APPLICATION_COMMON,
|
||||
initializeGenericWorkerRenderer,
|
||||
MessageBus
|
||||
} from 'angular2/platform/worker_render';
|
||||
|
||||
var bus = new MyAwesomeMessageBus();
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APP_COMMON, new Provider(MessageBus, {useValue: bus}),
|
||||
.application([WORKER_RENDER_APPLICATION_COMMON, new Provider(MessageBus, {useValue: bus}),
|
||||
new Provider(APP_INITIALIZER, {
|
||||
useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
|
||||
deps: [Injector],
|
||||
@ -419,7 +419,7 @@ import 'package:angular2/platform/worker_render.dart';
|
||||
main() {
|
||||
var bus = new MyAwesomeMessageBus();
|
||||
platform([WORKER_RENDER_PLATFORM])
|
||||
.application([WORKER_RENDER_APP_COMMON, new Provider(MessageBus, useValue: bus),
|
||||
.application([WORKER_RENDER_APPLICATION_COMMON, new Provider(MessageBus, useValue: bus),
|
||||
new Provider(APP_INITIALIZER,
|
||||
useFactory: (injector) => () => initializeGenericWorkerRenderer(injector),
|
||||
deps: [Injector],
|
||||
@ -456,9 +456,9 @@ void initAppThread(NgZone zone) {
|
||||
*/
|
||||
}
|
||||
```
|
||||
Notice how we use the `WORKER_RENDER_APP_COMMON` providers instead of the `WORKER_RENDER_APP` providers on the render thread.
|
||||
This is because the `WORKER_RENDER_APP` providers include an application initializer that starts a new WebWorker/Isolate.
|
||||
The `WORKER_RENDER_APP_COMMON` providers make no assumption about where your application code lives.
|
||||
Notice how we use the `WORKER_RENDER_APPLICTION_COMMON` providers instead of the `WORKER_RENDER_APPLICATION` providers on the render thread.
|
||||
This is because the `WORKER_RENDER_APPLICATION` providers include an application initializer that starts a new WebWorker/Isolate.
|
||||
The `WORKER_RENDER_APPLICATION_COMMON` providers make no assumption about where your application code lives.
|
||||
However, we now need to provide our own app initializer. At the very least this initializer needs to call `initializeGenericWorkerRenderer`.
|
||||
|
||||
## MessageBroker
|
||||
|
@ -6,8 +6,6 @@
|
||||
|
||||
/// <reference path="../typings/zone/zone.d.ts"/>
|
||||
/// <reference path="../typings/hammerjs/hammerjs.d.ts"/>
|
||||
/// <reference path="../typings/jasmine/jasmine.d.ts"/>
|
||||
/// <reference path="../typings/angular-protractor/angular-protractor.d.ts"/>
|
||||
|
||||
// TODO: ideally the node.d.ts reference should be scoped only for files that need and not to all
|
||||
// the code including client code
|
||||
@ -30,6 +28,7 @@ interface BrowserNodeGlobal {
|
||||
zone: Zone;
|
||||
getAngularTestability: Function;
|
||||
getAllAngularTestabilities: Function;
|
||||
frameworkStabilizers: Array<Function>;
|
||||
setTimeout: Function;
|
||||
clearTimeout: Function;
|
||||
setInterval: Function;
|
||||
|
21
modules/angular2/platform/testing/browser.ts
Normal file
21
modules/angular2/platform/testing/browser.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import {
|
||||
TEST_BROWSER_STATIC_PLATFORM_PROVIDERS,
|
||||
ADDITIONAL_TEST_BROWSER_PROVIDERS
|
||||
} from 'angular2/platform/testing/browser_static';
|
||||
|
||||
import {BROWSER_APP_PROVIDERS} from 'angular2/platform/browser';
|
||||
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
/**
|
||||
* Default patform providers for testing.
|
||||
*/
|
||||
export const TEST_BROWSER_PLATFORM_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([TEST_BROWSER_STATIC_PLATFORM_PROVIDERS]);
|
||||
|
||||
/**
|
||||
* Default application providers for testing.
|
||||
*/
|
||||
export const TEST_BROWSER_APPLICATION_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([BROWSER_APP_PROVIDERS, ADDITIONAL_TEST_BROWSER_PROVIDERS]);
|
69
modules/angular2/platform/testing/browser_static.ts
Normal file
69
modules/angular2/platform/testing/browser_static.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import {
|
||||
APP_ID,
|
||||
DirectiveResolver,
|
||||
NgZone,
|
||||
Provider,
|
||||
ViewResolver,
|
||||
PLATFORM_COMMON_PROVIDERS,
|
||||
PLATFORM_INITIALIZER
|
||||
} from 'angular2/core';
|
||||
import {BROWSER_APP_COMMON_PROVIDERS} from 'angular2/src/platform/browser_common';
|
||||
import {BrowserDomAdapter} from 'angular2/src/platform/browser/browser_adapter';
|
||||
|
||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||
import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
|
||||
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
|
||||
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
||||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||
|
||||
import {XHRImpl} from "angular2/src/platform/browser/xhr_impl";
|
||||
import {XHR} from 'angular2/compiler';
|
||||
|
||||
import {TestComponentBuilder} from 'angular2/src/testing/test_component_builder';
|
||||
|
||||
import {BrowserDetection} from 'angular2/src/testing/utils';
|
||||
|
||||
import {ELEMENT_PROBE_PROVIDERS} from 'angular2/platform/common_dom';
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Log} from 'angular2/src/testing/utils';
|
||||
|
||||
function initBrowserTests() {
|
||||
BrowserDomAdapter.makeCurrent();
|
||||
BrowserDetection.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default patform providers for testing without a compiler.
|
||||
*/
|
||||
export const TEST_BROWSER_STATIC_PLATFORM_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([
|
||||
PLATFORM_COMMON_PROVIDERS,
|
||||
new Provider(PLATFORM_INITIALIZER, {useValue: initBrowserTests, multi: true})
|
||||
]);
|
||||
|
||||
export const ADDITIONAL_TEST_BROWSER_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([
|
||||
new Provider(APP_ID, {useValue: 'a'}),
|
||||
ELEMENT_PROBE_PROVIDERS,
|
||||
new Provider(DirectiveResolver, {useClass: MockDirectiveResolver}),
|
||||
new Provider(ViewResolver, {useClass: MockViewResolver}),
|
||||
Log,
|
||||
TestComponentBuilder,
|
||||
new Provider(NgZone, {useClass: MockNgZone}),
|
||||
new Provider(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
new Provider(AnimationBuilder, {useClass: MockAnimationBuilder}),
|
||||
]);
|
||||
|
||||
/**
|
||||
* Default application providers for testing without a compiler.
|
||||
*/
|
||||
export const TEST_BROWSER_STATIC_APPLICATION_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([
|
||||
BROWSER_APP_COMMON_PROVIDERS,
|
||||
new Provider(XHR, {useClass: XHRImpl}),
|
||||
ADDITIONAL_TEST_BROWSER_PROVIDERS
|
||||
]);
|
1
modules/angular2/platform/testing/server.dart
Normal file
1
modules/angular2/platform/testing/server.dart
Normal file
@ -0,0 +1 @@
|
||||
// Intentionally blank, the Parse5Adapater bindings for JavaScript don't apply.
|
90
modules/angular2/platform/testing/server.ts
Normal file
90
modules/angular2/platform/testing/server.ts
Normal file
@ -0,0 +1,90 @@
|
||||
import {
|
||||
APP_ID,
|
||||
DirectiveResolver,
|
||||
NgZone,
|
||||
Provider,
|
||||
ViewResolver,
|
||||
PLATFORM_COMMON_PROVIDERS,
|
||||
PLATFORM_INITIALIZER,
|
||||
APPLICATION_COMMON_PROVIDERS,
|
||||
Renderer
|
||||
} from 'angular2/core';
|
||||
import {Parse5DomAdapter} from 'angular2/src/platform/server/parse5_adapter';
|
||||
|
||||
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||
import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
|
||||
import {MockDirectiveResolver} from 'angular2/src/mock/directive_resolver_mock';
|
||||
import {MockViewResolver} from 'angular2/src/mock/view_resolver_mock';
|
||||
import {MockLocationStrategy} from 'angular2/src/mock/mock_location_strategy';
|
||||
import {LocationStrategy} from 'angular2/src/router/location_strategy';
|
||||
import {MockNgZone} from 'angular2/src/mock/ng_zone_mock';
|
||||
|
||||
import {TestComponentBuilder} from 'angular2/src/testing/test_component_builder';
|
||||
import {XHR} from 'angular2/src/compiler/xhr';
|
||||
import {BrowserDetection} from 'angular2/src/testing/utils';
|
||||
|
||||
import {COMPILER_PROVIDERS} from 'angular2/src/compiler/compiler';
|
||||
import {DOCUMENT} from 'angular2/src/platform/dom/dom_tokens';
|
||||
import {DOM} from 'angular2/src/platform/dom/dom_adapter';
|
||||
import {RootRenderer} from 'angular2/src/core/render/api';
|
||||
import {DomRootRenderer, DomRootRenderer_} from 'angular2/src/platform/dom/dom_renderer';
|
||||
import {DomSharedStylesHost} from 'angular2/src/platform/dom/shared_styles_host';
|
||||
|
||||
import {
|
||||
EventManager,
|
||||
EVENT_MANAGER_PLUGINS,
|
||||
ELEMENT_PROBE_PROVIDERS
|
||||
} from 'angular2/platform/common_dom';
|
||||
import {DomEventsPlugin} from 'angular2/src/platform/dom/events/dom_events';
|
||||
|
||||
import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
|
||||
import {Log} from 'angular2/src/testing/utils';
|
||||
|
||||
function initServerTests() {
|
||||
Parse5DomAdapter.makeCurrent();
|
||||
BrowserDetection.setup();
|
||||
}
|
||||
|
||||
/**
|
||||
* Default patform providers for testing.
|
||||
*/
|
||||
export const TEST_SERVER_PLATFORM_PROVIDERS: Array<any /*Type | Provider | any[]*/> = CONST_EXPR([
|
||||
PLATFORM_COMMON_PROVIDERS,
|
||||
new Provider(PLATFORM_INITIALIZER, {useValue: initServerTests, multi: true})
|
||||
]);
|
||||
|
||||
function appDoc() {
|
||||
try {
|
||||
return DOM.defaultDoc();
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default application providers for testing.
|
||||
*/
|
||||
export const TEST_SERVER_APPLICATION_PROVIDERS: Array<any /*Type | Provider | any[]*/> =
|
||||
CONST_EXPR([
|
||||
// TODO(julie): when angular2/platform/server is available, use that instead of making our own
|
||||
// list here.
|
||||
APPLICATION_COMMON_PROVIDERS,
|
||||
COMPILER_PROVIDERS,
|
||||
new Provider(DOCUMENT, {useFactory: appDoc}),
|
||||
new Provider(DomRootRenderer, {useClass: DomRootRenderer_}),
|
||||
new Provider(RootRenderer, {useExisting: DomRootRenderer}),
|
||||
EventManager,
|
||||
new Provider(EVENT_MANAGER_PLUGINS, {useClass: DomEventsPlugin, multi: true}),
|
||||
new Provider(XHR, {useClass: XHR}),
|
||||
new Provider(APP_ID, {useValue: 'a'}),
|
||||
DomSharedStylesHost,
|
||||
ELEMENT_PROBE_PROVIDERS,
|
||||
new Provider(DirectiveResolver, {useClass: MockDirectiveResolver}),
|
||||
new Provider(ViewResolver, {useClass: MockViewResolver}),
|
||||
Log,
|
||||
TestComponentBuilder,
|
||||
new Provider(NgZone, {useClass: MockNgZone}),
|
||||
new Provider(LocationStrategy, {useClass: MockLocationStrategy}),
|
||||
new Provider(AnimationBuilder, {useClass: MockAnimationBuilder}),
|
||||
]);
|
@ -4,11 +4,11 @@ export 'package:angular2/src/platform/worker_render_common.dart'
|
||||
show
|
||||
WORKER_SCRIPT,
|
||||
WORKER_RENDER_PLATFORM,
|
||||
WORKER_RENDER_APP_COMMON,
|
||||
WORKER_RENDER_APPLICATION_COMMON,
|
||||
initializeGenericWorkerRenderer;
|
||||
|
||||
export 'package:angular2/src/platform/worker_render.dart'
|
||||
show WORKER_RENDER_APP, initIsolate, WebWorkerInstance;
|
||||
show WORKER_RENDER_APPLICATION, initIsolate, WebWorkerInstance;
|
||||
|
||||
export '../src/web_workers/shared/client_message_broker.dart'
|
||||
show ClientMessageBroker, ClientMessageBrokerFactory, FnArg, UiArguments;
|
||||
@ -18,3 +18,7 @@ export '../src/web_workers/shared/service_message_broker.dart'
|
||||
|
||||
export '../src/web_workers/shared/serializer.dart' show PRIMITIVE;
|
||||
export '../src/web_workers/shared/message_bus.dart';
|
||||
|
||||
import 'package:angular2/src/platform/worker_render_common.dart';
|
||||
|
||||
const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION_COMMON;
|
||||
|
@ -2,9 +2,9 @@ export {
|
||||
WORKER_SCRIPT,
|
||||
WORKER_RENDER_PLATFORM,
|
||||
initializeGenericWorkerRenderer,
|
||||
WORKER_RENDER_APP_COMMON
|
||||
WORKER_RENDER_APPLICATION_COMMON
|
||||
} from 'angular2/src/platform/worker_render_common';
|
||||
export * from 'angular2/src/platform/worker_render';
|
||||
export {WORKER_RENDER_APPLICATION, WebWorkerInstance} from 'angular2/src/platform/worker_render';
|
||||
export {
|
||||
ClientMessageBroker,
|
||||
ClientMessageBrokerFactory,
|
||||
@ -18,3 +18,9 @@ export {
|
||||
} from '../src/web_workers/shared/service_message_broker';
|
||||
export {PRIMITIVE} from '../src/web_workers/shared/serializer';
|
||||
export * from '../src/web_workers/shared/message_bus';
|
||||
import {WORKER_RENDER_APPLICATION} from 'angular2/src/platform/worker_render';
|
||||
|
||||
/**
|
||||
* @deprecated Use WORKER_RENDER_APPLICATION
|
||||
*/
|
||||
export const WORKER_RENDER_APP = WORKER_RENDER_APPLICATION;
|
||||
|
@ -9,9 +9,8 @@ homepage: <%= packageJson.homepage %>
|
||||
environment:
|
||||
sdk: '>=1.10.0 <2.0.0'
|
||||
dependencies:
|
||||
analyzer: '>=0.24.4 <0.27.0'
|
||||
analyzer: '>=0.24.4 <0.28.0'
|
||||
barback: '^0.15.2+2'
|
||||
code_transformers: '0.2.9+4'
|
||||
dart_style: '>=0.1.8 <0.3.0'
|
||||
glob: '^1.0.0'
|
||||
html: '^0.12.0'
|
||||
@ -23,7 +22,9 @@ dependencies:
|
||||
source_span: '^1.0.0'
|
||||
stack_trace: '^1.1.1'
|
||||
dev_dependencies:
|
||||
code_transformers: '>=0.2.9+4 <0.4.0'
|
||||
guinness: '^0.1.18'
|
||||
test: '^0.12.6'
|
||||
transformers:
|
||||
- angular2
|
||||
- $dart2js:
|
||||
|
@ -169,10 +169,10 @@ export class NgClass implements DoCheck, OnDestroy {
|
||||
if (className.indexOf(' ') > -1) {
|
||||
var classes = className.split(/\s+/g);
|
||||
for (var i = 0, len = classes.length; i < len; i++) {
|
||||
this._renderer.setElementClass(this._ngEl, classes[i], enabled);
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, classes[i], enabled);
|
||||
}
|
||||
} else {
|
||||
this._renderer.setElementClass(this._ngEl, className, enabled);
|
||||
this._renderer.setElementClass(this._ngEl.nativeElement, className, enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ import {
|
||||
IterableDiffers,
|
||||
ViewContainerRef,
|
||||
TemplateRef,
|
||||
ViewRef
|
||||
EmbeddedViewRef
|
||||
} from 'angular2/core';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
@ -110,7 +110,8 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
||||
this._viewContainer.get(i).setLocal('last', i === ilen - 1);
|
||||
var viewRef = <EmbeddedViewRef>this._viewContainer.get(i);
|
||||
viewRef.setLocal('last', i === ilen - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +154,7 @@ export class NgFor implements DoCheck {
|
||||
}
|
||||
|
||||
class RecordViewTuple {
|
||||
view: ViewRef;
|
||||
view: EmbeddedViewRef;
|
||||
record: any;
|
||||
constructor(record, view) {
|
||||
this.record = record;
|
||||
|
@ -92,6 +92,6 @@ export class NgStyle implements DoCheck {
|
||||
}
|
||||
|
||||
private _setStyle(name: string, val: string): void {
|
||||
this._renderer.setElementStyle(this._ngEl, name, val);
|
||||
this._renderer.setElementStyle(this._ngEl.nativeElement, name, val);
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ import {ListWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
const _WHEN_DEFAULT = CONST_EXPR(new Object());
|
||||
|
||||
class SwitchView {
|
||||
/** @internal */
|
||||
export class SwitchView {
|
||||
constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
|
||||
|
||||
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
|
||||
|
@ -21,7 +21,7 @@ class ObservableListDiff extends DefaultIterableDiffer {
|
||||
}
|
||||
}
|
||||
|
||||
dynamic diff(ObservableList collection) {
|
||||
DefaultIterableDiffer diff(ObservableList collection) {
|
||||
if (collection is! ObservableList) {
|
||||
throw "Cannot change the type of a collection";
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ export class CheckboxControlValueAccessor implements ControlValueAccessor {
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: any): void {
|
||||
this._renderer.setElementProperty(this._elementRef, 'checked', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'checked', value);
|
||||
}
|
||||
registerOnChange(fn: (_: any) => {}): void { this.onChange = fn; }
|
||||
registerOnTouched(fn: () => {}): void { this.onTouched = fn; }
|
||||
|
@ -31,7 +31,7 @@ export class DefaultValueAccessor implements ControlValueAccessor {
|
||||
|
||||
writeValue(value: any): void {
|
||||
var normalizedValue = isBlank(value) ? '' : value;
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', normalizedValue);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', normalizedValue);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: any) => void): void { this.onChange = fn; }
|
||||
|
@ -31,7 +31,7 @@ export class NumberValueAccessor implements ControlValueAccessor {
|
||||
constructor(private _renderer: Renderer, private _elementRef: ElementRef) {}
|
||||
|
||||
writeValue(value: number): void {
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: (_: number) => void): void {
|
||||
|
@ -36,11 +36,7 @@ export class NgSelectOption {
|
||||
*/
|
||||
@Directive({
|
||||
selector: 'select[ngControl],select[ngFormControl],select[ngModel]',
|
||||
host: {
|
||||
'(change)': 'onChange($event.target.value)',
|
||||
'(input)': 'onChange($event.target.value)',
|
||||
'(blur)': 'onTouched()'
|
||||
},
|
||||
host: {'(input)': 'onChange($event.target.value)', '(blur)': 'onTouched()'},
|
||||
bindings: [SELECT_VALUE_ACCESSOR]
|
||||
})
|
||||
export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
@ -55,7 +51,7 @@ export class SelectControlValueAccessor implements ControlValueAccessor {
|
||||
|
||||
writeValue(value: any): void {
|
||||
this.value = value;
|
||||
this._renderer.setElementProperty(this._elementRef, 'value', value);
|
||||
this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
|
||||
}
|
||||
|
||||
registerOnChange(fn: () => any): void { this.onChange = fn; }
|
||||
|
@ -80,7 +80,6 @@ export function selectValueAccessor(dir: NgControl,
|
||||
var defaultAccessor;
|
||||
var builtinAccessor;
|
||||
var customAccessor;
|
||||
|
||||
valueAccessors.forEach(v => {
|
||||
if (v instanceof DefaultValueAccessor) {
|
||||
defaultAccessor = v;
|
||||
|
@ -14,7 +14,7 @@ export const VALID = "VALID";
|
||||
export const INVALID = "INVALID";
|
||||
|
||||
/**
|
||||
* Indicates that a Control is pending, i.e. that async validation is occuring and
|
||||
* Indicates that a Control is pending, i.e. that async validation is occurring and
|
||||
* errors are not yet available for the input value.
|
||||
*/
|
||||
export const PENDING = "PENDING";
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
|
||||
@ -43,7 +43,7 @@ export function createChangeDetectorDefinitions(
|
||||
|
||||
class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
viewIndex: number;
|
||||
boundTextCount: number = 0;
|
||||
nodeCount: number = 0;
|
||||
boundElementCount: number = 0;
|
||||
variableNames: string[] = [];
|
||||
bindingRecords: BindingRecord[] = [];
|
||||
@ -57,6 +57,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
this.nodeCount++;
|
||||
this.boundElementCount++;
|
||||
templateVisitAll(this, ast.outputs);
|
||||
for (var i = 0; i < ast.directives.length; i++) {
|
||||
@ -73,6 +74,7 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
this.nodeCount++;
|
||||
if (ast.isBound()) {
|
||||
this.boundElementCount++;
|
||||
}
|
||||
@ -132,14 +134,20 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
visitAttr(ast: AttrAst, context: any): any { return null; }
|
||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
||||
var boundTextIndex = this.boundTextCount++;
|
||||
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, boundTextIndex));
|
||||
var nodeIndex = this.nodeCount++;
|
||||
this.bindingRecords.push(BindingRecord.createForTextNode(ast.value, nodeIndex));
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, context: any): any {
|
||||
this.nodeCount++;
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, context: any): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, directiveIndexAsNumber: number): any {
|
||||
var directiveIndex = new DirectiveIndex(this.boundElementCount - 1, directiveIndexAsNumber);
|
||||
var directiveMetadata = ast.directive;
|
||||
var outputsArray = [];
|
||||
StringMapWrapper.forEach(ast.directive.outputs, (eventName, dirProperty) => outputsArray.push(
|
||||
[dirProperty, eventName]));
|
||||
var directiveRecord = new DirectiveRecord({
|
||||
directiveIndex: directiveIndex,
|
||||
callAfterContentInit:
|
||||
@ -153,7 +161,9 @@ class ProtoViewVisitor implements TemplateAstVisitor {
|
||||
callOnChanges: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
|
||||
callDoCheck: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1,
|
||||
callOnInit: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
|
||||
changeDetection: directiveMetadata.changeDetection
|
||||
callOnDestroy: directiveMetadata.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1,
|
||||
changeDetection: directiveMetadata.changeDetection,
|
||||
outputs: outputsArray
|
||||
});
|
||||
this.directiveRecords.push(directiveRecord);
|
||||
|
||||
|
@ -3,6 +3,9 @@ import {SourceExpressions, moduleRef} from './source_module';
|
||||
import {
|
||||
ChangeDetectorJITGenerator
|
||||
} from 'angular2/src/core/change_detection/change_detection_jit_generator';
|
||||
import {AbstractChangeDetector} from 'angular2/src/core/change_detection/abstract_change_detector';
|
||||
import {ChangeDetectionUtil} from 'angular2/src/core/change_detection/change_detection_util';
|
||||
import {ChangeDetectorState} from 'angular2/src/core/change_detection/constants';
|
||||
|
||||
import {createChangeDetectorDefinitions} from './change_definition_factory';
|
||||
import {IS_DART, isJsObject, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
@ -23,6 +26,12 @@ const ABSTRACT_CHANGE_DETECTOR = "AbstractChangeDetector";
|
||||
const UTIL = "ChangeDetectionUtil";
|
||||
const CHANGE_DETECTOR_STATE = "ChangeDetectorState";
|
||||
|
||||
export const CHANGE_DETECTION_JIT_IMPORTS = CONST_EXPR({
|
||||
'AbstractChangeDetector': AbstractChangeDetector,
|
||||
'ChangeDetectionUtil': ChangeDetectionUtil,
|
||||
'ChangeDetectorState': ChangeDetectorState
|
||||
});
|
||||
|
||||
var ABSTRACT_CHANGE_DETECTOR_MODULE = moduleRef(
|
||||
`package:angular2/src/core/change_detection/abstract_change_detector${MODULE_SUFFIX}`);
|
||||
var UTIL_MODULE =
|
||||
@ -45,14 +54,8 @@ export class ChangeDetectionCompiler {
|
||||
}
|
||||
|
||||
private _createChangeDetectorFactory(definition: ChangeDetectorDefinition): Function {
|
||||
if (IS_DART || !this._genConfig.useJit) {
|
||||
var proto = new DynamicProtoChangeDetector(definition);
|
||||
return (dispatcher) => proto.instantiate(dispatcher);
|
||||
} else {
|
||||
return new ChangeDetectorJITGenerator(definition, UTIL, ABSTRACT_CHANGE_DETECTOR,
|
||||
CHANGE_DETECTOR_STATE)
|
||||
.generate();
|
||||
}
|
||||
var proto = new DynamicProtoChangeDetector(definition);
|
||||
return () => proto.instantiate();
|
||||
}
|
||||
|
||||
compileComponentCodeGen(componentType: CompileTypeMetadata, strategy: ChangeDetectionStrategy,
|
||||
@ -81,7 +84,7 @@ export class ChangeDetectionCompiler {
|
||||
definition, `${UTIL_MODULE}${UTIL}`,
|
||||
`${ABSTRACT_CHANGE_DETECTOR_MODULE}${ABSTRACT_CHANGE_DETECTOR}`,
|
||||
`${CONSTANTS_MODULE}${CHANGE_DETECTOR_STATE}`);
|
||||
factories.push(`function(dispatcher) { return new ${codegen.typeName}(dispatcher); }`);
|
||||
factories.push(`function() { return new ${codegen.typeName}(); }`);
|
||||
sourcePart = codegen.generateSource();
|
||||
}
|
||||
index++;
|
||||
|
@ -1,375 +0,0 @@
|
||||
import {isPresent, isBlank, Type, isString, StringWrapper, IS_DART} from 'angular2/src/facade/lang';
|
||||
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
TemplateCmd,
|
||||
TextCmd,
|
||||
NgContentCmd,
|
||||
BeginElementCmd,
|
||||
EndElementCmd,
|
||||
BeginComponentCmd,
|
||||
EndComponentCmd,
|
||||
EmbeddedTemplateCmd,
|
||||
CompiledComponentTemplate
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
import {
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
BoundTextAst,
|
||||
TextAst,
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll
|
||||
} from './template_ast';
|
||||
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
||||
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
codeGenConstConstructorCall,
|
||||
codeGenValueFn,
|
||||
MODULE_SUFFIX
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
export var TEMPLATE_COMMANDS_MODULE_REF =
|
||||
moduleRef(`package:angular2/src/core/linker/template_commands${MODULE_SUFFIX}`);
|
||||
|
||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||
const CLASS_ATTR = 'class';
|
||||
const STYLE_ATTR = 'style';
|
||||
|
||||
@Injectable()
|
||||
export class CommandCompiler {
|
||||
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
changeDetectorFactories: Function[],
|
||||
componentTemplateFactory: Function): TemplateCmd[] {
|
||||
var visitor = new CommandBuilderVisitor(
|
||||
new RuntimeCommandFactory(component, componentTemplateFactory, changeDetectorFactories), 0);
|
||||
templateVisitAll(visitor, template);
|
||||
return visitor.result;
|
||||
}
|
||||
|
||||
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
changeDetectorFactoryExpressions: string[],
|
||||
componentTemplateFactory: Function): SourceExpression {
|
||||
var visitor =
|
||||
new CommandBuilderVisitor(new CodegenCommandFactory(component, componentTemplateFactory,
|
||||
changeDetectorFactoryExpressions),
|
||||
0);
|
||||
templateVisitAll(visitor, template);
|
||||
return new SourceExpression([], codeGenArray(visitor.result));
|
||||
}
|
||||
}
|
||||
|
||||
interface CommandFactory<R> {
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): R;
|
||||
createNgContent(index: number, ngContentIndex: number): R;
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): R;
|
||||
createEndElement(): R;
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
encapsulation: ViewEncapsulation, ngContentIndex: number): R;
|
||||
createEndComponent(): R;
|
||||
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isMerged: boolean, ngContentIndex: number, children: R[]): R;
|
||||
}
|
||||
|
||||
class RuntimeCommandFactory implements CommandFactory<TemplateCmd> {
|
||||
constructor(private component: CompileDirectiveMetadata,
|
||||
private componentTemplateFactory: Function,
|
||||
private changeDetectorFactories: Function[]) {}
|
||||
private _mapDirectives(directives: CompileDirectiveMetadata[]): Type[] {
|
||||
return directives.map(directive => directive.type.runtime);
|
||||
}
|
||||
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||
return new TextCmd(value, isBound, ngContentIndex);
|
||||
}
|
||||
createNgContent(index: number, ngContentIndex: number): TemplateCmd {
|
||||
return new NgContentCmd(index, ngContentIndex);
|
||||
}
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): TemplateCmd {
|
||||
return new BeginElementCmd(name, attrNameAndValues, eventTargetAndNames, variableNameAndValues,
|
||||
this._mapDirectives(directives), isBound, ngContentIndex);
|
||||
}
|
||||
createEndElement(): TemplateCmd { return new EndElementCmd(); }
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
encapsulation: ViewEncapsulation, ngContentIndex: number): TemplateCmd {
|
||||
var nestedTemplateAccessor = this.componentTemplateFactory(directives[0]);
|
||||
return new BeginComponentCmd(name, attrNameAndValues, eventTargetAndNames,
|
||||
variableNameAndValues, this._mapDirectives(directives),
|
||||
encapsulation, ngContentIndex, nestedTemplateAccessor);
|
||||
}
|
||||
createEndComponent(): TemplateCmd { return new EndComponentCmd(); }
|
||||
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isMerged: boolean, ngContentIndex: number,
|
||||
children: TemplateCmd[]): TemplateCmd {
|
||||
return new EmbeddedTemplateCmd(attrNameAndValues, variableNameAndValues,
|
||||
this._mapDirectives(directives), isMerged, ngContentIndex,
|
||||
this.changeDetectorFactories[embeddedTemplateIndex], children);
|
||||
}
|
||||
}
|
||||
|
||||
class CodegenCommandFactory implements CommandFactory<Expression> {
|
||||
constructor(private component: CompileDirectiveMetadata,
|
||||
private componentTemplateFactory: Function,
|
||||
private changeDetectorFactoryExpressions: string[]) {}
|
||||
|
||||
createText(value: string, isBound: boolean, ngContentIndex: number): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'TextCmd')}(${escapeSingleQuoteString(value)}, ${isBound}, ${ngContentIndex})`);
|
||||
}
|
||||
createNgContent(index: number, ngContentIndex: number): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'NgContentCmd')}(${index}, ${ngContentIndex})`);
|
||||
}
|
||||
createBeginElement(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isBound: boolean, ngContentIndex: number): Expression {
|
||||
var attrsExpression = codeGenArray(attrNameAndValues);
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginElementCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
|
||||
`${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${isBound}, ${ngContentIndex})`);
|
||||
}
|
||||
createEndElement(): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndElementCmd')}()`);
|
||||
}
|
||||
createBeginComponent(name: string, attrNameAndValues: string[], eventTargetAndNames: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
encapsulation: ViewEncapsulation, ngContentIndex: number): Expression {
|
||||
var attrsExpression = codeGenArray(attrNameAndValues);
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'BeginComponentCmd')}(${escapeSingleQuoteString(name)}, ${attrsExpression}, ` +
|
||||
`${codeGenArray(eventTargetAndNames)}, ${codeGenArray(variableNameAndValues)}, ${codeGenDirectivesArray(directives)}, ${codeGenViewEncapsulation(encapsulation)}, ${ngContentIndex}, ${this.componentTemplateFactory(directives[0])})`);
|
||||
}
|
||||
createEndComponent(): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EndComponentCmd')}()`);
|
||||
}
|
||||
createEmbeddedTemplate(embeddedTemplateIndex: number, attrNameAndValues: string[],
|
||||
variableNameAndValues: string[], directives: CompileDirectiveMetadata[],
|
||||
isMerged: boolean, ngContentIndex: number,
|
||||
children: Expression[]): Expression {
|
||||
return new Expression(
|
||||
`${codeGenConstConstructorCall(TEMPLATE_COMMANDS_MODULE_REF+'EmbeddedTemplateCmd')}(${codeGenArray(attrNameAndValues)}, ${codeGenArray(variableNameAndValues)}, ` +
|
||||
`${codeGenDirectivesArray(directives)}, ${isMerged}, ${ngContentIndex}, ${this.changeDetectorFactoryExpressions[embeddedTemplateIndex]}, ${codeGenArray(children)})`);
|
||||
}
|
||||
}
|
||||
|
||||
function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
|
||||
context: any): any {
|
||||
templateVisitAll(visitor, asts, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
class CommandBuilderVisitor<R> implements TemplateAstVisitor {
|
||||
result: R[] = [];
|
||||
transitiveNgContentCount: number = 0;
|
||||
constructor(public commandFactory: CommandFactory<R>, public embeddedTemplateIndex: number) {}
|
||||
|
||||
private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
|
||||
attrAsts: TemplateAst[]): string[] {
|
||||
var attrs = keyValueArrayToMap(visitAndReturnContext(this, attrAsts, []));
|
||||
directives.forEach(directiveMeta => {
|
||||
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||
var prevValue = attrs[name];
|
||||
attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||
});
|
||||
});
|
||||
return mapToKeyValueArray(attrs);
|
||||
}
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any {
|
||||
this.transitiveNgContentCount++;
|
||||
this.result.push(this.commandFactory.createNgContent(ast.index, ast.ngContentIndex));
|
||||
return null;
|
||||
}
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
this.embeddedTemplateIndex++;
|
||||
var childVisitor = new CommandBuilderVisitor(this.commandFactory, this.embeddedTemplateIndex);
|
||||
templateVisitAll(childVisitor, ast.children);
|
||||
var isMerged = childVisitor.transitiveNgContentCount > 0;
|
||||
var variableNameAndValues = [];
|
||||
ast.vars.forEach((varAst) => {
|
||||
variableNameAndValues.push(varAst.name);
|
||||
variableNameAndValues.push(varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR);
|
||||
});
|
||||
var directives = [];
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||
directiveAst.visit(this, new DirectiveContext(index, [], [], directives));
|
||||
});
|
||||
this.result.push(this.commandFactory.createEmbeddedTemplate(
|
||||
this.embeddedTemplateIndex, this._readAttrNameAndValues(directives, ast.attrs),
|
||||
variableNameAndValues, directives, isMerged, ast.ngContentIndex, childVisitor.result));
|
||||
this.transitiveNgContentCount += childVisitor.transitiveNgContentCount;
|
||||
this.embeddedTemplateIndex = childVisitor.embeddedTemplateIndex;
|
||||
return null;
|
||||
}
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
var component = ast.getComponent();
|
||||
var eventTargetAndNames = visitAndReturnContext(this, ast.outputs, []);
|
||||
var variableNameAndValues = [];
|
||||
if (isBlank(component)) {
|
||||
ast.exportAsVars.forEach((varAst) => {
|
||||
variableNameAndValues.push(varAst.name);
|
||||
variableNameAndValues.push(null);
|
||||
});
|
||||
}
|
||||
var directives = [];
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||
directiveAst.visit(this, new DirectiveContext(index, eventTargetAndNames,
|
||||
variableNameAndValues, directives));
|
||||
});
|
||||
eventTargetAndNames = removeKeyValueArrayDuplicates(eventTargetAndNames);
|
||||
|
||||
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
||||
if (isPresent(component)) {
|
||||
this.result.push(this.commandFactory.createBeginComponent(
|
||||
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
|
||||
component.template.encapsulation, ast.ngContentIndex));
|
||||
templateVisitAll(this, ast.children);
|
||||
this.result.push(this.commandFactory.createEndComponent());
|
||||
} else {
|
||||
this.result.push(this.commandFactory.createBeginElement(
|
||||
ast.name, attrNameAndValues, eventTargetAndNames, variableNameAndValues, directives,
|
||||
ast.isBound(), ast.ngContentIndex));
|
||||
templateVisitAll(this, ast.children);
|
||||
this.result.push(this.commandFactory.createEndElement());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitAttr(ast: AttrAst, attrNameAndValues: string[]): any {
|
||||
attrNameAndValues.push(ast.name);
|
||||
attrNameAndValues.push(ast.value);
|
||||
return null;
|
||||
}
|
||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
||||
this.result.push(this.commandFactory.createText(null, true, ast.ngContentIndex));
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, context: any): any {
|
||||
this.result.push(this.commandFactory.createText(ast.value, false, ast.ngContentIndex));
|
||||
return null;
|
||||
}
|
||||
visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
|
||||
ctx.targetDirectives.push(ast.directive);
|
||||
templateVisitAll(this, ast.hostEvents, ctx.eventTargetAndNames);
|
||||
ast.exportAsVars.forEach(varAst => {
|
||||
ctx.targetVariableNameAndValues.push(varAst.name);
|
||||
ctx.targetVariableNameAndValues.push(ctx.index);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: string[]): any {
|
||||
eventTargetAndNames.push(ast.target);
|
||||
eventTargetAndNames.push(ast.name);
|
||||
return null;
|
||||
}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
function removeKeyValueArrayDuplicates(keyValueArray: string[]): string[] {
|
||||
var knownPairs = new Set();
|
||||
var resultKeyValueArray = [];
|
||||
for (var i = 0; i < keyValueArray.length; i += 2) {
|
||||
var key = keyValueArray[i];
|
||||
var value = keyValueArray[i + 1];
|
||||
var pairId = `${key}:${value}`;
|
||||
if (!SetWrapper.has(knownPairs, pairId)) {
|
||||
resultKeyValueArray.push(key);
|
||||
resultKeyValueArray.push(value);
|
||||
knownPairs.add(pairId);
|
||||
}
|
||||
}
|
||||
return resultKeyValueArray;
|
||||
}
|
||||
|
||||
function keyValueArrayToMap(keyValueArr: string[]): {[key: string]: string} {
|
||||
var data: {[key: string]: string} = {};
|
||||
for (var i = 0; i < keyValueArr.length; i += 2) {
|
||||
data[keyValueArr[i]] = keyValueArr[i + 1];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function mapToKeyValueArray(data: {[key: string]: string}): string[] {
|
||||
var entryArray = [];
|
||||
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
|
||||
// We need to sort to get a defined output order
|
||||
// for tests and for caching generated artifacts...
|
||||
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
||||
var keyValueArray = [];
|
||||
entryArray.forEach((entry) => {
|
||||
keyValueArray.push(entry[0]);
|
||||
keyValueArray.push(entry[1]);
|
||||
});
|
||||
return keyValueArray;
|
||||
}
|
||||
|
||||
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||
return `${attrValue1} ${attrValue2}`;
|
||||
} else {
|
||||
return attrValue2;
|
||||
}
|
||||
}
|
||||
|
||||
class DirectiveContext {
|
||||
constructor(public index: number, public eventTargetAndNames: string[],
|
||||
public targetVariableNameAndValues: any[],
|
||||
public targetDirectives: CompileDirectiveMetadata[]) {}
|
||||
}
|
||||
|
||||
class Expression {
|
||||
constructor(public value: string) {}
|
||||
}
|
||||
|
||||
function escapeValue(value: any): string {
|
||||
if (value instanceof Expression) {
|
||||
return value.value;
|
||||
} else if (isString(value)) {
|
||||
return escapeSingleQuoteString(value);
|
||||
} else if (isBlank(value)) {
|
||||
return 'null';
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
function codeGenArray(data: any[]): string {
|
||||
var base = `[${data.map(escapeValue).join(',')}]`;
|
||||
return IS_DART ? `const ${base}` : base;
|
||||
}
|
||||
|
||||
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
|
||||
var expressions = directives.map(
|
||||
directiveType => `${moduleRef(directiveType.type.moduleUrl)}${directiveType.type.name}`);
|
||||
var base = `[${expressions.join(',')}]`;
|
||||
return IS_DART ? `const ${base}` : base;
|
||||
}
|
||||
|
||||
function codeGenViewEncapsulation(value: ViewEncapsulation): string {
|
||||
if (IS_DART) {
|
||||
return `${TEMPLATE_COMMANDS_MODULE_REF}${value}`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
@ -17,7 +17,8 @@ import {TemplateNormalizer} from 'angular2/src/compiler/template_normalizer';
|
||||
import {RuntimeMetadataResolver} from 'angular2/src/compiler/runtime_metadata';
|
||||
import {ChangeDetectionCompiler} from 'angular2/src/compiler/change_detector_compiler';
|
||||
import {StyleCompiler} from 'angular2/src/compiler/style_compiler';
|
||||
import {CommandCompiler} from 'angular2/src/compiler/command_compiler';
|
||||
import {ViewCompiler} from 'angular2/src/compiler/view_compiler';
|
||||
import {ProtoViewCompiler} from 'angular2/src/compiler/proto_view_compiler';
|
||||
import {TemplateCompiler} from 'angular2/src/compiler/template_compiler';
|
||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {Compiler} from 'angular2/src/core/linker/compiler';
|
||||
@ -44,7 +45,8 @@ export const COMPILER_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||
RuntimeMetadataResolver,
|
||||
DEFAULT_PACKAGE_URL_PROVIDER,
|
||||
StyleCompiler,
|
||||
CommandCompiler,
|
||||
ProtoViewCompiler,
|
||||
ViewCompiler,
|
||||
ChangeDetectionCompiler,
|
||||
new Provider(ChangeDetectorGenConfig, {useFactory: _createChangeDetectorGenConfig, deps: []}),
|
||||
TemplateCompiler,
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
RegExpWrapper,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
@ -21,6 +22,16 @@ import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/i
|
||||
// group 2: "event" from "(event)"
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
|
||||
export abstract class CompileMetadataWithType {
|
||||
static fromJson(data: {[key: string]: any}): CompileMetadataWithType {
|
||||
return _COMPILE_METADATA_FROM_JSON[data['class']](data);
|
||||
}
|
||||
|
||||
abstract toJson(): {[key: string]: any};
|
||||
|
||||
get type(): CompileTypeMetadata { return unimplemented(); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Metadata regarding compilation of a type.
|
||||
*/
|
||||
@ -107,7 +118,7 @@ export class CompileTemplateMetadata {
|
||||
/**
|
||||
* Metadata regarding compilation of a directive.
|
||||
*/
|
||||
export class CompileDirectiveMetadata {
|
||||
export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
static create({type, isComponent, dynamicLoadable, selector, exportAs, changeDetection, inputs,
|
||||
outputs, host, lifecycleHooks, template}: {
|
||||
type?: CompileTypeMetadata,
|
||||
@ -241,6 +252,7 @@ export class CompileDirectiveMetadata {
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'Directive',
|
||||
'isComponent': this.isComponent,
|
||||
'dynamicLoadable': this.dynamicLoadable,
|
||||
'selector': this.selector,
|
||||
@ -284,3 +296,38 @@ export function createHostComponentMeta(componentType: CompileTypeMetadata,
|
||||
selector: '*'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
export class CompilePipeMetadata implements CompileMetadataWithType {
|
||||
type: CompileTypeMetadata;
|
||||
name: string;
|
||||
pure: boolean;
|
||||
constructor({type, name,
|
||||
pure}: {type?: CompileTypeMetadata, name?: string, pure?: boolean} = {}) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.pure = normalizeBool(pure);
|
||||
}
|
||||
|
||||
static fromJson(data: {[key: string]: any}): CompilePipeMetadata {
|
||||
return new CompilePipeMetadata({
|
||||
type: isPresent(data['type']) ? CompileTypeMetadata.fromJson(data['type']) : data['type'],
|
||||
name: data['name'],
|
||||
pure: data['pure']
|
||||
});
|
||||
}
|
||||
|
||||
toJson(): {[key: string]: any} {
|
||||
return {
|
||||
'class': 'Pipe',
|
||||
'type': isPresent(this.type) ? this.type.toJson() : null,
|
||||
'name': this.name,
|
||||
'pure': this.pure
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var _COMPILE_METADATA_FROM_JSON = {
|
||||
'Directive': CompileDirectiveMetadata.fromJson,
|
||||
'Pipe': CompilePipeMetadata.fromJson
|
||||
};
|
||||
|
@ -73,10 +73,13 @@ const $LT = 60;
|
||||
const $EQ = 61;
|
||||
const $GT = 62;
|
||||
const $QUESTION = 63;
|
||||
const $A = 65;
|
||||
const $Z = 90;
|
||||
const $LBRACKET = 91;
|
||||
const $RBRACKET = 93;
|
||||
const $A = 65;
|
||||
const $F = 70;
|
||||
const $X = 88;
|
||||
const $Z = 90;
|
||||
|
||||
const $a = 97;
|
||||
const $f = 102;
|
||||
const $z = 122;
|
||||
@ -102,7 +105,6 @@ class ControlFlowError {
|
||||
// See http://www.w3.org/TR/html51/syntax.html#writing
|
||||
class _HtmlTokenizer {
|
||||
private input: string;
|
||||
private inputLowercase: string;
|
||||
private length: number;
|
||||
// Note: this is always lowercase!
|
||||
private peek: number = -1;
|
||||
@ -117,7 +119,6 @@ class _HtmlTokenizer {
|
||||
|
||||
constructor(private file: ParseSourceFile) {
|
||||
this.input = file.content;
|
||||
this.inputLowercase = file.content.toLowerCase();
|
||||
this.length = file.content.length;
|
||||
this._advance();
|
||||
}
|
||||
@ -133,16 +134,16 @@ class _HtmlTokenizer {
|
||||
while (this.peek !== $EOF) {
|
||||
var start = this._getLocation();
|
||||
try {
|
||||
if (this._attemptChar($LT)) {
|
||||
if (this._attemptChar($BANG)) {
|
||||
if (this._attemptChar($LBRACKET)) {
|
||||
if (this._attemptCharCode($LT)) {
|
||||
if (this._attemptCharCode($BANG)) {
|
||||
if (this._attemptCharCode($LBRACKET)) {
|
||||
this._consumeCdata(start);
|
||||
} else if (this._attemptChar($MINUS)) {
|
||||
} else if (this._attemptCharCode($MINUS)) {
|
||||
this._consumeComment(start);
|
||||
} else {
|
||||
this._consumeDocType(start);
|
||||
}
|
||||
} else if (this._attemptChar($SLASH)) {
|
||||
} else if (this._attemptCharCode($SLASH)) {
|
||||
this._consumeTagClose(start);
|
||||
} else {
|
||||
this._consumeTagOpen(start);
|
||||
@ -205,11 +206,10 @@ class _HtmlTokenizer {
|
||||
this.column++;
|
||||
}
|
||||
this.index++;
|
||||
this.peek = this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.inputLowercase,
|
||||
this.index);
|
||||
this.peek = this.index >= this.length ? $EOF : StringWrapper.charCodeAt(this.input, this.index);
|
||||
}
|
||||
|
||||
private _attemptChar(charCode: number): boolean {
|
||||
private _attemptCharCode(charCode: number): boolean {
|
||||
if (this.peek === charCode) {
|
||||
this._advance();
|
||||
return true;
|
||||
@ -217,38 +217,55 @@ class _HtmlTokenizer {
|
||||
return false;
|
||||
}
|
||||
|
||||
private _requireChar(charCode: number) {
|
||||
private _attemptCharCodeCaseInsensitive(charCode: number): boolean {
|
||||
if (compareCharCodeCaseInsensitive(this.peek, charCode)) {
|
||||
this._advance();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private _requireCharCode(charCode: number) {
|
||||
var location = this._getLocation();
|
||||
if (!this._attemptChar(charCode)) {
|
||||
if (!this._attemptCharCode(charCode)) {
|
||||
throw this._createError(unexpectedCharacterErrorMsg(this.peek), location);
|
||||
}
|
||||
}
|
||||
|
||||
private _attemptChars(chars: string): boolean {
|
||||
private _attemptStr(chars: string): boolean {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
if (!this._attemptChar(StringWrapper.charCodeAt(chars, i))) {
|
||||
if (!this._attemptCharCode(StringWrapper.charCodeAt(chars, i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _requireChars(chars: string) {
|
||||
private _attemptStrCaseInsensitive(chars: string): boolean {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
if (!this._attemptCharCodeCaseInsensitive(StringWrapper.charCodeAt(chars, i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private _requireStr(chars: string) {
|
||||
var location = this._getLocation();
|
||||
if (!this._attemptChars(chars)) {
|
||||
if (!this._attemptStr(chars)) {
|
||||
throw this._createError(unexpectedCharacterErrorMsg(this.peek), location);
|
||||
}
|
||||
}
|
||||
|
||||
private _attemptUntilFn(predicate: Function) {
|
||||
private _attemptCharCodeUntilFn(predicate: Function) {
|
||||
while (!predicate(this.peek)) {
|
||||
this._advance();
|
||||
}
|
||||
}
|
||||
|
||||
private _requireUntilFn(predicate: Function, len: number) {
|
||||
private _requireCharCodeUntilFn(predicate: Function, len: number) {
|
||||
var start = this._getLocation();
|
||||
this._attemptUntilFn(predicate);
|
||||
this._attemptCharCodeUntilFn(predicate);
|
||||
if (this.index - start.offset < len) {
|
||||
throw this._createError(unexpectedCharacterErrorMsg(this.peek), start);
|
||||
}
|
||||
@ -273,10 +290,10 @@ class _HtmlTokenizer {
|
||||
private _decodeEntity(): string {
|
||||
var start = this._getLocation();
|
||||
this._advance();
|
||||
if (this._attemptChar($HASH)) {
|
||||
let isHex = this._attemptChar($x);
|
||||
if (this._attemptCharCode($HASH)) {
|
||||
let isHex = this._attemptCharCode($x) || this._attemptCharCode($X);
|
||||
let numberStart = this._getLocation().offset;
|
||||
this._attemptUntilFn(isDigitEntityEnd);
|
||||
this._attemptCharCodeUntilFn(isDigitEntityEnd);
|
||||
if (this.peek != $SEMICOLON) {
|
||||
throw this._createError(unexpectedCharacterErrorMsg(this.peek), this._getLocation());
|
||||
}
|
||||
@ -291,7 +308,7 @@ class _HtmlTokenizer {
|
||||
}
|
||||
} else {
|
||||
let startPosition = this._savePosition();
|
||||
this._attemptUntilFn(isNamedEntityEnd);
|
||||
this._attemptCharCodeUntilFn(isNamedEntityEnd);
|
||||
if (this.peek != $SEMICOLON) {
|
||||
this._restorePosition(startPosition);
|
||||
return '&';
|
||||
@ -315,7 +332,7 @@ class _HtmlTokenizer {
|
||||
var parts = [];
|
||||
while (true) {
|
||||
tagCloseStart = this._getLocation();
|
||||
if (this._attemptChar(firstCharOfEnd) && attemptEndRest()) {
|
||||
if (this._attemptCharCode(firstCharOfEnd) && attemptEndRest()) {
|
||||
break;
|
||||
}
|
||||
if (this.index > tagCloseStart.offset) {
|
||||
@ -330,18 +347,18 @@ class _HtmlTokenizer {
|
||||
|
||||
private _consumeComment(start: ParseLocation) {
|
||||
this._beginToken(HtmlTokenType.COMMENT_START, start);
|
||||
this._requireChar($MINUS);
|
||||
this._requireCharCode($MINUS);
|
||||
this._endToken([]);
|
||||
var textToken = this._consumeRawText(false, $MINUS, () => this._attemptChars('->'));
|
||||
var textToken = this._consumeRawText(false, $MINUS, () => this._attemptStr('->'));
|
||||
this._beginToken(HtmlTokenType.COMMENT_END, textToken.sourceSpan.end);
|
||||
this._endToken([]);
|
||||
}
|
||||
|
||||
private _consumeCdata(start: ParseLocation) {
|
||||
this._beginToken(HtmlTokenType.CDATA_START, start);
|
||||
this._requireChars('cdata[');
|
||||
this._requireStr('CDATA[');
|
||||
this._endToken([]);
|
||||
var textToken = this._consumeRawText(false, $RBRACKET, () => this._attemptChars(']>'));
|
||||
var textToken = this._consumeRawText(false, $RBRACKET, () => this._attemptStr(']>'));
|
||||
this._beginToken(HtmlTokenType.CDATA_END, textToken.sourceSpan.end);
|
||||
this._endToken([]);
|
||||
}
|
||||
@ -367,7 +384,7 @@ class _HtmlTokenizer {
|
||||
} else {
|
||||
nameStart = nameOrPrefixStart;
|
||||
}
|
||||
this._requireUntilFn(isNameEnd, this.index === nameStart ? 1 : 0);
|
||||
this._requireCharCodeUntilFn(isNameEnd, this.index === nameStart ? 1 : 0);
|
||||
var name = this.input.substring(nameStart, this.index);
|
||||
return [prefix, name];
|
||||
}
|
||||
@ -381,16 +398,16 @@ class _HtmlTokenizer {
|
||||
}
|
||||
var nameStart = this.index;
|
||||
this._consumeTagOpenStart(start);
|
||||
lowercaseTagName = this.inputLowercase.substring(nameStart, this.index);
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
lowercaseTagName = this.input.substring(nameStart, this.index).toLowerCase();
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
while (this.peek !== $SLASH && this.peek !== $GT) {
|
||||
this._consumeAttributeName();
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
if (this._attemptChar($EQ)) {
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
if (this._attemptCharCode($EQ)) {
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
this._consumeAttributeValue();
|
||||
}
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
}
|
||||
this._consumeTagOpenEnd();
|
||||
} catch (e) {
|
||||
@ -416,11 +433,11 @@ class _HtmlTokenizer {
|
||||
|
||||
private _consumeRawTextWithTagClose(lowercaseTagName: string, decodeEntities: boolean) {
|
||||
var textToken = this._consumeRawText(decodeEntities, $LT, () => {
|
||||
if (!this._attemptChar($SLASH)) return false;
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
if (!this._attemptChars(lowercaseTagName)) return false;
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
if (!this._attemptChar($GT)) return false;
|
||||
if (!this._attemptCharCode($SLASH)) return false;
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
if (!this._attemptStrCaseInsensitive(lowercaseTagName)) return false;
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
if (!this._attemptCharCode($GT)) return false;
|
||||
return true;
|
||||
});
|
||||
this._beginToken(HtmlTokenType.TAG_CLOSE, textToken.sourceSpan.end);
|
||||
@ -453,27 +470,27 @@ class _HtmlTokenizer {
|
||||
this._advance();
|
||||
} else {
|
||||
var valueStart = this.index;
|
||||
this._requireUntilFn(isNameEnd, 1);
|
||||
this._requireCharCodeUntilFn(isNameEnd, 1);
|
||||
value = this.input.substring(valueStart, this.index);
|
||||
}
|
||||
this._endToken([this._processCarriageReturns(value)]);
|
||||
}
|
||||
|
||||
private _consumeTagOpenEnd() {
|
||||
var tokenType =
|
||||
this._attemptChar($SLASH) ? HtmlTokenType.TAG_OPEN_END_VOID : HtmlTokenType.TAG_OPEN_END;
|
||||
var tokenType = this._attemptCharCode($SLASH) ? HtmlTokenType.TAG_OPEN_END_VOID :
|
||||
HtmlTokenType.TAG_OPEN_END;
|
||||
this._beginToken(tokenType);
|
||||
this._requireChar($GT);
|
||||
this._requireCharCode($GT);
|
||||
this._endToken([]);
|
||||
}
|
||||
|
||||
private _consumeTagClose(start: ParseLocation) {
|
||||
this._beginToken(HtmlTokenType.TAG_CLOSE, start);
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
var prefixAndName;
|
||||
prefixAndName = this._consumePrefixAndName();
|
||||
this._attemptUntilFn(isNotWhitespace);
|
||||
this._requireChar($GT);
|
||||
this._attemptCharCodeUntilFn(isNotWhitespace);
|
||||
this._requireCharCode($GT);
|
||||
this._endToken(prefixAndName);
|
||||
}
|
||||
|
||||
@ -534,11 +551,19 @@ function isTextEnd(code: number): boolean {
|
||||
}
|
||||
|
||||
function isAsciiLetter(code: number): boolean {
|
||||
return code >= $a && code <= $z;
|
||||
return code >= $a && code <= $z || code >= $A && code <= $Z;
|
||||
}
|
||||
|
||||
function isAsciiHexDigit(code: number): boolean {
|
||||
return code >= $a && code <= $f || code >= $0 && code <= $9;
|
||||
return code >= $a && code <= $f || code >= $A && code <= $F || code >= $0 && code <= $9;
|
||||
}
|
||||
|
||||
function compareCharCodeCaseInsensitive(code1: number, code2: number): boolean {
|
||||
return toUpperCaseCharCode(code1) == toUpperCaseCharCode(code2);
|
||||
}
|
||||
|
||||
function toUpperCaseCharCode(code: number): number {
|
||||
return code >= $a && code <= $z ? code - $a + $A : code;
|
||||
}
|
||||
|
||||
function mergeTextTokens(srcTokens: HtmlToken[]): HtmlToken[] {
|
||||
|
397
modules/angular2/src/compiler/proto_view_compiler.ts
Normal file
397
modules/angular2/src/compiler/proto_view_compiler.ts
Normal file
@ -0,0 +1,397 @@
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
Type,
|
||||
isString,
|
||||
StringWrapper,
|
||||
IS_DART,
|
||||
CONST_EXPR
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
SetWrapper,
|
||||
StringMapWrapper,
|
||||
ListWrapper,
|
||||
MapWrapper
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
BoundTextAst,
|
||||
TextAst,
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll
|
||||
} from './template_ast';
|
||||
import {
|
||||
CompileTypeMetadata,
|
||||
CompileDirectiveMetadata,
|
||||
CompilePipeMetadata
|
||||
} from './directive_metadata';
|
||||
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||
import {AppProtoView, AppView} from 'angular2/src/core/linker/view';
|
||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||
import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
codeGenConstConstructorCall,
|
||||
codeGenValueFn,
|
||||
codeGenFnHeader,
|
||||
MODULE_SUFFIX,
|
||||
codeGenStringMap,
|
||||
Expression,
|
||||
Statement
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
export const PROTO_VIEW_JIT_IMPORTS = CONST_EXPR(
|
||||
{'AppProtoView': AppProtoView, 'AppProtoElement': AppProtoElement, 'ViewType': ViewType});
|
||||
|
||||
// TODO: have a single file that reexports everything needed for
|
||||
// codegen explicitly
|
||||
// - helps understanding what codegen works against
|
||||
// - less imports in codegen code
|
||||
export var APP_VIEW_MODULE_REF = moduleRef('package:angular2/src/core/linker/view' + MODULE_SUFFIX);
|
||||
export var VIEW_TYPE_MODULE_REF =
|
||||
moduleRef('package:angular2/src/core/linker/view_type' + MODULE_SUFFIX);
|
||||
export var APP_EL_MODULE_REF =
|
||||
moduleRef('package:angular2/src/core/linker/element' + MODULE_SUFFIX);
|
||||
export var METADATA_MODULE_REF =
|
||||
moduleRef('package:angular2/src/core/metadata/view' + MODULE_SUFFIX);
|
||||
|
||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||
const CLASS_ATTR = 'class';
|
||||
const STYLE_ATTR = 'style';
|
||||
|
||||
@Injectable()
|
||||
export class ProtoViewCompiler {
|
||||
constructor() {}
|
||||
|
||||
compileProtoViewRuntime(metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
|
||||
template: TemplateAst[], pipes: CompilePipeMetadata[]):
|
||||
CompileProtoViews<AppProtoView, AppProtoElement, any> {
|
||||
var protoViewFactory = new RuntimeProtoViewFactory(metadataCache, component, pipes);
|
||||
var allProtoViews = [];
|
||||
protoViewFactory.createCompileProtoView(template, [], [], allProtoViews);
|
||||
return new CompileProtoViews<AppProtoView, AppProtoElement, any>([], allProtoViews);
|
||||
}
|
||||
|
||||
compileProtoViewCodeGen(resolvedMetadataCacheExpr: Expression,
|
||||
component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
pipes: CompilePipeMetadata[]):
|
||||
CompileProtoViews<Expression, Expression, string> {
|
||||
var protoViewFactory = new CodeGenProtoViewFactory(resolvedMetadataCacheExpr, component, pipes);
|
||||
var allProtoViews = [];
|
||||
var allStatements = [];
|
||||
protoViewFactory.createCompileProtoView(template, [], allStatements, allProtoViews);
|
||||
return new CompileProtoViews<Expression, Expression, string>(
|
||||
allStatements.map(stmt => stmt.statement), allProtoViews);
|
||||
}
|
||||
}
|
||||
|
||||
export class CompileProtoViews<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
|
||||
constructor(public declarations: STATEMENT[],
|
||||
public protoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
|
||||
}
|
||||
|
||||
|
||||
export class CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
|
||||
constructor(public embeddedTemplateIndex: number,
|
||||
public protoElements: CompileProtoElement<APP_PROTO_EL>[],
|
||||
public protoView: APP_PROTO_VIEW) {}
|
||||
}
|
||||
|
||||
export class CompileProtoElement<APP_PROTO_EL> {
|
||||
constructor(public boundElementIndex, public attrNameAndValues: string[][],
|
||||
public variableNameAndValues: string[][], public renderEvents: BoundEventAst[],
|
||||
public directives: CompileDirectiveMetadata[], public embeddedTemplateIndex: number,
|
||||
public appProtoEl: APP_PROTO_EL) {}
|
||||
}
|
||||
|
||||
function visitAndReturnContext(visitor: TemplateAstVisitor, asts: TemplateAst[],
|
||||
context: any): any {
|
||||
templateVisitAll(visitor, asts, context);
|
||||
return context;
|
||||
}
|
||||
|
||||
abstract class ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> {
|
||||
constructor(public component: CompileDirectiveMetadata) {}
|
||||
|
||||
abstract createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
||||
templateVariableBindings: string[][],
|
||||
targetStatements: STATEMENT[]): APP_PROTO_VIEW;
|
||||
|
||||
abstract createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
||||
variableNameAndValues: string[][],
|
||||
directives: CompileDirectiveMetadata[],
|
||||
targetStatements: STATEMENT[]): APP_PROTO_EL;
|
||||
|
||||
createCompileProtoView(template: TemplateAst[], templateVariableBindings: string[][],
|
||||
targetStatements: STATEMENT[],
|
||||
targetProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]):
|
||||
CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL> {
|
||||
var embeddedTemplateIndex = targetProtoViews.length;
|
||||
// Note: targetProtoViews needs to be in depth first order.
|
||||
// So we "reserve" a space here that we fill after the recursion is done
|
||||
targetProtoViews.push(null);
|
||||
var builder = new ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, any>(
|
||||
this, targetStatements, targetProtoViews);
|
||||
templateVisitAll(builder, template);
|
||||
var viewType = getViewType(this.component, embeddedTemplateIndex);
|
||||
var appProtoView = this.createAppProtoView(embeddedTemplateIndex, viewType,
|
||||
templateVariableBindings, targetStatements);
|
||||
var cpv = new CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>(
|
||||
embeddedTemplateIndex, builder.protoElements, appProtoView);
|
||||
targetProtoViews[embeddedTemplateIndex] = cpv;
|
||||
return cpv;
|
||||
}
|
||||
}
|
||||
|
||||
class CodeGenProtoViewFactory extends ProtoViewFactory<Expression, Expression, Statement> {
|
||||
private _nextVarId: number = 0;
|
||||
|
||||
constructor(public resolvedMetadataCacheExpr: Expression, component: CompileDirectiveMetadata,
|
||||
public pipes: CompilePipeMetadata[]) {
|
||||
super(component);
|
||||
}
|
||||
|
||||
private _nextProtoViewVar(embeddedTemplateIndex: number): string {
|
||||
return `appProtoView${this._nextVarId++}_${this.component.type.name}${embeddedTemplateIndex}`;
|
||||
}
|
||||
|
||||
createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
||||
templateVariableBindings: string[][],
|
||||
targetStatements: Statement[]): Expression {
|
||||
var protoViewVarName = this._nextProtoViewVar(embeddedTemplateIndex);
|
||||
var viewTypeExpr = codeGenViewType(viewType);
|
||||
var pipesExpr = embeddedTemplateIndex === 0 ?
|
||||
codeGenTypesArray(this.pipes.map(pipeMeta => pipeMeta.type)) :
|
||||
null;
|
||||
var statement =
|
||||
`var ${protoViewVarName} = ${APP_VIEW_MODULE_REF}AppProtoView.create(${this.resolvedMetadataCacheExpr.expression}, ${viewTypeExpr}, ${pipesExpr}, ${codeGenStringMap(templateVariableBindings)});`;
|
||||
targetStatements.push(new Statement(statement));
|
||||
return new Expression(protoViewVarName);
|
||||
}
|
||||
|
||||
createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
||||
variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
|
||||
targetStatements: Statement[]): Expression {
|
||||
var varName = `appProtoEl${this._nextVarId++}_${this.component.type.name}`;
|
||||
var value = `${APP_EL_MODULE_REF}AppProtoElement.create(
|
||||
${this.resolvedMetadataCacheExpr.expression},
|
||||
${boundElementIndex},
|
||||
${codeGenStringMap(attrNameAndValues)},
|
||||
${codeGenDirectivesArray(directives)},
|
||||
${codeGenStringMap(variableNameAndValues)}
|
||||
)`;
|
||||
var statement = `var ${varName} = ${value};`;
|
||||
targetStatements.push(new Statement(statement));
|
||||
return new Expression(varName);
|
||||
}
|
||||
}
|
||||
|
||||
class RuntimeProtoViewFactory extends ProtoViewFactory<AppProtoView, AppProtoElement, any> {
|
||||
constructor(public metadataCache: ResolvedMetadataCache, component: CompileDirectiveMetadata,
|
||||
public pipes: CompilePipeMetadata[]) {
|
||||
super(component);
|
||||
}
|
||||
|
||||
createAppProtoView(embeddedTemplateIndex: number, viewType: ViewType,
|
||||
templateVariableBindings: string[][], targetStatements: any[]): AppProtoView {
|
||||
var pipes =
|
||||
embeddedTemplateIndex === 0 ? this.pipes.map(pipeMeta => pipeMeta.type.runtime) : [];
|
||||
var templateVars = keyValueArrayToStringMap(templateVariableBindings);
|
||||
return AppProtoView.create(this.metadataCache, viewType, pipes, templateVars);
|
||||
}
|
||||
|
||||
createAppProtoElement(boundElementIndex: number, attrNameAndValues: string[][],
|
||||
variableNameAndValues: string[][], directives: CompileDirectiveMetadata[],
|
||||
targetStatements: any[]): AppProtoElement {
|
||||
var attrs = keyValueArrayToStringMap(attrNameAndValues);
|
||||
return AppProtoElement.create(this.metadataCache, boundElementIndex, attrs,
|
||||
directives.map(dirMeta => dirMeta.type.runtime),
|
||||
keyValueArrayToStringMap(variableNameAndValues));
|
||||
}
|
||||
}
|
||||
|
||||
class ProtoViewBuilderVisitor<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT> implements
|
||||
TemplateAstVisitor {
|
||||
protoElements: CompileProtoElement<APP_PROTO_EL>[] = [];
|
||||
boundElementCount: number = 0;
|
||||
|
||||
constructor(public factory: ProtoViewFactory<APP_PROTO_VIEW, APP_PROTO_EL, STATEMENT>,
|
||||
public allStatements: STATEMENT[],
|
||||
public allProtoViews: CompileProtoView<APP_PROTO_VIEW, APP_PROTO_EL>[]) {}
|
||||
|
||||
private _readAttrNameAndValues(directives: CompileDirectiveMetadata[],
|
||||
attrAsts: TemplateAst[]): string[][] {
|
||||
var attrs = visitAndReturnContext(this, attrAsts, {});
|
||||
directives.forEach(directiveMeta => {
|
||||
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||
var prevValue = attrs[name];
|
||||
attrs[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||
});
|
||||
});
|
||||
return mapToKeyValueArray(attrs);
|
||||
}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, context: any): any { return null; }
|
||||
visitText(ast: TextAst, context: any): any { return null; }
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
||||
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
var boundElementIndex = null;
|
||||
if (ast.isBound()) {
|
||||
boundElementIndex = this.boundElementCount++;
|
||||
}
|
||||
var component = ast.getComponent();
|
||||
|
||||
var variableNameAndValues: string[][] = [];
|
||||
if (isBlank(component)) {
|
||||
ast.exportAsVars.forEach((varAst) => { variableNameAndValues.push([varAst.name, null]); });
|
||||
}
|
||||
var directives = [];
|
||||
var renderEvents: Map<string, BoundEventAst> =
|
||||
visitAndReturnContext(this, ast.outputs, new Map<string, BoundEventAst>());
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||
directiveAst.visit(this, new DirectiveContext(index, boundElementIndex, renderEvents,
|
||||
variableNameAndValues, directives));
|
||||
});
|
||||
var renderEventArray = [];
|
||||
renderEvents.forEach((eventAst, _) => renderEventArray.push(eventAst));
|
||||
|
||||
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
||||
this._addProtoElement(ast.isBound(), boundElementIndex, attrNameAndValues,
|
||||
variableNameAndValues, renderEventArray, directives, null);
|
||||
templateVisitAll(this, ast.children);
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
var boundElementIndex = this.boundElementCount++;
|
||||
var directives: CompileDirectiveMetadata[] = [];
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst: DirectiveAst, index: number) => {
|
||||
directiveAst.visit(
|
||||
this, new DirectiveContext(index, boundElementIndex, new Map<string, BoundEventAst>(), [],
|
||||
directives));
|
||||
});
|
||||
|
||||
var attrNameAndValues = this._readAttrNameAndValues(directives, ast.attrs);
|
||||
var templateVariableBindings = ast.vars.map(
|
||||
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
||||
var nestedProtoView = this.factory.createCompileProtoView(
|
||||
ast.children, templateVariableBindings, this.allStatements, this.allProtoViews);
|
||||
this._addProtoElement(true, boundElementIndex, attrNameAndValues, [], [], directives,
|
||||
nestedProtoView.embeddedTemplateIndex);
|
||||
return null;
|
||||
}
|
||||
|
||||
private _addProtoElement(isBound: boolean, boundElementIndex, attrNameAndValues: string[][],
|
||||
variableNameAndValues: string[][], renderEvents: BoundEventAst[],
|
||||
directives: CompileDirectiveMetadata[], embeddedTemplateIndex: number) {
|
||||
var appProtoEl = null;
|
||||
if (isBound) {
|
||||
appProtoEl =
|
||||
this.factory.createAppProtoElement(boundElementIndex, attrNameAndValues,
|
||||
variableNameAndValues, directives, this.allStatements);
|
||||
}
|
||||
var compileProtoEl = new CompileProtoElement<APP_PROTO_EL>(
|
||||
boundElementIndex, attrNameAndValues, variableNameAndValues, renderEvents, directives,
|
||||
embeddedTemplateIndex, appProtoEl);
|
||||
this.protoElements.push(compileProtoEl);
|
||||
}
|
||||
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any {
|
||||
attrNameAndValues[ast.name] = ast.value;
|
||||
return null;
|
||||
}
|
||||
visitDirective(ast: DirectiveAst, ctx: DirectiveContext): any {
|
||||
ctx.targetDirectives.push(ast.directive);
|
||||
templateVisitAll(this, ast.hostEvents, ctx.hostEventTargetAndNames);
|
||||
ast.exportAsVars.forEach(
|
||||
varAst => { ctx.targetVariableNameAndValues.push([varAst.name, ctx.index]); });
|
||||
return null;
|
||||
}
|
||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||
eventTargetAndNames.set(ast.fullName, ast);
|
||||
return null;
|
||||
}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
||||
var entryArray = [];
|
||||
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
|
||||
// We need to sort to get a defined output order
|
||||
// for tests and for caching generated artifacts...
|
||||
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
||||
var keyValueArray = [];
|
||||
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
|
||||
return keyValueArray;
|
||||
}
|
||||
|
||||
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||
return `${attrValue1} ${attrValue2}`;
|
||||
} else {
|
||||
return attrValue2;
|
||||
}
|
||||
}
|
||||
|
||||
class DirectiveContext {
|
||||
constructor(public index: number, public boundElementIndex: number,
|
||||
public hostEventTargetAndNames: Map<string, BoundEventAst>,
|
||||
public targetVariableNameAndValues: any[][],
|
||||
public targetDirectives: CompileDirectiveMetadata[]) {}
|
||||
}
|
||||
|
||||
function keyValueArrayToStringMap(keyValueArray: any[][]): {[key: string]: any} {
|
||||
var stringMap: {[key: string]: string} = {};
|
||||
for (var i = 0; i < keyValueArray.length; i++) {
|
||||
var entry = keyValueArray[i];
|
||||
stringMap[entry[0]] = entry[1];
|
||||
}
|
||||
return stringMap;
|
||||
}
|
||||
|
||||
function codeGenDirectivesArray(directives: CompileDirectiveMetadata[]): string {
|
||||
var expressions = directives.map(directiveType => typeRef(directiveType.type));
|
||||
return `[${expressions.join(',')}]`;
|
||||
}
|
||||
|
||||
function codeGenTypesArray(types: CompileTypeMetadata[]): string {
|
||||
var expressions = types.map(typeRef);
|
||||
return `[${expressions.join(',')}]`;
|
||||
}
|
||||
|
||||
function codeGenViewType(value: ViewType): string {
|
||||
if (IS_DART) {
|
||||
return `${VIEW_TYPE_MODULE_REF}${value}`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
function typeRef(type: CompileTypeMetadata): string {
|
||||
return `${moduleRef(type.moduleUrl)}${type.name}`;
|
||||
}
|
||||
|
||||
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
|
||||
if (embeddedTemplateIndex > 0) {
|
||||
return ViewType.EMBEDDED;
|
||||
} else if (component.type.isHost) {
|
||||
return ViewType.HOST;
|
||||
} else {
|
||||
return ViewType.COMPONENT;
|
||||
}
|
||||
}
|
@ -1,23 +1,23 @@
|
||||
import {Compiler, Compiler_, internalCreateProtoView} from 'angular2/src/core/linker/compiler';
|
||||
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {Compiler, Compiler_} from 'angular2/src/core/linker/compiler';
|
||||
import {HostViewFactoryRef, HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
||||
import {TemplateCompiler} from './template_compiler';
|
||||
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Type} from 'angular2/src/facade/lang';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export abstract class RuntimeCompiler extends Compiler {}
|
||||
export abstract class RuntimeCompiler extends Compiler {
|
||||
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
|
||||
abstract clearCache();
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class RuntimeCompiler_ extends Compiler_ implements RuntimeCompiler {
|
||||
constructor(_protoViewFactory: ProtoViewFactory, private _templateCompiler: TemplateCompiler) {
|
||||
super(_protoViewFactory);
|
||||
}
|
||||
constructor(private _templateCompiler: TemplateCompiler) { super(); }
|
||||
|
||||
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
||||
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||
return this._templateCompiler.compileHostComponentRuntime(componentType)
|
||||
.then(compiledHostTemplate => internalCreateProtoView(this, compiledHostTemplate));
|
||||
.then(hostViewFactory => new HostViewFactoryRef_(hostViewFactory));
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
|
@ -11,25 +11,29 @@ import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as cpl from './directive_metadata';
|
||||
import * as md from 'angular2/src/core/metadata/directives';
|
||||
import {DirectiveResolver} from 'angular2/src/core/linker/directive_resolver';
|
||||
import {PipeResolver} from 'angular2/src/core/linker/pipe_resolver';
|
||||
import {ViewResolver} from 'angular2/src/core/linker/view_resolver';
|
||||
import {ViewMetadata} from 'angular2/src/core/metadata/view';
|
||||
import {hasLifecycleHook} from 'angular2/src/core/linker/directive_lifecycle_reflector';
|
||||
import {LifecycleHooks, LIFECYCLE_HOOKS_VALUES} from 'angular2/src/core/linker/interfaces';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {Injectable, Inject, Optional} from 'angular2/src/core/di';
|
||||
import {PLATFORM_DIRECTIVES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||
import {PLATFORM_DIRECTIVES, PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||
import {MODULE_SUFFIX} from './util';
|
||||
import {getUrlScheme} from 'angular2/src/compiler/url_resolver';
|
||||
|
||||
@Injectable()
|
||||
export class RuntimeMetadataResolver {
|
||||
private _cache = new Map<Type, cpl.CompileDirectiveMetadata>();
|
||||
private _directiveCache = new Map<Type, cpl.CompileDirectiveMetadata>();
|
||||
private _pipeCache = new Map<Type, cpl.CompilePipeMetadata>();
|
||||
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
|
||||
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[]) {}
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver,
|
||||
private _viewResolver: ViewResolver,
|
||||
@Optional() @Inject(PLATFORM_DIRECTIVES) private _platformDirectives: Type[],
|
||||
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Type[]) {}
|
||||
|
||||
getMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||
var meta = this._cache.get(directiveType);
|
||||
getDirectiveMetadata(directiveType: Type): cpl.CompileDirectiveMetadata {
|
||||
var meta = this._directiveCache.get(directiveType);
|
||||
if (isBlank(meta)) {
|
||||
var dirMeta = this._directiveResolver.resolve(directiveType);
|
||||
var moduleUrl = null;
|
||||
@ -63,7 +67,23 @@ export class RuntimeMetadataResolver {
|
||||
host: dirMeta.host,
|
||||
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, directiveType))
|
||||
});
|
||||
this._cache.set(directiveType, meta);
|
||||
this._directiveCache.set(directiveType, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
getPipeMetadata(pipeType: Type): cpl.CompilePipeMetadata {
|
||||
var meta = this._pipeCache.get(pipeType);
|
||||
if (isBlank(meta)) {
|
||||
var pipeMeta = this._pipeResolver.resolve(pipeType);
|
||||
var moduleUrl = reflector.importUri(pipeType);
|
||||
meta = new cpl.CompilePipeMetadata({
|
||||
type: new cpl.CompileTypeMetadata(
|
||||
{name: stringify(pipeType), moduleUrl: moduleUrl, runtime: pipeType}),
|
||||
name: pipeMeta.name,
|
||||
pure: pipeMeta.pure
|
||||
});
|
||||
this._pipeCache.set(pipeType, meta);
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
@ -72,13 +92,25 @@ export class RuntimeMetadataResolver {
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var directives = flattenDirectives(view, this._platformDirectives);
|
||||
for (var i = 0; i < directives.length; i++) {
|
||||
if (!isValidDirective(directives[i])) {
|
||||
if (!isValidType(directives[i])) {
|
||||
throw new BaseException(
|
||||
`Unexpected directive value '${stringify(directives[i])}' on the View of component '${stringify(component)}'`);
|
||||
}
|
||||
}
|
||||
|
||||
return directives.map(type => this.getMetadata(type));
|
||||
return directives.map(type => this.getDirectiveMetadata(type));
|
||||
}
|
||||
|
||||
getViewPipesMetadata(component: Type): cpl.CompilePipeMetadata[] {
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var pipes = flattenPipes(view, this._platformPipes);
|
||||
for (var i = 0; i < pipes.length; i++) {
|
||||
if (!isValidType(pipes[i])) {
|
||||
throw new BaseException(
|
||||
`Unexpected piped value '${stringify(pipes[i])}' on the View of component '${stringify(component)}'`);
|
||||
}
|
||||
}
|
||||
return pipes.map(type => this.getPipeMetadata(type));
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,6 +125,17 @@ function flattenDirectives(view: ViewMetadata, platformDirectives: any[]): Type[
|
||||
return directives;
|
||||
}
|
||||
|
||||
function flattenPipes(view: ViewMetadata, platformPipes: any[]): Type[] {
|
||||
let pipes = [];
|
||||
if (isPresent(platformPipes)) {
|
||||
flattenArray(platformPipes, pipes);
|
||||
}
|
||||
if (isPresent(view.pipes)) {
|
||||
flattenArray(view.pipes, pipes);
|
||||
}
|
||||
return pipes;
|
||||
}
|
||||
|
||||
function flattenArray(tree: any[], out: Array<Type | any[]>): void {
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
var item = resolveForwardRef(tree[i]);
|
||||
@ -104,7 +147,7 @@ function flattenArray(tree: any[], out: Array<Type | any[]>): void {
|
||||
}
|
||||
}
|
||||
|
||||
function isValidDirective(value: Type): boolean {
|
||||
function isValidType(value: Type): boolean {
|
||||
return isPresent(value) && (value instanceof Type);
|
||||
}
|
||||
|
||||
|
@ -52,7 +52,7 @@ import {
|
||||
}
|
||||
|
||||
* encapsultion: Styles defined within ShadowDOM, apply only to
|
||||
dom inside the ShadowDOM. Polymer uses one of two techniques to imlement
|
||||
dom inside the ShadowDOM. Polymer uses one of two techniques to implement
|
||||
this feature.
|
||||
|
||||
By default, rules are prefixed with the host element tag name
|
||||
|
@ -10,6 +10,10 @@ export function moduleRef(moduleUrl): string {
|
||||
* Represents generated source code with module references. Internal to the Angular compiler.
|
||||
*/
|
||||
export class SourceModule {
|
||||
static getSourceWithoutImports(sourceWithModuleRefs: string): string {
|
||||
return StringWrapper.replaceAllMapped(sourceWithModuleRefs, MODULE_REGEXP, (match) => '');
|
||||
}
|
||||
|
||||
constructor(public moduleUrl: string, public sourceWithModuleRefs: string) {}
|
||||
|
||||
getSourceWithImports(): SourceWithImports {
|
||||
|
@ -14,7 +14,10 @@ import {
|
||||
MODULE_SUFFIX
|
||||
} from './util';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {COMPONENT_VARIABLE, HOST_ATTR, CONTENT_ATTR} from 'angular2/src/core/render/view_factory';
|
||||
|
||||
const COMPONENT_VARIABLE = '%COMP%';
|
||||
const HOST_ATTR = `_nghost-${COMPONENT_VARIABLE}`;
|
||||
const CONTENT_ATTR = `_ngcontent-${COMPONENT_VARIABLE}`;
|
||||
|
||||
@Injectable()
|
||||
export class StyleCompiler {
|
||||
|
@ -1,37 +1,74 @@
|
||||
import {IS_DART, Type, Json, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper, SetWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {
|
||||
CompiledComponentTemplate,
|
||||
TemplateCmd,
|
||||
CompiledHostTemplate,
|
||||
BeginComponentCmd
|
||||
} from 'angular2/src/core/linker/template_commands';
|
||||
IS_DART,
|
||||
Type,
|
||||
Json,
|
||||
isBlank,
|
||||
isPresent,
|
||||
stringify,
|
||||
evalExpression
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
ListWrapper,
|
||||
SetWrapper,
|
||||
MapWrapper,
|
||||
StringMapWrapper
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
import {
|
||||
createHostComponentMeta,
|
||||
CompileDirectiveMetadata,
|
||||
CompileTypeMetadata,
|
||||
CompileTemplateMetadata
|
||||
CompileTemplateMetadata,
|
||||
CompilePipeMetadata,
|
||||
CompileMetadataWithType
|
||||
} from './directive_metadata';
|
||||
import {TemplateAst} from './template_ast';
|
||||
import {
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
BoundTextAst,
|
||||
TextAst,
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll
|
||||
} from './template_ast';
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {SourceModule, moduleRef} from './source_module';
|
||||
import {ChangeDetectionCompiler} from './change_detector_compiler';
|
||||
import {SourceModule, moduleRef, SourceExpression} from './source_module';
|
||||
import {ChangeDetectionCompiler, CHANGE_DETECTION_JIT_IMPORTS} from './change_detector_compiler';
|
||||
import {StyleCompiler} from './style_compiler';
|
||||
import {CommandCompiler} from './command_compiler';
|
||||
import {TemplateParser} from './template_parser';
|
||||
import {ViewCompiler, VIEW_JIT_IMPORTS} from './view_compiler';
|
||||
import {
|
||||
ProtoViewCompiler,
|
||||
APP_VIEW_MODULE_REF,
|
||||
CompileProtoView,
|
||||
PROTO_VIEW_JIT_IMPORTS
|
||||
} from './proto_view_compiler';
|
||||
import {TemplateParser, PipeCollector} from './template_parser';
|
||||
import {TemplateNormalizer} from './template_normalizer';
|
||||
import {RuntimeMetadataResolver} from './runtime_metadata';
|
||||
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||
import {ChangeDetectorGenConfig} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
|
||||
import {TEMPLATE_COMMANDS_MODULE_REF} from './command_compiler';
|
||||
import {
|
||||
codeGenExportVariable,
|
||||
escapeSingleQuoteString,
|
||||
codeGenValueFn,
|
||||
MODULE_SUFFIX
|
||||
MODULE_SUFFIX,
|
||||
addAll,
|
||||
Expression
|
||||
} from './util';
|
||||
|
||||
export var METADATA_CACHE_MODULE_REF =
|
||||
moduleRef('package:angular2/src/core/linker/resolved_metadata_cache' + MODULE_SUFFIX);
|
||||
|
||||
/**
|
||||
* An internal module of the Angular compiler that begins with component types,
|
||||
* extracts templates, and eventually produces a compiled version of the component
|
||||
@ -40,15 +77,16 @@ import {
|
||||
@Injectable()
|
||||
export class TemplateCompiler {
|
||||
private _hostCacheKeys = new Map<Type, any>();
|
||||
private _compiledTemplateCache = new Map<any, CompiledComponentTemplate>();
|
||||
private _compiledTemplateDone = new Map<any, Promise<CompiledComponentTemplate>>();
|
||||
private _nextTemplateId: number = 0;
|
||||
private _compiledTemplateCache = new Map<any, CompiledTemplate>();
|
||||
private _compiledTemplateDone = new Map<any, Promise<CompiledTemplate>>();
|
||||
|
||||
constructor(private _runtimeMetadataResolver: RuntimeMetadataResolver,
|
||||
private _templateNormalizer: TemplateNormalizer,
|
||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
||||
private _commandCompiler: CommandCompiler,
|
||||
private _cdCompiler: ChangeDetectionCompiler) {}
|
||||
private _cdCompiler: ChangeDetectionCompiler,
|
||||
private _protoViewCompiler: ProtoViewCompiler, private _viewCompiler: ViewCompiler,
|
||||
private _resolvedMetadataCache: ResolvedMetadataCache,
|
||||
private _genConfig: ChangeDetectorGenConfig) {}
|
||||
|
||||
normalizeDirectiveMetadata(directive: CompileDirectiveMetadata):
|
||||
Promise<CompileDirectiveMetadata> {
|
||||
@ -75,99 +113,29 @@ export class TemplateCompiler {
|
||||
}));
|
||||
}
|
||||
|
||||
compileHostComponentRuntime(type: Type): Promise<CompiledHostTemplate> {
|
||||
compileHostComponentRuntime(type: Type): Promise<HostViewFactory> {
|
||||
var compMeta: CompileDirectiveMetadata =
|
||||
this._runtimeMetadataResolver.getDirectiveMetadata(type);
|
||||
var hostCacheKey = this._hostCacheKeys.get(type);
|
||||
if (isBlank(hostCacheKey)) {
|
||||
hostCacheKey = new Object();
|
||||
this._hostCacheKeys.set(type, hostCacheKey);
|
||||
var compMeta: CompileDirectiveMetadata = this._runtimeMetadataResolver.getMetadata(type);
|
||||
assertComponent(compMeta);
|
||||
var hostMeta: CompileDirectiveMetadata =
|
||||
createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
|
||||
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], new Set());
|
||||
this._compileComponentRuntime(hostCacheKey, hostMeta, [compMeta], [], []);
|
||||
}
|
||||
return this._compiledTemplateDone.get(hostCacheKey)
|
||||
.then(compiledTemplate => new CompiledHostTemplate(compiledTemplate));
|
||||
.then((compiledTemplate: CompiledTemplate) =>
|
||||
new HostViewFactory(compMeta.selector, compiledTemplate.viewFactory));
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
this._hostCacheKeys.clear();
|
||||
this._styleCompiler.clearCache();
|
||||
this._compiledTemplateCache.clear();
|
||||
this._compiledTemplateDone.clear();
|
||||
}
|
||||
|
||||
private _compileComponentRuntime(
|
||||
cacheKey: any, compMeta: CompileDirectiveMetadata, viewDirectives: CompileDirectiveMetadata[],
|
||||
compilingComponentCacheKeys: Set<any>): CompiledComponentTemplate {
|
||||
let uniqViewDirectives = removeDuplicates(viewDirectives);
|
||||
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
||||
var done = this._compiledTemplateDone.get(cacheKey);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
var styles = [];
|
||||
var changeDetectorFactory;
|
||||
var commands = [];
|
||||
var templateId = `${stringify(compMeta.type.runtime)}Template${this._nextTemplateId++}`;
|
||||
compiledTemplate = new CompiledComponentTemplate(
|
||||
templateId, (dispatcher) => changeDetectorFactory(dispatcher), commands, styles);
|
||||
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
||||
compilingComponentCacheKeys.add(cacheKey);
|
||||
done = PromiseWrapper
|
||||
.all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
|
||||
uniqViewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||
var childPromises = [];
|
||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||
var parsedTemplate = this._templateParser.parse(
|
||||
compMeta.template.template, normalizedViewDirMetas, compMeta.type.name);
|
||||
|
||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
changeDetectorFactory = changeDetectorFactories[0];
|
||||
var tmpStyles: string[] = stylesAndNormalizedViewDirMetas[0];
|
||||
tmpStyles.forEach(style => styles.push(style));
|
||||
var tmpCommands: TemplateCmd[] = this._compileCommandsRuntime(
|
||||
compMeta, parsedTemplate, changeDetectorFactories,
|
||||
compilingComponentCacheKeys, childPromises);
|
||||
tmpCommands.forEach(cmd => commands.push(cmd));
|
||||
return PromiseWrapper.all(childPromises);
|
||||
})
|
||||
.then((_) => {
|
||||
SetWrapper.delete(compilingComponentCacheKeys, cacheKey);
|
||||
return compiledTemplate;
|
||||
});
|
||||
this._compiledTemplateDone.set(cacheKey, done);
|
||||
}
|
||||
return compiledTemplate;
|
||||
}
|
||||
|
||||
private _compileCommandsRuntime(compMeta: CompileDirectiveMetadata, parsedTemplate: TemplateAst[],
|
||||
changeDetectorFactories: Function[],
|
||||
compilingComponentCacheKeys: Set<Type>,
|
||||
childPromises: Promise<any>[]): TemplateCmd[] {
|
||||
var cmds: TemplateCmd[] = this._commandCompiler.compileComponentRuntime(
|
||||
compMeta, parsedTemplate, changeDetectorFactories,
|
||||
(childComponentDir: CompileDirectiveMetadata) => {
|
||||
var childCacheKey = childComponentDir.type.runtime;
|
||||
var childViewDirectives: CompileDirectiveMetadata[] =
|
||||
this._runtimeMetadataResolver.getViewDirectivesMetadata(
|
||||
childComponentDir.type.runtime);
|
||||
var childIsRecursive = SetWrapper.has(compilingComponentCacheKeys, childCacheKey);
|
||||
var childTemplate = this._compileComponentRuntime(
|
||||
childCacheKey, childComponentDir, childViewDirectives, compilingComponentCacheKeys);
|
||||
if (!childIsRecursive) {
|
||||
// Only wait for a child if it is not a cycle
|
||||
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
|
||||
}
|
||||
return () => childTemplate;
|
||||
});
|
||||
cmds.forEach(cmd => {
|
||||
if (cmd instanceof BeginComponentCmd) {
|
||||
cmd.templateGetter();
|
||||
}
|
||||
});
|
||||
return cmds;
|
||||
this._hostCacheKeys.clear();
|
||||
}
|
||||
|
||||
compileTemplatesCodeGen(components: NormalizedComponentWithViewDirectives[]): SourceModule {
|
||||
@ -175,38 +143,22 @@ export class TemplateCompiler {
|
||||
throw new BaseException('No components given');
|
||||
}
|
||||
var declarations = [];
|
||||
var templateArguments = [];
|
||||
var componentMetas: CompileDirectiveMetadata[] = [];
|
||||
components.forEach(componentWithDirs => {
|
||||
var compMeta = <CompileDirectiveMetadata>componentWithDirs.component;
|
||||
assertComponent(compMeta);
|
||||
componentMetas.push(compMeta);
|
||||
|
||||
this._processTemplateCodeGen(compMeta, componentWithDirs.directives, declarations,
|
||||
templateArguments);
|
||||
this._compileComponentCodeGen(compMeta, componentWithDirs.directives, componentWithDirs.pipes,
|
||||
declarations);
|
||||
if (compMeta.dynamicLoadable) {
|
||||
var hostMeta = createHostComponentMeta(compMeta.type, compMeta.selector);
|
||||
componentMetas.push(hostMeta);
|
||||
this._processTemplateCodeGen(hostMeta, [compMeta], declarations, templateArguments);
|
||||
var viewFactoryExpression =
|
||||
this._compileComponentCodeGen(hostMeta, [compMeta], [], declarations);
|
||||
var constructionKeyword = IS_DART ? 'const' : 'new';
|
||||
var compiledTemplateExpr =
|
||||
`${constructionKeyword} ${APP_VIEW_MODULE_REF}HostViewFactory('${compMeta.selector}',${viewFactoryExpression})`;
|
||||
var varName = codeGenHostViewFactoryName(compMeta.type);
|
||||
declarations.push(`${codeGenExportVariable(varName)}${compiledTemplateExpr};`);
|
||||
}
|
||||
});
|
||||
ListWrapper.forEachWithIndex(componentMetas, (compMeta: CompileDirectiveMetadata,
|
||||
index: number) => {
|
||||
var templateId = `${compMeta.type.moduleUrl}|${compMeta.type.name}`;
|
||||
var constructionKeyword = IS_DART ? 'const' : 'new';
|
||||
var compiledTemplateExpr =
|
||||
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledComponentTemplate('${templateId}',${(<any[]>templateArguments[index]).join(',')})`;
|
||||
var variableValueExpr;
|
||||
if (compMeta.type.isHost) {
|
||||
variableValueExpr =
|
||||
`${constructionKeyword} ${TEMPLATE_COMMANDS_MODULE_REF}CompiledHostTemplate(${compiledTemplateExpr})`;
|
||||
} else {
|
||||
variableValueExpr = compiledTemplateExpr;
|
||||
}
|
||||
var varName = templateVariableName(compMeta.type);
|
||||
declarations.push(`${codeGenExportVariable(varName)}${variableValueExpr};`);
|
||||
declarations.push(`${codeGenValueFn([], varName, templateGetterName(compMeta.type))};`);
|
||||
});
|
||||
var moduleUrl = components[0].component.type.moduleUrl;
|
||||
return new SourceModule(`${templateModuleUrl(moduleUrl)}`, declarations.join('\n'));
|
||||
}
|
||||
@ -215,31 +167,150 @@ export class TemplateCompiler {
|
||||
return this._styleCompiler.compileStylesheetCodeGen(stylesheetUrl, cssText);
|
||||
}
|
||||
|
||||
private _processTemplateCodeGen(compMeta: CompileDirectiveMetadata,
|
||||
directives: CompileDirectiveMetadata[],
|
||||
targetDeclarations: string[], targetTemplateArguments: any[][]) {
|
||||
let uniqueDirectives = removeDuplicates(directives);
|
||||
|
||||
|
||||
private _compileComponentRuntime(cacheKey: any, compMeta: CompileDirectiveMetadata,
|
||||
viewDirectives: CompileDirectiveMetadata[],
|
||||
pipes: CompilePipeMetadata[],
|
||||
compilingComponentsPath: any[]): CompiledTemplate {
|
||||
let uniqViewDirectives = <CompileDirectiveMetadata[]>removeDuplicates(viewDirectives);
|
||||
let uniqViewPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
|
||||
var compiledTemplate = this._compiledTemplateCache.get(cacheKey);
|
||||
var done = this._compiledTemplateDone.get(cacheKey);
|
||||
if (isBlank(compiledTemplate)) {
|
||||
compiledTemplate = new CompiledTemplate();
|
||||
this._compiledTemplateCache.set(cacheKey, compiledTemplate);
|
||||
done = PromiseWrapper
|
||||
.all([<any>this._styleCompiler.compileComponentRuntime(compMeta.template)].concat(
|
||||
uniqViewDirectives.map(dirMeta => this.normalizeDirectiveMetadata(dirMeta))))
|
||||
.then((stylesAndNormalizedViewDirMetas: any[]) => {
|
||||
var normalizedViewDirMetas = stylesAndNormalizedViewDirMetas.slice(1);
|
||||
var styles = stylesAndNormalizedViewDirMetas[0];
|
||||
var parsedTemplate = this._templateParser.parse(
|
||||
compMeta.template.template, normalizedViewDirMetas, uniqViewPipes,
|
||||
compMeta.type.name);
|
||||
|
||||
var childPromises = [];
|
||||
var usedDirectives = DirectiveCollector.findUsedDirectives(parsedTemplate);
|
||||
usedDirectives.components.forEach(
|
||||
component => this._compileNestedComponentRuntime(
|
||||
component, compilingComponentsPath, childPromises));
|
||||
return PromiseWrapper.all(childPromises)
|
||||
.then((_) => {
|
||||
var filteredPipes = filterPipes(parsedTemplate, uniqViewPipes);
|
||||
compiledTemplate.init(this._createViewFactoryRuntime(
|
||||
compMeta, parsedTemplate, usedDirectives.directives, styles,
|
||||
filteredPipes));
|
||||
return compiledTemplate;
|
||||
});
|
||||
});
|
||||
this._compiledTemplateDone.set(cacheKey, done);
|
||||
}
|
||||
return compiledTemplate;
|
||||
}
|
||||
|
||||
private _compileNestedComponentRuntime(childComponentDir: CompileDirectiveMetadata,
|
||||
parentCompilingComponentsPath: any[],
|
||||
childPromises: Promise<any>[]) {
|
||||
var compilingComponentsPath = ListWrapper.clone(parentCompilingComponentsPath);
|
||||
|
||||
var childCacheKey = childComponentDir.type.runtime;
|
||||
var childViewDirectives: CompileDirectiveMetadata[] =
|
||||
this._runtimeMetadataResolver.getViewDirectivesMetadata(childComponentDir.type.runtime);
|
||||
var childViewPipes: CompilePipeMetadata[] =
|
||||
this._runtimeMetadataResolver.getViewPipesMetadata(childComponentDir.type.runtime);
|
||||
var childIsRecursive = ListWrapper.contains(compilingComponentsPath, childCacheKey);
|
||||
compilingComponentsPath.push(childCacheKey);
|
||||
this._compileComponentRuntime(childCacheKey, childComponentDir, childViewDirectives,
|
||||
childViewPipes, compilingComponentsPath);
|
||||
if (!childIsRecursive) {
|
||||
// Only wait for a child if it is not a cycle
|
||||
childPromises.push(this._compiledTemplateDone.get(childCacheKey));
|
||||
}
|
||||
}
|
||||
|
||||
private _createViewFactoryRuntime(compMeta: CompileDirectiveMetadata,
|
||||
parsedTemplate: TemplateAst[],
|
||||
directives: CompileDirectiveMetadata[], styles: string[],
|
||||
pipes: CompilePipeMetadata[]): Function {
|
||||
if (IS_DART || !this._genConfig.useJit) {
|
||||
var changeDetectorFactories = this._cdCompiler.compileComponentRuntime(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
var protoViews = this._protoViewCompiler.compileProtoViewRuntime(
|
||||
this._resolvedMetadataCache, compMeta, parsedTemplate, pipes);
|
||||
return this._viewCompiler.compileComponentRuntime(
|
||||
compMeta, parsedTemplate, styles, protoViews.protoViews, changeDetectorFactories,
|
||||
(compMeta) => this._getNestedComponentViewFactory(compMeta));
|
||||
} else {
|
||||
var declarations = [];
|
||||
var viewFactoryExpr = this._createViewFactoryCodeGen('resolvedMetadataCache', compMeta,
|
||||
new SourceExpression([], 'styles'),
|
||||
parsedTemplate, pipes, declarations);
|
||||
var vars: {[key: string]: any} =
|
||||
{'exports': {}, 'styles': styles, 'resolvedMetadataCache': this._resolvedMetadataCache};
|
||||
directives.forEach(dirMeta => {
|
||||
vars[dirMeta.type.name] = dirMeta.type.runtime;
|
||||
if (dirMeta.isComponent && dirMeta.type.runtime !== compMeta.type.runtime) {
|
||||
vars[`viewFactory_${dirMeta.type.name}0`] = this._getNestedComponentViewFactory(dirMeta);
|
||||
}
|
||||
});
|
||||
pipes.forEach(pipeMeta => vars[pipeMeta.type.name] = pipeMeta.type.runtime);
|
||||
var declarationsWithoutImports =
|
||||
SourceModule.getSourceWithoutImports(declarations.join('\n'));
|
||||
return evalExpression(
|
||||
`viewFactory_${compMeta.type.name}`, viewFactoryExpr, declarationsWithoutImports,
|
||||
mergeStringMaps(
|
||||
[vars, CHANGE_DETECTION_JIT_IMPORTS, PROTO_VIEW_JIT_IMPORTS, VIEW_JIT_IMPORTS]));
|
||||
}
|
||||
}
|
||||
|
||||
private _getNestedComponentViewFactory(compMeta: CompileDirectiveMetadata): Function {
|
||||
return this._compiledTemplateCache.get(compMeta.type.runtime).viewFactory;
|
||||
}
|
||||
|
||||
private _compileComponentCodeGen(compMeta: CompileDirectiveMetadata,
|
||||
directives: CompileDirectiveMetadata[],
|
||||
pipes: CompilePipeMetadata[],
|
||||
targetDeclarations: string[]): string {
|
||||
let uniqueDirectives = <CompileDirectiveMetadata[]>removeDuplicates(directives);
|
||||
let uniqPipes = <CompilePipeMetadata[]>removeDuplicates(pipes);
|
||||
var styleExpr = this._styleCompiler.compileComponentCodeGen(compMeta.template);
|
||||
var parsedTemplate = this._templateParser.parse(compMeta.template.template, uniqueDirectives,
|
||||
compMeta.type.name);
|
||||
uniqPipes, compMeta.type.name);
|
||||
var filteredPipes = filterPipes(parsedTemplate, uniqPipes);
|
||||
return this._createViewFactoryCodeGen(
|
||||
`${METADATA_CACHE_MODULE_REF}CODEGEN_RESOLVED_METADATA_CACHE`, compMeta, styleExpr,
|
||||
parsedTemplate, filteredPipes, targetDeclarations);
|
||||
}
|
||||
|
||||
private _createViewFactoryCodeGen(resolvedMetadataCacheExpr: string,
|
||||
compMeta: CompileDirectiveMetadata, styleExpr: SourceExpression,
|
||||
parsedTemplate: TemplateAst[], pipes: CompilePipeMetadata[],
|
||||
targetDeclarations: string[]): string {
|
||||
var changeDetectorsExprs = this._cdCompiler.compileComponentCodeGen(
|
||||
compMeta.type, compMeta.changeDetection, parsedTemplate);
|
||||
var commandsExpr = this._commandCompiler.compileComponentCodeGen(
|
||||
compMeta, parsedTemplate, changeDetectorsExprs.expressions,
|
||||
codeGenComponentTemplateFactory);
|
||||
var protoViewExprs = this._protoViewCompiler.compileProtoViewCodeGen(
|
||||
new Expression(resolvedMetadataCacheExpr), compMeta, parsedTemplate, pipes);
|
||||
var viewFactoryExpr = this._viewCompiler.compileComponentCodeGen(
|
||||
compMeta, parsedTemplate, styleExpr, protoViewExprs.protoViews, changeDetectorsExprs,
|
||||
codeGenComponentViewFactoryName);
|
||||
|
||||
addAll(styleExpr.declarations, targetDeclarations);
|
||||
addAll(changeDetectorsExprs.declarations, targetDeclarations);
|
||||
addAll(commandsExpr.declarations, targetDeclarations);
|
||||
addAll(protoViewExprs.declarations, targetDeclarations);
|
||||
addAll(viewFactoryExpr.declarations, targetDeclarations);
|
||||
|
||||
targetTemplateArguments.push(
|
||||
[changeDetectorsExprs.expressions[0], commandsExpr.expression, styleExpr.expression]);
|
||||
return viewFactoryExpr.expression;
|
||||
}
|
||||
}
|
||||
|
||||
export class NormalizedComponentWithViewDirectives {
|
||||
constructor(public component: CompileDirectiveMetadata,
|
||||
public directives: CompileDirectiveMetadata[]) {}
|
||||
public directives: CompileDirectiveMetadata[], public pipes: CompilePipeMetadata[]) {}
|
||||
}
|
||||
|
||||
class CompiledTemplate {
|
||||
viewFactory: Function = null;
|
||||
init(viewFactory: Function) { this.viewFactory = viewFactory; }
|
||||
}
|
||||
|
||||
function assertComponent(meta: CompileDirectiveMetadata) {
|
||||
@ -248,30 +319,28 @@ function assertComponent(meta: CompileDirectiveMetadata) {
|
||||
}
|
||||
}
|
||||
|
||||
function templateVariableName(type: CompileTypeMetadata): string {
|
||||
return `${type.name}Template`;
|
||||
}
|
||||
|
||||
function templateGetterName(type: CompileTypeMetadata): string {
|
||||
return `${templateVariableName(type)}Getter`;
|
||||
}
|
||||
|
||||
function templateModuleUrl(moduleUrl: string): string {
|
||||
var urlWithoutSuffix = moduleUrl.substring(0, moduleUrl.length - MODULE_SUFFIX.length);
|
||||
return `${urlWithoutSuffix}.template${MODULE_SUFFIX}`;
|
||||
}
|
||||
|
||||
function addAll(source: any[], target: any[]) {
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
target.push(source[i]);
|
||||
}
|
||||
|
||||
function codeGenHostViewFactoryName(type: CompileTypeMetadata): string {
|
||||
return `hostViewFactory_${type.name}`;
|
||||
}
|
||||
|
||||
function codeGenComponentTemplateFactory(nestedCompType: CompileDirectiveMetadata): string {
|
||||
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}${templateGetterName(nestedCompType.type)}`;
|
||||
function codeGenComponentViewFactoryName(nestedCompType: CompileDirectiveMetadata): string {
|
||||
return `${moduleRef(templateModuleUrl(nestedCompType.type.moduleUrl))}viewFactory_${nestedCompType.type.name}0`;
|
||||
}
|
||||
|
||||
function removeDuplicates(items: CompileDirectiveMetadata[]): CompileDirectiveMetadata[] {
|
||||
function mergeStringMaps(maps: Array<{[key: string]: any}>): {[key: string]: any} {
|
||||
var result = {};
|
||||
maps.forEach(
|
||||
(map) => { StringMapWrapper.forEach(map, (value, key) => { result[key] = value; }); });
|
||||
return result;
|
||||
}
|
||||
|
||||
function removeDuplicates(items: CompileMetadataWithType[]): CompileMetadataWithType[] {
|
||||
let res = [];
|
||||
items.forEach(item => {
|
||||
let hasMatch =
|
||||
@ -284,3 +353,100 @@ function removeDuplicates(items: CompileDirectiveMetadata[]): CompileDirectiveMe
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
class DirectiveCollector implements TemplateAstVisitor {
|
||||
static findUsedDirectives(parsedTemplate: TemplateAst[]): DirectiveCollector {
|
||||
var collector = new DirectiveCollector();
|
||||
templateVisitAll(collector, parsedTemplate);
|
||||
return collector;
|
||||
}
|
||||
|
||||
directives: CompileDirectiveMetadata[] = [];
|
||||
components: CompileDirectiveMetadata[] = [];
|
||||
|
||||
visitBoundText(ast: BoundTextAst, context: any): any { return null; }
|
||||
visitText(ast: TextAst, context: any): any { return null; }
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
||||
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
templateVisitAll(this, ast.directives);
|
||||
templateVisitAll(this, ast.children);
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
templateVisitAll(this, ast.directives);
|
||||
templateVisitAll(this, ast.children);
|
||||
return null;
|
||||
}
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, ctx: any): any {
|
||||
if (ast.directive.isComponent) {
|
||||
this.components.push(ast.directive);
|
||||
}
|
||||
this.directives.push(ast.directive);
|
||||
return null;
|
||||
}
|
||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||
return null;
|
||||
}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
|
||||
function filterPipes(template: TemplateAst[],
|
||||
allPipes: CompilePipeMetadata[]): CompilePipeMetadata[] {
|
||||
var visitor = new PipeVisitor();
|
||||
templateVisitAll(visitor, template);
|
||||
return allPipes.filter((pipeMeta) => SetWrapper.has(visitor.collector.pipes, pipeMeta.name));
|
||||
}
|
||||
|
||||
class PipeVisitor implements TemplateAstVisitor {
|
||||
collector: PipeCollector = new PipeCollector();
|
||||
|
||||
visitBoundText(ast: BoundTextAst, context: any): any {
|
||||
ast.value.visit(this.collector);
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, context: any): any { return null; }
|
||||
|
||||
visitNgContent(ast: NgContentAst, context: any): any { return null; }
|
||||
|
||||
visitElement(ast: ElementAst, context: any): any {
|
||||
templateVisitAll(this, ast.inputs);
|
||||
templateVisitAll(this, ast.outputs);
|
||||
templateVisitAll(this, ast.directives);
|
||||
templateVisitAll(this, ast.children);
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any): any {
|
||||
templateVisitAll(this, ast.outputs);
|
||||
templateVisitAll(this, ast.directives);
|
||||
templateVisitAll(this, ast.children);
|
||||
return null;
|
||||
}
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitAttr(ast: AttrAst, attrNameAndValues: {[key: string]: string}): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, ctx: any): any {
|
||||
templateVisitAll(this, ast.inputs);
|
||||
templateVisitAll(this, ast.hostEvents);
|
||||
templateVisitAll(this, ast.hostProperties);
|
||||
return null;
|
||||
}
|
||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||
ast.handler.visit(this.collector);
|
||||
return null;
|
||||
}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {
|
||||
ast.value.visit(this.collector);
|
||||
return null;
|
||||
}
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {
|
||||
ast.value.visit(this.collector);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +112,10 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
|
||||
case PreparsedElementType.STYLESHEET:
|
||||
this.styleUrls.push(preparsedElement.hrefAttr);
|
||||
break;
|
||||
default:
|
||||
// DDC reports this as error. See:
|
||||
// https://github.com/dart-lang/dev_compiler/issues/428
|
||||
break;
|
||||
}
|
||||
if (preparsedElement.nonBindable) {
|
||||
this.ngNonBindableStackCount++;
|
||||
|
@ -5,10 +5,11 @@ import {CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {Parser, AST, ASTWithSource} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {TemplateBinding} from 'angular2/src/core/change_detection/parser/ast';
|
||||
import {CompileDirectiveMetadata} from './directive_metadata';
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from './directive_metadata';
|
||||
import {HtmlParser} from './html_parser';
|
||||
import {splitNsName} from './html_tags';
|
||||
import {ParseSourceSpan, ParseError, ParseLocation} from './parse_util';
|
||||
import {RecursiveAstVisitor, BindingPipe} from 'angular2/src/core/change_detection/parser/ast';
|
||||
|
||||
|
||||
import {
|
||||
@ -51,8 +52,8 @@ import {splitAtColon} from './util';
|
||||
// Group 3 = "on-"
|
||||
// Group 4 = "bindon-"
|
||||
// Group 5 = the identifier after "bind-", "var-/#", or "on-"
|
||||
// Group 6 = idenitifer inside [()]
|
||||
// Group 7 = idenitifer inside []
|
||||
// Group 6 = identifier inside [()]
|
||||
// Group 7 = identifier inside []
|
||||
// Group 8 = identifier inside ()
|
||||
var BIND_NAME_REGEXP =
|
||||
/^(?:(?:(?:(bind-)|(var-|#)|(on-)|(bindon-))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/g;
|
||||
@ -88,9 +89,10 @@ export class TemplateParser {
|
||||
private _htmlParser: HtmlParser,
|
||||
@Optional() @Inject(TEMPLATE_TRANSFORMS) public transforms: TemplateAstVisitor[]) {}
|
||||
|
||||
parse(template: string, directives: CompileDirectiveMetadata[],
|
||||
parse(template: string, directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
templateUrl: string): TemplateAst[] {
|
||||
var parseVisitor = new TemplateParseVisitor(directives, this._exprParser, this._schemaRegistry);
|
||||
var parseVisitor =
|
||||
new TemplateParseVisitor(directives, pipes, this._exprParser, this._schemaRegistry);
|
||||
var htmlAstWithErrors = this._htmlParser.parse(template, templateUrl);
|
||||
var result = htmlVisitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_COMPONENT);
|
||||
var errors: ParseError[] = htmlAstWithErrors.errors.concat(parseVisitor.errors);
|
||||
@ -111,9 +113,10 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
errors: TemplateParseError[] = [];
|
||||
directivesIndex = new Map<CompileDirectiveMetadata, number>();
|
||||
ngContentCount: number = 0;
|
||||
pipesByName: Map<string, CompilePipeMetadata>;
|
||||
|
||||
constructor(directives: CompileDirectiveMetadata[], private _exprParser: Parser,
|
||||
private _schemaRegistry: ElementSchemaRegistry) {
|
||||
constructor(directives: CompileDirectiveMetadata[], pipes: CompilePipeMetadata[],
|
||||
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry) {
|
||||
this.selectorMatcher = new SelectorMatcher();
|
||||
ListWrapper.forEachWithIndex(directives,
|
||||
(directive: CompileDirectiveMetadata, index: number) => {
|
||||
@ -121,6 +124,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
this.selectorMatcher.addSelectables(selector, directive);
|
||||
this.directivesIndex.set(directive, index);
|
||||
});
|
||||
this.pipesByName = new Map<string, CompilePipeMetadata>();
|
||||
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
|
||||
}
|
||||
|
||||
private _reportError(message: string, sourceSpan: ParseSourceSpan) {
|
||||
@ -130,7 +135,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseInterpolation(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseInterpolation(value, sourceInfo);
|
||||
var ast = this._exprParser.parseInterpolation(value, sourceInfo);
|
||||
this._checkPipes(ast, sourceSpan);
|
||||
return ast;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||
@ -140,7 +147,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseAction(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseAction(value, sourceInfo);
|
||||
var ast = this._exprParser.parseAction(value, sourceInfo);
|
||||
this._checkPipes(ast, sourceSpan);
|
||||
return ast;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||
@ -150,7 +159,9 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseBinding(value: string, sourceSpan: ParseSourceSpan): ASTWithSource {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseBinding(value, sourceInfo);
|
||||
var ast = this._exprParser.parseBinding(value, sourceInfo);
|
||||
this._checkPipes(ast, sourceSpan);
|
||||
return ast;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return this._exprParser.wrapLiteralPrimitive('ERROR', sourceInfo);
|
||||
@ -160,13 +171,31 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
private _parseTemplateBindings(value: string, sourceSpan: ParseSourceSpan): TemplateBinding[] {
|
||||
var sourceInfo = sourceSpan.start.toString();
|
||||
try {
|
||||
return this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||
var bindings = this._exprParser.parseTemplateBindings(value, sourceInfo);
|
||||
bindings.forEach((binding) => {
|
||||
if (isPresent(binding.expression)) {
|
||||
this._checkPipes(binding.expression, sourceSpan);
|
||||
}
|
||||
});
|
||||
return bindings;
|
||||
} catch (e) {
|
||||
this._reportError(`${e}`, sourceSpan);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
private _checkPipes(ast: ASTWithSource, sourceSpan: ParseSourceSpan) {
|
||||
if (isPresent(ast)) {
|
||||
var collector = new PipeCollector();
|
||||
ast.visit(collector);
|
||||
collector.pipes.forEach((pipeName) => {
|
||||
if (!this.pipesByName.has(pipeName)) {
|
||||
this._reportError(`The pipe '${pipeName}' could not be found`, sourceSpan);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
visitText(ast: HtmlTextAst, component: Component): any {
|
||||
var ngContentIndex = component.findNgContentIndex(TEXT_CSS_SELECTOR);
|
||||
var expr = this._parseInterpolation(ast.value, ast.sourceSpan);
|
||||
@ -210,13 +239,13 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
var attrs = [];
|
||||
|
||||
element.attrs.forEach(attr => {
|
||||
matchableAttrs.push([attr.name, attr.value]);
|
||||
var hasBinding = this._parseAttr(attr, matchableAttrs, elementOrDirectiveProps, events, vars);
|
||||
var hasTemplateBinding = this._parseInlineTemplateBinding(
|
||||
attr, templateMatchableAttrs, templateElementOrDirectiveProps, templateVars);
|
||||
if (!hasBinding && !hasTemplateBinding) {
|
||||
// don't include the bindings as attributes as well in the AST
|
||||
attrs.push(this.visitAttr(attr, null));
|
||||
matchableAttrs.push([attr.name, attr.value]);
|
||||
}
|
||||
if (hasTemplateBinding) {
|
||||
hasInlineTemplates = true;
|
||||
@ -714,3 +743,14 @@ function createElementCssSelector(elementName: string, matchableAttrs: string[][
|
||||
|
||||
var EMPTY_COMPONENT = new Component(new SelectorMatcher(), null);
|
||||
var NON_BINDABLE_VISITOR = new NonBindableVisitor();
|
||||
|
||||
|
||||
export class PipeCollector extends RecursiveAstVisitor {
|
||||
pipes: Set<string> = new Set<string>();
|
||||
visitPipe(ast: BindingPipe): any {
|
||||
this.pipes.add(ast.name);
|
||||
ast.exp.visit(this);
|
||||
this.visitAll(ast.args);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,11 @@
|
||||
import {IS_DART, StringWrapper, isBlank} from 'angular2/src/facade/lang';
|
||||
import {
|
||||
IS_DART,
|
||||
StringWrapper,
|
||||
isBlank,
|
||||
isPresent,
|
||||
isString,
|
||||
isArray
|
||||
} from 'angular2/src/facade/lang';
|
||||
|
||||
var CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||
var DASH_CASE_REGEXP = /-([a-z])/g;
|
||||
@ -7,6 +14,8 @@ var DOUBLE_QUOTE_ESCAPE_STRING_RE = /"|\\|\n|\r|\$/g;
|
||||
|
||||
export var MODULE_SUFFIX = IS_DART ? '.dart' : '.js';
|
||||
|
||||
export var CONST_VAR = IS_DART ? 'const' : 'var';
|
||||
|
||||
export function camelCaseToDashCase(input: string): string {
|
||||
return StringWrapper.replaceAllMapped(input, CAMEL_CASE_REGEXP,
|
||||
(m) => { return '-' + m[1].toLowerCase(); });
|
||||
@ -63,17 +72,24 @@ export function codeGenConstConstructorCall(name: string): string {
|
||||
|
||||
export function codeGenValueFn(params: string[], value: string, fnName: string = ''): string {
|
||||
if (IS_DART) {
|
||||
return `${fnName}(${params.join(',')}) => ${value}`;
|
||||
return `${codeGenFnHeader(params, fnName)} => ${value}`;
|
||||
} else {
|
||||
return `function ${fnName}(${params.join(',')}) { return ${value}; }`;
|
||||
return `${codeGenFnHeader(params, fnName)} { return ${value}; }`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenFnHeader(params: string[], fnName: string = ''): string {
|
||||
if (IS_DART) {
|
||||
return `${fnName}(${params.join(',')})`;
|
||||
} else {
|
||||
return `function ${fnName}(${params.join(',')})`;
|
||||
}
|
||||
}
|
||||
export function codeGenToString(expr: string): string {
|
||||
if (IS_DART) {
|
||||
return `'\${${expr}}'`;
|
||||
} else {
|
||||
// JS automatically convets to string...
|
||||
// JS automatically converts to string...
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
@ -86,3 +102,77 @@ export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||
return defaultValues;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class Statement {
|
||||
constructor(public statement: string) {}
|
||||
}
|
||||
|
||||
export class Expression {
|
||||
constructor(public expression: string, public isArray = false) {}
|
||||
}
|
||||
|
||||
export function escapeValue(value: any): string {
|
||||
if (value instanceof Expression) {
|
||||
return value.expression;
|
||||
} else if (isString(value)) {
|
||||
return escapeSingleQuoteString(value);
|
||||
} else if (isBlank(value)) {
|
||||
return 'null';
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
||||
|
||||
export function codeGenArray(data: any[]): string {
|
||||
return `[${data.map(escapeValue).join(',')}]`;
|
||||
}
|
||||
|
||||
export function codeGenFlatArray(values: any[]): string {
|
||||
var result = '([';
|
||||
var isFirstArrayEntry = true;
|
||||
var concatFn = IS_DART ? '.addAll' : 'concat';
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var value = values[i];
|
||||
if (value instanceof Expression && (<Expression>value).isArray) {
|
||||
result += `]).${concatFn}(${value.expression}).${concatFn}([`;
|
||||
isFirstArrayEntry = true;
|
||||
} else {
|
||||
if (!isFirstArrayEntry) {
|
||||
result += ',';
|
||||
}
|
||||
isFirstArrayEntry = false;
|
||||
result += escapeValue(value);
|
||||
}
|
||||
}
|
||||
result += '])';
|
||||
return result;
|
||||
}
|
||||
|
||||
export function codeGenStringMap(keyValueArray: any[][]): string {
|
||||
return `{${keyValueArray.map(codeGenKeyValue).join(',')}}`;
|
||||
}
|
||||
|
||||
function codeGenKeyValue(keyValue: any[]): string {
|
||||
return `${escapeValue(keyValue[0])}:${escapeValue(keyValue[1])}`;
|
||||
}
|
||||
|
||||
export function addAll(source: any[], target: any[]) {
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
target.push(source[i]);
|
||||
}
|
||||
}
|
||||
|
||||
export function flattenArray(source: any[], target: any[]): any[] {
|
||||
if (isPresent(source)) {
|
||||
for (var i = 0; i < source.length; i++) {
|
||||
var item = source[i];
|
||||
if (isArray(item)) {
|
||||
flattenArray(item, target);
|
||||
} else {
|
||||
target.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
606
modules/angular2/src/compiler/view_compiler.ts
Normal file
606
modules/angular2/src/compiler/view_compiler.ts
Normal file
@ -0,0 +1,606 @@
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
Type,
|
||||
isString,
|
||||
StringWrapper,
|
||||
IS_DART,
|
||||
CONST_EXPR
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {SetWrapper, StringMapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
BoundTextAst,
|
||||
TextAst,
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll
|
||||
} from './template_ast';
|
||||
import {CompileTypeMetadata, CompileDirectiveMetadata} from './directive_metadata';
|
||||
import {SourceExpressions, SourceExpression, moduleRef} from './source_module';
|
||||
import {
|
||||
AppProtoView,
|
||||
AppView,
|
||||
flattenNestedViewRenderNodes,
|
||||
checkSlotCount
|
||||
} from 'angular2/src/core/linker/view';
|
||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||
import {AppViewManager_} from 'angular2/src/core/linker/view_manager';
|
||||
import {AppProtoElement, AppElement} from 'angular2/src/core/linker/element';
|
||||
import {Renderer, ParentRenderer} from 'angular2/src/core/render/api';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {
|
||||
escapeSingleQuoteString,
|
||||
codeGenConstConstructorCall,
|
||||
codeGenValueFn,
|
||||
codeGenFnHeader,
|
||||
MODULE_SUFFIX,
|
||||
Statement,
|
||||
escapeValue,
|
||||
codeGenArray,
|
||||
codeGenFlatArray,
|
||||
Expression,
|
||||
flattenArray,
|
||||
CONST_VAR
|
||||
} from './util';
|
||||
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
|
||||
|
||||
import {
|
||||
APP_VIEW_MODULE_REF,
|
||||
APP_EL_MODULE_REF,
|
||||
METADATA_MODULE_REF,
|
||||
CompileProtoView,
|
||||
CompileProtoElement
|
||||
} from './proto_view_compiler';
|
||||
|
||||
export const VIEW_JIT_IMPORTS = CONST_EXPR({
|
||||
'AppView': AppView,
|
||||
'AppElement': AppElement,
|
||||
'flattenNestedViewRenderNodes': flattenNestedViewRenderNodes,
|
||||
'checkSlotCount': checkSlotCount
|
||||
});
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ViewCompiler {
|
||||
constructor() {}
|
||||
|
||||
compileComponentRuntime(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
styles: Array<string | any[]>,
|
||||
protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
|
||||
changeDetectorFactories: Function[],
|
||||
componentViewFactory: Function): Function {
|
||||
var viewFactory = new RuntimeViewFactory(component, styles, protoViews, changeDetectorFactories,
|
||||
componentViewFactory);
|
||||
return viewFactory.createViewFactory(template, 0, []);
|
||||
}
|
||||
|
||||
compileComponentCodeGen(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
styles: SourceExpression,
|
||||
protoViews: CompileProtoView<Expression, Expression>[],
|
||||
changeDetectorFactoryExpressions: SourceExpressions,
|
||||
componentViewFactory: Function): SourceExpression {
|
||||
var viewFactory = new CodeGenViewFactory(
|
||||
component, styles, protoViews, changeDetectorFactoryExpressions, componentViewFactory);
|
||||
var targetStatements: Statement[] = [];
|
||||
var viewFactoryExpression = viewFactory.createViewFactory(template, 0, targetStatements);
|
||||
return new SourceExpression(targetStatements.map(stmt => stmt.statement),
|
||||
viewFactoryExpression.expression);
|
||||
}
|
||||
}
|
||||
|
||||
interface ViewFactory<EXPRESSION, STATEMENT> {
|
||||
createText(renderer: EXPRESSION, parent: EXPRESSION, text: string,
|
||||
targetStatements: STATEMENT[]): EXPRESSION;
|
||||
|
||||
createElement(renderer: EXPRESSION, parent: EXPRESSION, name: string, rootSelector: EXPRESSION,
|
||||
targetStatements: STATEMENT[]): EXPRESSION;
|
||||
|
||||
createTemplateAnchor(renderer: EXPRESSION, parent: EXPRESSION,
|
||||
targetStatements: STATEMENT[]): EXPRESSION;
|
||||
|
||||
createGlobalEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
|
||||
eventAst: BoundEventAst, targetStatements: STATEMENT[]): EXPRESSION;
|
||||
|
||||
createElementEventListener(renderer: EXPRESSION, view: EXPRESSION, boundElementIndex: number,
|
||||
renderNode: EXPRESSION, eventAst: BoundEventAst,
|
||||
targetStatements: STATEMENT[]): EXPRESSION;
|
||||
|
||||
setElementAttribute(renderer: EXPRESSION, renderNode: EXPRESSION, attrName: string,
|
||||
attrValue: string, targetStatements: STATEMENT[]);
|
||||
|
||||
createAppElement(appProtoEl: EXPRESSION, view: EXPRESSION, renderNode: EXPRESSION,
|
||||
parentAppEl: EXPRESSION, embeddedViewFactory: EXPRESSION,
|
||||
targetStatements: STATEMENT[]): EXPRESSION;
|
||||
|
||||
createAndSetComponentView(renderer: EXPRESSION, viewManager: EXPRESSION, view: EXPRESSION,
|
||||
appEl: EXPRESSION, component: CompileDirectiveMetadata,
|
||||
contentNodesByNgContentIndex: EXPRESSION[][],
|
||||
targetStatements: STATEMENT[]);
|
||||
|
||||
getProjectedNodes(projectableNodes: EXPRESSION, ngContentIndex: number): EXPRESSION;
|
||||
|
||||
appendProjectedNodes(renderer: EXPRESSION, parent: EXPRESSION, nodes: EXPRESSION,
|
||||
targetStatements: STATEMENT[]);
|
||||
|
||||
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
||||
targetStatements: STATEMENT[]): EXPRESSION;
|
||||
}
|
||||
|
||||
class CodeGenViewFactory implements ViewFactory<Expression, Statement> {
|
||||
private _nextVarId: number = 0;
|
||||
constructor(public component: CompileDirectiveMetadata, public styles: SourceExpression,
|
||||
public protoViews: CompileProtoView<Expression, Expression>[],
|
||||
public changeDetectorExpressions: SourceExpressions,
|
||||
public componentViewFactory: Function) {}
|
||||
|
||||
private _nextVar(prefix: string): string {
|
||||
return `${prefix}${this._nextVarId++}_${this.component.type.name}`;
|
||||
}
|
||||
|
||||
private _nextRenderVar(): string { return this._nextVar('render'); }
|
||||
|
||||
private _nextAppVar(): string { return this._nextVar('app'); }
|
||||
|
||||
private _nextDisposableVar(): string {
|
||||
return `disposable${this._nextVarId++}_${this.component.type.name}`;
|
||||
}
|
||||
|
||||
createText(renderer: Expression, parent: Expression, text: string,
|
||||
targetStatements: Statement[]): Expression {
|
||||
var varName = this._nextRenderVar();
|
||||
var statement =
|
||||
`var ${varName} = ${renderer.expression}.createText(${isPresent(parent) ? parent.expression : null}, ${escapeSingleQuoteString(text)});`;
|
||||
targetStatements.push(new Statement(statement));
|
||||
return new Expression(varName);
|
||||
}
|
||||
|
||||
createElement(renderer: Expression, parentRenderNode: Expression, name: string,
|
||||
rootSelector: Expression, targetStatements: Statement[]): Expression {
|
||||
var varName = this._nextRenderVar();
|
||||
var valueExpr;
|
||||
if (isPresent(rootSelector)) {
|
||||
valueExpr = `${rootSelector.expression} == null ?
|
||||
${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)}) :
|
||||
${renderer.expression}.selectRootElement(${rootSelector.expression});`;
|
||||
} else {
|
||||
valueExpr =
|
||||
`${renderer.expression}.createElement(${isPresent(parentRenderNode) ? parentRenderNode.expression : null}, ${escapeSingleQuoteString(name)})`;
|
||||
}
|
||||
var statement = `var ${varName} = ${valueExpr};`;
|
||||
targetStatements.push(new Statement(statement));
|
||||
return new Expression(varName);
|
||||
}
|
||||
|
||||
createTemplateAnchor(renderer: Expression, parentRenderNode: Expression,
|
||||
targetStatements: Statement[]): Expression {
|
||||
var varName = this._nextRenderVar();
|
||||
var valueExpr =
|
||||
`${renderer.expression}.createTemplateAnchor(${isPresent(parentRenderNode) ? parentRenderNode.expression : null});`;
|
||||
targetStatements.push(new Statement(`var ${varName} = ${valueExpr}`));
|
||||
return new Expression(varName);
|
||||
}
|
||||
|
||||
createGlobalEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
||||
eventAst: BoundEventAst, targetStatements: Statement[]): Expression {
|
||||
var disposableVar = this._nextDisposableVar();
|
||||
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
||||
targetStatements.push(new Statement(
|
||||
`var ${disposableVar} = ${renderer.expression}.listenGlobal(${escapeValue(eventAst.target)}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
|
||||
return new Expression(disposableVar);
|
||||
}
|
||||
|
||||
createElementEventListener(renderer: Expression, appView: Expression, boundElementIndex: number,
|
||||
renderNode: Expression, eventAst: BoundEventAst,
|
||||
targetStatements: Statement[]) {
|
||||
var disposableVar = this._nextDisposableVar();
|
||||
var eventHandlerExpr = codeGenEventHandler(appView, boundElementIndex, eventAst.fullName);
|
||||
targetStatements.push(new Statement(
|
||||
`var ${disposableVar} = ${renderer.expression}.listen(${renderNode.expression}, ${escapeValue(eventAst.name)}, ${eventHandlerExpr});`));
|
||||
return new Expression(disposableVar);
|
||||
}
|
||||
|
||||
setElementAttribute(renderer: Expression, renderNode: Expression, attrName: string,
|
||||
attrValue: string, targetStatements: Statement[]) {
|
||||
targetStatements.push(new Statement(
|
||||
`${renderer.expression}.setElementAttribute(${renderNode.expression}, ${escapeSingleQuoteString(attrName)}, ${escapeSingleQuoteString(attrValue)});`));
|
||||
}
|
||||
|
||||
createAppElement(appProtoEl: Expression, appView: Expression, renderNode: Expression,
|
||||
parentAppEl: Expression, embeddedViewFactory: Expression,
|
||||
targetStatements: Statement[]): Expression {
|
||||
var appVar = this._nextAppVar();
|
||||
var varValue =
|
||||
`new ${APP_EL_MODULE_REF}AppElement(${appProtoEl.expression}, ${appView.expression},
|
||||
${isPresent(parentAppEl) ? parentAppEl.expression : null}, ${renderNode.expression}, ${isPresent(embeddedViewFactory) ? embeddedViewFactory.expression : null})`;
|
||||
targetStatements.push(new Statement(`var ${appVar} = ${varValue};`));
|
||||
return new Expression(appVar);
|
||||
}
|
||||
|
||||
createAndSetComponentView(renderer: Expression, viewManager: Expression, view: Expression,
|
||||
appEl: Expression, component: CompileDirectiveMetadata,
|
||||
contentNodesByNgContentIndex: Expression[][],
|
||||
targetStatements: Statement[]) {
|
||||
var codeGenContentNodes;
|
||||
if (this.component.type.isHost) {
|
||||
codeGenContentNodes = `${view.expression}.projectableNodes`;
|
||||
} else {
|
||||
codeGenContentNodes =
|
||||
`[${contentNodesByNgContentIndex.map( nodes => codeGenFlatArray(nodes) ).join(',')}]`;
|
||||
}
|
||||
targetStatements.push(new Statement(
|
||||
`${this.componentViewFactory(component)}(${renderer.expression}, ${viewManager.expression}, ${appEl.expression}, ${codeGenContentNodes}, null, null, null);`));
|
||||
}
|
||||
|
||||
getProjectedNodes(projectableNodes: Expression, ngContentIndex: number): Expression {
|
||||
return new Expression(`${projectableNodes.expression}[${ngContentIndex}]`, true);
|
||||
}
|
||||
|
||||
appendProjectedNodes(renderer: Expression, parent: Expression, nodes: Expression,
|
||||
targetStatements: Statement[]) {
|
||||
targetStatements.push(new Statement(
|
||||
`${renderer.expression}.projectNodes(${parent.expression}, ${APP_VIEW_MODULE_REF}flattenNestedViewRenderNodes(${nodes.expression}));`));
|
||||
}
|
||||
|
||||
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
||||
targetStatements: Statement[]): Expression {
|
||||
var compileProtoView = this.protoViews[embeddedTemplateIndex];
|
||||
var isHostView = this.component.type.isHost;
|
||||
var isComponentView = embeddedTemplateIndex === 0 && !isHostView;
|
||||
var visitor = new ViewBuilderVisitor<Expression, Statement>(
|
||||
new Expression('renderer'), new Expression('viewManager'),
|
||||
new Expression('projectableNodes'), isHostView ? new Expression('rootSelector') : null,
|
||||
new Expression('view'), compileProtoView, targetStatements, this);
|
||||
|
||||
templateVisitAll(
|
||||
visitor, asts,
|
||||
new ParentElement(isComponentView ? new Expression('parentRenderNode') : null, null, null));
|
||||
|
||||
var appProtoView = compileProtoView.protoView.expression;
|
||||
var viewFactoryName = codeGenViewFactoryName(this.component, embeddedTemplateIndex);
|
||||
var changeDetectorFactory = this.changeDetectorExpressions.expressions[embeddedTemplateIndex];
|
||||
var factoryArgs = [
|
||||
'parentRenderer',
|
||||
'viewManager',
|
||||
'containerEl',
|
||||
'projectableNodes',
|
||||
'rootSelector',
|
||||
'dynamicallyCreatedProviders',
|
||||
'rootInjector'
|
||||
];
|
||||
var initRendererStmts = [];
|
||||
var rendererExpr = `parentRenderer`;
|
||||
if (embeddedTemplateIndex === 0) {
|
||||
var renderCompTypeVar = this._nextVar('renderType');
|
||||
targetStatements.push(new Statement(`var ${renderCompTypeVar} = null;`));
|
||||
var stylesVar = this._nextVar('styles');
|
||||
targetStatements.push(
|
||||
new Statement(`${CONST_VAR} ${stylesVar} = ${this.styles.expression};`));
|
||||
var encapsulation = this.component.template.encapsulation;
|
||||
initRendererStmts.push(`if (${renderCompTypeVar} == null) {
|
||||
${renderCompTypeVar} = viewManager.createRenderComponentType(${codeGenViewEncapsulation(encapsulation)}, ${stylesVar});
|
||||
}`);
|
||||
rendererExpr = `parentRenderer.renderComponent(${renderCompTypeVar})`;
|
||||
}
|
||||
var statement = `
|
||||
${codeGenFnHeader(factoryArgs, viewFactoryName)}{
|
||||
${initRendererStmts.join('\n')}
|
||||
var renderer = ${rendererExpr};
|
||||
var view = new ${APP_VIEW_MODULE_REF}AppView(
|
||||
${appProtoView}, renderer, viewManager,
|
||||
projectableNodes,
|
||||
containerEl,
|
||||
dynamicallyCreatedProviders, rootInjector,
|
||||
${changeDetectorFactory}()
|
||||
);
|
||||
${APP_VIEW_MODULE_REF}checkSlotCount(${escapeValue(this.component.type.name)}, ${this.component.template.ngContentSelectors.length}, projectableNodes);
|
||||
${isComponentView ? 'var parentRenderNode = renderer.createViewRoot(view.containerAppElement.nativeElement);' : ''}
|
||||
${visitor.renderStmts.map(stmt => stmt.statement).join('\n')}
|
||||
${visitor.appStmts.map(stmt => stmt.statement).join('\n')}
|
||||
|
||||
view.init(${codeGenFlatArray(visitor.rootNodesOrAppElements)}, ${codeGenArray(visitor.renderNodes)}, ${codeGenArray(visitor.appDisposables)},
|
||||
${codeGenArray(visitor.appElements)});
|
||||
return view;
|
||||
}`;
|
||||
targetStatements.push(new Statement(statement));
|
||||
return new Expression(viewFactoryName);
|
||||
}
|
||||
}
|
||||
|
||||
class RuntimeViewFactory implements ViewFactory<any, any> {
|
||||
constructor(public component: CompileDirectiveMetadata, public styles: Array<string | any[]>,
|
||||
public protoViews: CompileProtoView<AppProtoView, AppProtoElement>[],
|
||||
public changeDetectorFactories: Function[], public componentViewFactory: Function) {}
|
||||
|
||||
createText(renderer: Renderer, parent: any, text: string, targetStatements: any[]): any {
|
||||
return renderer.createText(parent, text);
|
||||
}
|
||||
|
||||
createElement(renderer: Renderer, parent: any, name: string, rootSelector: string,
|
||||
targetStatements: any[]): any {
|
||||
var el;
|
||||
if (isPresent(rootSelector)) {
|
||||
el = renderer.selectRootElement(rootSelector);
|
||||
} else {
|
||||
el = renderer.createElement(parent, name);
|
||||
}
|
||||
return el;
|
||||
}
|
||||
|
||||
createTemplateAnchor(renderer: Renderer, parent: any, targetStatements: any[]): any {
|
||||
return renderer.createTemplateAnchor(parent);
|
||||
}
|
||||
|
||||
createGlobalEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
|
||||
eventAst: BoundEventAst, targetStatements: any[]): any {
|
||||
return renderer.listenGlobal(
|
||||
eventAst.target, eventAst.name,
|
||||
(event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex));
|
||||
}
|
||||
|
||||
createElementEventListener(renderer: Renderer, appView: AppView, boundElementIndex: number,
|
||||
renderNode: any, eventAst: BoundEventAst,
|
||||
targetStatements: any[]): any {
|
||||
return renderer.listen(
|
||||
renderNode, eventAst.name,
|
||||
(event) => appView.triggerEventHandlers(eventAst.fullName, event, boundElementIndex));
|
||||
}
|
||||
|
||||
setElementAttribute(renderer: Renderer, renderNode: any, attrName: string, attrValue: string,
|
||||
targetStatements: any[]) {
|
||||
renderer.setElementAttribute(renderNode, attrName, attrValue);
|
||||
}
|
||||
|
||||
createAppElement(appProtoEl: AppProtoElement, appView: AppView, renderNode: any,
|
||||
parentAppEl: AppElement, embeddedViewFactory: Function,
|
||||
targetStatements: any[]): any {
|
||||
return new AppElement(appProtoEl, appView, parentAppEl, renderNode, embeddedViewFactory);
|
||||
}
|
||||
|
||||
createAndSetComponentView(renderer: Renderer, viewManager: AppViewManager_, appView: AppView,
|
||||
appEl: AppElement, component: CompileDirectiveMetadata,
|
||||
contentNodesByNgContentIndex: Array<Array<any | any[]>>,
|
||||
targetStatements: any[]) {
|
||||
var flattenedContentNodes;
|
||||
if (this.component.type.isHost) {
|
||||
flattenedContentNodes = appView.projectableNodes;
|
||||
} else {
|
||||
flattenedContentNodes = ListWrapper.createFixedSize(contentNodesByNgContentIndex.length);
|
||||
for (var i = 0; i < contentNodesByNgContentIndex.length; i++) {
|
||||
flattenedContentNodes[i] = flattenArray(contentNodesByNgContentIndex[i], []);
|
||||
}
|
||||
}
|
||||
this.componentViewFactory(component)(renderer, viewManager, appEl, flattenedContentNodes);
|
||||
}
|
||||
|
||||
getProjectedNodes(projectableNodes: any[][], ngContentIndex: number): any[] {
|
||||
return projectableNodes[ngContentIndex];
|
||||
}
|
||||
|
||||
appendProjectedNodes(renderer: Renderer, parent: any, nodes: any[], targetStatements: any[]) {
|
||||
renderer.projectNodes(parent, flattenNestedViewRenderNodes(nodes));
|
||||
}
|
||||
|
||||
createViewFactory(asts: TemplateAst[], embeddedTemplateIndex: number,
|
||||
targetStatements: any[]): Function {
|
||||
var compileProtoView = this.protoViews[embeddedTemplateIndex];
|
||||
var isComponentView = compileProtoView.protoView.type === ViewType.COMPONENT;
|
||||
var renderComponentType = null;
|
||||
return (parentRenderer: ParentRenderer, viewManager: AppViewManager_, containerEl: AppElement,
|
||||
projectableNodes: any[][], rootSelector: string = null,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[] = null,
|
||||
rootInjector: Injector = null) => {
|
||||
checkSlotCount(this.component.type.name, this.component.template.ngContentSelectors.length,
|
||||
projectableNodes);
|
||||
var renderer;
|
||||
if (embeddedTemplateIndex === 0) {
|
||||
if (isBlank(renderComponentType)) {
|
||||
renderComponentType = viewManager.createRenderComponentType(
|
||||
this.component.template.encapsulation, this.styles);
|
||||
}
|
||||
renderer = parentRenderer.renderComponent(renderComponentType);
|
||||
} else {
|
||||
renderer = <Renderer>parentRenderer;
|
||||
}
|
||||
var changeDetector = this.changeDetectorFactories[embeddedTemplateIndex]();
|
||||
var view =
|
||||
new AppView(compileProtoView.protoView, renderer, viewManager, projectableNodes,
|
||||
containerEl, dynamicallyCreatedProviders, rootInjector, changeDetector);
|
||||
var visitor = new ViewBuilderVisitor<any, any>(
|
||||
renderer, viewManager, projectableNodes, rootSelector, view, compileProtoView, [], this);
|
||||
var parentRenderNode =
|
||||
isComponentView ? renderer.createViewRoot(containerEl.nativeElement) : null;
|
||||
templateVisitAll(visitor, asts, new ParentElement(parentRenderNode, null, null));
|
||||
view.init(flattenArray(visitor.rootNodesOrAppElements, []), visitor.renderNodes,
|
||||
visitor.appDisposables, visitor.appElements);
|
||||
return view;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ParentElement<EXPRESSION> {
|
||||
public contentNodesByNgContentIndex: Array<EXPRESSION>[];
|
||||
|
||||
constructor(public renderNode: EXPRESSION, public appEl: EXPRESSION,
|
||||
public component: CompileDirectiveMetadata) {
|
||||
if (isPresent(component)) {
|
||||
this.contentNodesByNgContentIndex =
|
||||
ListWrapper.createFixedSize(component.template.ngContentSelectors.length);
|
||||
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||
this.contentNodesByNgContentIndex[i] = [];
|
||||
}
|
||||
} else {
|
||||
this.contentNodesByNgContentIndex = null;
|
||||
}
|
||||
}
|
||||
|
||||
addContentNode(ngContentIndex: number, nodeExpr: EXPRESSION) {
|
||||
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
||||
}
|
||||
}
|
||||
|
||||
class ViewBuilderVisitor<EXPRESSION, STATEMENT> implements TemplateAstVisitor {
|
||||
renderStmts: Array<STATEMENT> = [];
|
||||
renderNodes: EXPRESSION[] = [];
|
||||
appStmts: Array<STATEMENT> = [];
|
||||
appElements: EXPRESSION[] = [];
|
||||
appDisposables: EXPRESSION[] = [];
|
||||
|
||||
rootNodesOrAppElements: EXPRESSION[] = [];
|
||||
|
||||
elementCount: number = 0;
|
||||
|
||||
constructor(public renderer: EXPRESSION, public viewManager: EXPRESSION,
|
||||
public projectableNodes: EXPRESSION, public rootSelector: EXPRESSION,
|
||||
public view: EXPRESSION, public protoView: CompileProtoView<EXPRESSION, EXPRESSION>,
|
||||
public targetStatements: STATEMENT[],
|
||||
public factory: ViewFactory<EXPRESSION, STATEMENT>) {}
|
||||
|
||||
private _addRenderNode(renderNode: EXPRESSION, appEl: EXPRESSION, ngContentIndex: number,
|
||||
parent: ParentElement<EXPRESSION>) {
|
||||
this.renderNodes.push(renderNode);
|
||||
if (isPresent(parent.component)) {
|
||||
if (isPresent(ngContentIndex)) {
|
||||
parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : renderNode);
|
||||
}
|
||||
} else if (isBlank(parent.renderNode)) {
|
||||
this.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
private _getParentRenderNode(ngContentIndex: number,
|
||||
parent: ParentElement<EXPRESSION>): EXPRESSION {
|
||||
return isPresent(parent.component) &&
|
||||
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
|
||||
null :
|
||||
parent.renderNode;
|
||||
}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, parent: ParentElement<EXPRESSION>): any {
|
||||
return this._visitText('', ast.ngContentIndex, parent);
|
||||
}
|
||||
visitText(ast: TextAst, parent: ParentElement<EXPRESSION>): any {
|
||||
return this._visitText(ast.value, ast.ngContentIndex, parent);
|
||||
}
|
||||
private _visitText(value: string, ngContentIndex: number, parent: ParentElement<EXPRESSION>) {
|
||||
var renderNode = this.factory.createText(
|
||||
this.renderer, this._getParentRenderNode(ngContentIndex, parent), value, this.renderStmts);
|
||||
this._addRenderNode(renderNode, null, ngContentIndex, parent);
|
||||
return null;
|
||||
}
|
||||
|
||||
visitNgContent(ast: NgContentAst, parent: ParentElement<EXPRESSION>): any {
|
||||
var nodesExpression = this.factory.getProjectedNodes(this.projectableNodes, ast.index);
|
||||
if (isPresent(parent.component)) {
|
||||
if (isPresent(ast.ngContentIndex)) {
|
||||
parent.addContentNode(ast.ngContentIndex, nodesExpression);
|
||||
}
|
||||
} else {
|
||||
if (isPresent(parent.renderNode)) {
|
||||
this.factory.appendProjectedNodes(this.renderer, parent.renderNode, nodesExpression,
|
||||
this.renderStmts);
|
||||
} else {
|
||||
this.rootNodesOrAppElements.push(nodesExpression);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst, parent: ParentElement<EXPRESSION>): any {
|
||||
var renderNode = this.factory.createElement(
|
||||
this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), ast.name,
|
||||
this.rootSelector, this.renderStmts);
|
||||
|
||||
var component = ast.getComponent();
|
||||
var elementIndex = this.elementCount++;
|
||||
var protoEl = this.protoView.protoElements[elementIndex];
|
||||
|
||||
protoEl.renderEvents.forEach((eventAst) => {
|
||||
var disposable;
|
||||
if (isPresent(eventAst.target)) {
|
||||
disposable = this.factory.createGlobalEventListener(
|
||||
this.renderer, this.view, protoEl.boundElementIndex, eventAst, this.renderStmts);
|
||||
} else {
|
||||
disposable = this.factory.createElementEventListener(this.renderer, this.view,
|
||||
protoEl.boundElementIndex, renderNode,
|
||||
eventAst, this.renderStmts);
|
||||
}
|
||||
this.appDisposables.push(disposable);
|
||||
});
|
||||
for (var i = 0; i < protoEl.attrNameAndValues.length; i++) {
|
||||
var attrName = protoEl.attrNameAndValues[i][0];
|
||||
var attrValue = protoEl.attrNameAndValues[i][1];
|
||||
this.factory.setElementAttribute(this.renderer, renderNode, attrName, attrValue,
|
||||
this.renderStmts);
|
||||
}
|
||||
var appEl = null;
|
||||
if (isPresent(protoEl.appProtoEl)) {
|
||||
appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode, parent.appEl,
|
||||
null, this.appStmts);
|
||||
this.appElements.push(appEl);
|
||||
}
|
||||
this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
|
||||
|
||||
var newParent = new ParentElement<EXPRESSION>(
|
||||
renderNode, isPresent(appEl) ? appEl : parent.appEl, component);
|
||||
templateVisitAll(this, ast.children, newParent);
|
||||
if (isPresent(appEl) && isPresent(component)) {
|
||||
this.factory.createAndSetComponentView(this.renderer, this.viewManager, this.view, appEl,
|
||||
component, newParent.contentNodesByNgContentIndex,
|
||||
this.appStmts);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: ParentElement<EXPRESSION>): any {
|
||||
var renderNode = this.factory.createTemplateAnchor(
|
||||
this.renderer, this._getParentRenderNode(ast.ngContentIndex, parent), this.renderStmts);
|
||||
|
||||
var elementIndex = this.elementCount++;
|
||||
var protoEl = this.protoView.protoElements[elementIndex];
|
||||
var embeddedViewFactory = this.factory.createViewFactory(
|
||||
ast.children, protoEl.embeddedTemplateIndex, this.targetStatements);
|
||||
|
||||
var appEl = this.factory.createAppElement(protoEl.appProtoEl, this.view, renderNode,
|
||||
parent.appEl, embeddedViewFactory, this.appStmts);
|
||||
this._addRenderNode(renderNode, appEl, ast.ngContentIndex, parent);
|
||||
this.appElements.push(appEl);
|
||||
return null;
|
||||
}
|
||||
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
||||
visitEvent(ast: BoundEventAst, ctx: any): any { return null; }
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
|
||||
function codeGenEventHandler(view: Expression, boundElementIndex: number,
|
||||
eventName: string): string {
|
||||
return codeGenValueFn(
|
||||
['event'],
|
||||
`${view.expression}.triggerEventHandlers(${escapeValue(eventName)}, event, ${boundElementIndex})`);
|
||||
}
|
||||
|
||||
function codeGenViewFactoryName(component: CompileDirectiveMetadata,
|
||||
embeddedTemplateIndex: number): string {
|
||||
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
||||
}
|
||||
|
||||
function codeGenViewEncapsulation(value: ViewEncapsulation): string {
|
||||
if (IS_DART) {
|
||||
return `${METADATA_MODULE_REF}${value}`;
|
||||
} else {
|
||||
return `${value}`;
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import {PromiseCompleter, PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||
|
||||
/**
|
||||
* A mock implemenation of {@link XHR} that allows outgoing requests to be mocked
|
||||
* A mock implementation of {@link XHR} that allows outgoing requests to be mocked
|
||||
* and responded to within a single test, without going to the network.
|
||||
*/
|
||||
export class MockXHR extends XHR {
|
||||
|
@ -11,13 +11,11 @@ import {
|
||||
KeyValueDiffers,
|
||||
defaultKeyValueDiffers
|
||||
} from './change_detection/change_detection';
|
||||
import {AppViewPool, APP_VIEW_POOL_CAPACITY} from './linker/view_pool';
|
||||
import {ResolvedMetadataCache} from 'angular2/src/core/linker/resolved_metadata_cache';
|
||||
import {AppViewManager} from './linker/view_manager';
|
||||
import {AppViewManager_} from "./linker/view_manager";
|
||||
import {AppViewManagerUtils} from './linker/view_manager_utils';
|
||||
import {ViewResolver} from './linker/view_resolver';
|
||||
import {AppViewListener} from './linker/view_listener';
|
||||
import {ProtoViewFactory} from './linker/proto_view_factory';
|
||||
import {DirectiveResolver} from './linker/directive_resolver';
|
||||
import {PipeResolver} from './linker/pipe_resolver';
|
||||
import {Compiler} from './linker/compiler';
|
||||
@ -32,12 +30,9 @@ import {DynamicComponentLoader_} from "./linker/dynamic_component_loader";
|
||||
export const APPLICATION_COMMON_PROVIDERS: Array<Type | Provider | any[]> = CONST_EXPR([
|
||||
new Provider(Compiler, {useClass: Compiler_}),
|
||||
APP_ID_RANDOM_PROVIDER,
|
||||
AppViewPool,
|
||||
new Provider(APP_VIEW_POOL_CAPACITY, {useValue: 10000}),
|
||||
ResolvedMetadataCache,
|
||||
new Provider(AppViewManager, {useClass: AppViewManager_}),
|
||||
AppViewManagerUtils,
|
||||
AppViewListener,
|
||||
ProtoViewFactory,
|
||||
ViewResolver,
|
||||
new Provider(IterableDiffers, {useValue: defaultIterableDiffers}),
|
||||
new Provider(KeyValueDiffers, {useValue: defaultKeyValueDiffers}),
|
||||
|
@ -33,11 +33,11 @@ import {
|
||||
ExceptionHandler,
|
||||
unimplemented
|
||||
} from 'angular2/src/facade/exceptions';
|
||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
||||
import {Console} from 'angular2/src/core/console';
|
||||
import {wtfLeave, wtfCreateScope, WtfScopeFn} from './profile/profile';
|
||||
import {ChangeDetectorRef} from 'angular2/src/core/change_detection/change_detector_ref';
|
||||
import {lockMode} from 'angular2/src/facade/lang';
|
||||
import {ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
|
||||
/**
|
||||
* Construct providers specific to an individual root component.
|
||||
@ -56,10 +56,10 @@ function _componentProviders(appComponentType: Type): Array<Type | Provider | an
|
||||
() => { appRef._unloadComponent(ref); })
|
||||
.then((componentRef) => {
|
||||
ref = componentRef;
|
||||
if (isPresent(componentRef.location.nativeElement)) {
|
||||
var testability = injector.getOptional(Testability);
|
||||
if (isPresent(testability)) {
|
||||
injector.get(TestabilityRegistry)
|
||||
.registerApplication(componentRef.location.nativeElement,
|
||||
injector.get(Testability));
|
||||
.registerApplication(componentRef.location.nativeElement, testability);
|
||||
}
|
||||
return componentRef;
|
||||
});
|
||||
@ -217,25 +217,36 @@ export class PlatformRef_ extends PlatformRef {
|
||||
|
||||
application(providers: Array<Type | Provider | any[]>): ApplicationRef {
|
||||
var app = this._initApp(createNgZone(), providers);
|
||||
return app;
|
||||
if (PromiseWrapper.isPromise(app)) {
|
||||
throw new BaseException(
|
||||
"Cannot use asyncronous app initializers with application. Use asyncApplication instead.");
|
||||
}
|
||||
return <ApplicationRef>app;
|
||||
}
|
||||
|
||||
asyncApplication(bindingFn: (zone: NgZone) => Promise<Array<Type | Provider | any[]>>,
|
||||
additionalProviders?: Array<Type | Provider | any[]>): Promise<ApplicationRef> {
|
||||
var zone = createNgZone();
|
||||
var completer = PromiseWrapper.completer();
|
||||
zone.run(() => {
|
||||
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type | Provider | any[]>) => {
|
||||
if (isPresent(additionalProviders)) {
|
||||
providers = ListWrapper.concat(providers, additionalProviders);
|
||||
}
|
||||
completer.resolve(this._initApp(zone, providers));
|
||||
if (bindingFn === null) {
|
||||
completer.resolve(this._initApp(zone, additionalProviders));
|
||||
} else {
|
||||
zone.run(() => {
|
||||
PromiseWrapper.then(bindingFn(zone), (providers: Array<Type | Provider | any[]>) => {
|
||||
if (isPresent(additionalProviders)) {
|
||||
providers = ListWrapper.concat(providers, additionalProviders);
|
||||
}
|
||||
let promise = this._initApp(zone, providers);
|
||||
completer.resolve(promise);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
return completer.promise;
|
||||
}
|
||||
|
||||
private _initApp(zone: NgZone, providers: Array<Type | Provider | any[]>): ApplicationRef {
|
||||
private _initApp(zone: NgZone,
|
||||
providers: Array<Type | Provider | any[]>): Promise<ApplicationRef>|
|
||||
ApplicationRef {
|
||||
var injector: Injector;
|
||||
var app: ApplicationRef;
|
||||
zone.run(() => {
|
||||
@ -259,8 +270,12 @@ export class PlatformRef_ extends PlatformRef {
|
||||
});
|
||||
app = new ApplicationRef_(this, zone, injector);
|
||||
this._applications.push(app);
|
||||
_runAppInitializers(injector);
|
||||
return app;
|
||||
var promise = _runAppInitializers(injector);
|
||||
if (promise !== null) {
|
||||
return PromiseWrapper.then(promise, (_) => app);
|
||||
} else {
|
||||
return app;
|
||||
}
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
@ -273,9 +288,22 @@ export class PlatformRef_ extends PlatformRef {
|
||||
_applicationDisposed(app: ApplicationRef): void { ListWrapper.remove(this._applications, app); }
|
||||
}
|
||||
|
||||
function _runAppInitializers(injector: Injector): void {
|
||||
function _runAppInitializers(injector: Injector): Promise<any> {
|
||||
let inits: Function[] = injector.getOptional(APP_INITIALIZER);
|
||||
if (isPresent(inits)) inits.forEach(init => init());
|
||||
let promises: Promise<any>[] = [];
|
||||
if (isPresent(inits)) {
|
||||
inits.forEach(init => {
|
||||
var retVal = init();
|
||||
if (PromiseWrapper.isPromise(retVal)) {
|
||||
promises.push(retVal);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (promises.length > 0) {
|
||||
return PromiseWrapper.all(promises);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -439,7 +467,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
|
||||
/** @internal */
|
||||
_loadComponent(ref): void {
|
||||
var appChangeDetector = internalView(ref.hostView).changeDetector;
|
||||
var appChangeDetector = (<ElementRef_>ref.location).internalElement.parentView.changeDetector;
|
||||
this._changeDetectorRefs.push(appChangeDetector.ref);
|
||||
this.tick();
|
||||
this._rootComponents.push(ref);
|
||||
@ -451,7 +479,8 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
if (!ListWrapper.contains(this._rootComponents, ref)) {
|
||||
return;
|
||||
}
|
||||
this.unregisterChangeDetector(internalView(ref.hostView).changeDetector.ref);
|
||||
this.unregisterChangeDetector(
|
||||
(<ElementRef_>ref.location).internalElement.parentView.changeDetector.ref);
|
||||
ListWrapper.remove(this._rootComponents, ref);
|
||||
}
|
||||
|
||||
@ -484,5 +513,5 @@ export class ApplicationRef_ extends ApplicationRef {
|
||||
this._platform._applicationDisposed(this);
|
||||
}
|
||||
|
||||
get componentTypes(): any[] { return this._rootComponentTypes; }
|
||||
get componentTypes(): Type[] { return this._rootComponentTypes; }
|
||||
}
|
||||
|
@ -8,14 +8,16 @@ import {Pipes} from './pipes';
|
||||
import {
|
||||
ChangeDetectionError,
|
||||
ExpressionChangedAfterItHasBeenCheckedException,
|
||||
DehydratedException
|
||||
DehydratedException,
|
||||
EventEvaluationErrorContext,
|
||||
EventEvaluationError
|
||||
} from './exceptions';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {Locals} from './parser/locals';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
import {isObservable} from './observable_facade';
|
||||
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
|
||||
|
||||
@ -38,14 +40,18 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
mode: ChangeDetectionStrategy = null;
|
||||
pipes: Pipes = null;
|
||||
propertyBindingIndex: number;
|
||||
outputSubscriptions: any[];
|
||||
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
subscriptions: any[];
|
||||
streams: any[];
|
||||
|
||||
constructor(public id: string, public dispatcher: ChangeDispatcher,
|
||||
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
|
||||
public directiveIndices: DirectiveIndex[], public strategy: ChangeDetectionStrategy) {
|
||||
dispatcher: ChangeDispatcher;
|
||||
|
||||
|
||||
constructor(public id: string, public numberOfPropertyProtoRecords: number,
|
||||
public bindingTargets: BindingTarget[], public directiveIndices: DirectiveIndex[],
|
||||
public strategy: ChangeDetectionStrategy) {
|
||||
this.ref = new ChangeDetectorRef_(this);
|
||||
}
|
||||
|
||||
@ -65,10 +71,24 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
remove(): void { this.parent.removeContentChild(this); }
|
||||
|
||||
handleEvent(eventName: string, elIndex: number, locals: Locals): boolean {
|
||||
var res = this.handleEventInternal(eventName, elIndex, locals);
|
||||
this.markPathToRootAsCheckOnce();
|
||||
return res;
|
||||
handleEvent(eventName: string, elIndex: number, event: any): boolean {
|
||||
if (!this.hydrated()) {
|
||||
this.throwDehydratedError();
|
||||
}
|
||||
try {
|
||||
var locals = new Map<string, any>();
|
||||
locals.set('$event', event);
|
||||
var res = !this.handleEventInternal(eventName, elIndex, new Locals(this.locals, locals));
|
||||
this.markPathToRootAsCheckOnce();
|
||||
return res;
|
||||
} catch (e) {
|
||||
var c = this.dispatcher.getDebugContext(null, elIndex, null);
|
||||
var context = isPresent(c) ?
|
||||
new EventEvaluationErrorContext(c.element, c.componentElement, c.context,
|
||||
c.locals, c.injector) :
|
||||
null;
|
||||
throw new EventEvaluationError(eventName, e, e.stack, context);
|
||||
}
|
||||
}
|
||||
|
||||
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
|
||||
@ -133,7 +153,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
// implementation of `hydrateDirectives`.
|
||||
hydrate(context: T, locals: Locals, directives: any, pipes: Pipes): void {
|
||||
hydrate(context: T, locals: Locals, dispatcher: ChangeDispatcher, pipes: Pipes): void {
|
||||
this.dispatcher = dispatcher;
|
||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
|
||||
this.context = context;
|
||||
|
||||
@ -143,12 +164,12 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
this.locals = locals;
|
||||
this.pipes = pipes;
|
||||
this.hydrateDirectives(directives);
|
||||
this.hydrateDirectives(dispatcher);
|
||||
this.state = ChangeDetectorState.NeverChecked;
|
||||
}
|
||||
|
||||
// Subclasses should override this method to hydrate any directives.
|
||||
hydrateDirectives(directives: any): void {}
|
||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {}
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
// implementation of `dehydrateDirectives`.
|
||||
@ -160,6 +181,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
this._unsubsribeFromObservables();
|
||||
}
|
||||
|
||||
this._unsubscribeFromOutputs();
|
||||
|
||||
this.dispatcher = null;
|
||||
this.context = null;
|
||||
this.locals = null;
|
||||
this.pipes = null;
|
||||
@ -171,6 +195,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
||||
hydrated(): boolean { return isPresent(this.context); }
|
||||
|
||||
destroyRecursive(): void {
|
||||
this.dispatcher.notifyOnDestroy();
|
||||
this.dehydrate();
|
||||
var children = this.contentChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].destroyRecursive();
|
||||
}
|
||||
children = this.viewChildren;
|
||||
for (var i = 0; i < children.length; i++) {
|
||||
children[i].destroyRecursive();
|
||||
}
|
||||
}
|
||||
|
||||
afterContentLifecycleCallbacks(): void {
|
||||
this.dispatcher.notifyAfterContentChecked();
|
||||
this.afterContentLifecycleCallbacksInternal();
|
||||
@ -224,6 +261,15 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
}
|
||||
}
|
||||
|
||||
private _unsubscribeFromOutputs(): void {
|
||||
if (isPresent(this.outputSubscriptions)) {
|
||||
for (var i = 0; i < this.outputSubscriptions.length; ++i) {
|
||||
ObservableWrapper.dispose(this.outputSubscriptions[i]);
|
||||
this.outputSubscriptions[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
observeValue(value: any, index: number): any {
|
||||
if (isObservable(value)) {
|
||||
@ -298,7 +344,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
private _throwError(exception: any, stack: any): void {
|
||||
var error;
|
||||
try {
|
||||
var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
|
||||
var c = this.dispatcher.getDebugContext(null, this._currentBinding().elementIndex, null);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||
c.injector, this._currentBinding().debug) :
|
||||
null;
|
||||
|
@ -66,8 +66,8 @@ export class ChangeDetectorJITGenerator {
|
||||
generate(): Function {
|
||||
var factorySource = `
|
||||
${this.generateSource()}
|
||||
return function(dispatcher) {
|
||||
return new ${this.typeName}(dispatcher);
|
||||
return function() {
|
||||
return new ${this.typeName}();
|
||||
}
|
||||
`;
|
||||
return new Function(this.abstractChangeDetectorVarName, this.changeDetectionUtilVarName,
|
||||
@ -77,9 +77,9 @@ export class ChangeDetectorJITGenerator {
|
||||
|
||||
generateSource(): string {
|
||||
return `
|
||||
var ${this.typeName} = function ${this.typeName}(dispatcher) {
|
||||
var ${this.typeName} = function ${this.typeName}() {
|
||||
${this.abstractChangeDetectorVarName}.call(
|
||||
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
|
||||
this, ${JSON.stringify(this.id)}, ${this.records.length},
|
||||
${this.typeName}.gen_propertyBindingTargets, ${this.typeName}.gen_directiveIndices,
|
||||
${codify(this.changeDetectionStrategy)});
|
||||
this.dehydrateDirectives(false);
|
||||
@ -199,13 +199,14 @@ export class ChangeDetectorJITGenerator {
|
||||
/** @internal */
|
||||
_maybeGenDehydrateDirectives(): string {
|
||||
var destroyPipesCode = this._names.genPipeOnDestroy();
|
||||
if (destroyPipesCode) {
|
||||
destroyPipesCode = `if (destroyPipes) { ${destroyPipesCode} }`;
|
||||
}
|
||||
var destroyDirectivesCode = this._logic.genDirectivesOnDestroy(this.directiveRecords);
|
||||
var dehydrateFieldsCode = this._names.genDehydrateFields();
|
||||
if (!destroyPipesCode && !dehydrateFieldsCode) return '';
|
||||
if (!destroyPipesCode && !destroyDirectivesCode && !dehydrateFieldsCode) return '';
|
||||
return `${this.typeName}.prototype.dehydrateDirectives = function(destroyPipes) {
|
||||
${destroyPipesCode}
|
||||
if (destroyPipes) {
|
||||
${destroyPipesCode}
|
||||
${destroyDirectivesCode}
|
||||
}
|
||||
${dehydrateFieldsCode}
|
||||
}`;
|
||||
}
|
||||
|
@ -205,4 +205,4 @@ export class ChangeDetectorRef_ extends ChangeDetectorRef {
|
||||
this._cd.mode = ChangeDetectionStrategy.CheckAlways;
|
||||
this.markForCheck();
|
||||
}
|
||||
}
|
||||
}
|
@ -103,7 +103,7 @@ export class CodegenLogicUtil {
|
||||
break;
|
||||
|
||||
case RecordType.Chain:
|
||||
rhs = 'null';
|
||||
rhs = `${getLocalName(protoRec.args[protoRec.args.length - 1])}`;
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -153,19 +153,62 @@ export class CodegenLogicUtil {
|
||||
|
||||
genHydrateDirectives(directiveRecords: DirectiveRecord[]): string {
|
||||
var res = [];
|
||||
var outputCount = 0;
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
var r = directiveRecords[i];
|
||||
res.push(`${this._names.getDirectiveName(r.directiveIndex)} = ${this._genReadDirective(i)};`);
|
||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
||||
res.push(`${dirVarName} = ${this._genReadDirective(i)};`);
|
||||
if (isPresent(r.outputs)) {
|
||||
r.outputs.forEach(output => {
|
||||
var eventHandlerExpr = this._genEventHandler(r.directiveIndex.elementIndex, output[1]);
|
||||
var statementStart =
|
||||
`this.outputSubscriptions[${outputCount++}] = ${dirVarName}.${output[0]}`;
|
||||
if (IS_DART) {
|
||||
res.push(`${statementStart}.listen(${eventHandlerExpr});`);
|
||||
} else {
|
||||
res.push(`${statementStart}.subscribe({next: ${eventHandlerExpr}});`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
if (outputCount > 0) {
|
||||
var statementStart = 'this.outputSubscriptions';
|
||||
if (IS_DART) {
|
||||
res.unshift(`${statementStart} = new List(${outputCount});`);
|
||||
} else {
|
||||
res.unshift(`${statementStart} = new Array(${outputCount});`);
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
genDirectivesOnDestroy(directiveRecords: DirectiveRecord[]): string {
|
||||
var res = [];
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
var r = directiveRecords[i];
|
||||
if (r.callOnDestroy) {
|
||||
var dirVarName = this._names.getDirectiveName(r.directiveIndex);
|
||||
res.push(`${dirVarName}.ngOnDestroy();`);
|
||||
}
|
||||
}
|
||||
return res.join("\n");
|
||||
}
|
||||
|
||||
private _genEventHandler(boundElementIndex: number, eventName: string): string {
|
||||
if (IS_DART) {
|
||||
return `(event) => this.handleEvent('${eventName}', ${boundElementIndex}, event)`;
|
||||
} else {
|
||||
return `(function(event) { return this.handleEvent('${eventName}', ${boundElementIndex}, event); }).bind(this)`;
|
||||
}
|
||||
}
|
||||
|
||||
private _genReadDirective(index: number) {
|
||||
var directiveExpr = `this.getDirectiveFor(directives, ${index})`;
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
if (this._changeDetection === ChangeDetectionStrategy.OnPushObserve) {
|
||||
return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
|
||||
return `this.observeDirective(${directiveExpr}, ${index})`;
|
||||
} else {
|
||||
return `this.getDirectiveFor(directives, ${index})`;
|
||||
return directiveExpr;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import {IterableDiffer, IterableDifferFactory} from '../differs/iterable_differs
|
||||
@CONST()
|
||||
export class DefaultIterableDifferFactory implements IterableDifferFactory {
|
||||
supports(obj: Object): boolean { return isListLikeIterable(obj); }
|
||||
create(cdRef: ChangeDetectorRef): any { return new DefaultIterableDiffer(); }
|
||||
create(cdRef: ChangeDetectorRef): DefaultIterableDiffer { return new DefaultIterableDiffer(); }
|
||||
}
|
||||
|
||||
export class DefaultIterableDiffer implements IterableDiffer {
|
||||
|
@ -9,7 +9,7 @@ import {Provider, SkipSelfMetadata, OptionalMetadata, Injectable} from 'angular2
|
||||
* respond to changes in an iterable by effecting equivalent changes in the DOM.
|
||||
*/
|
||||
export interface IterableDiffer {
|
||||
diff(object: Object): any;
|
||||
diff(object: any): any;
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ export interface IterableDiffer {
|
||||
* Provides a factory for {@link IterableDiffer}.
|
||||
*/
|
||||
export interface IterableDifferFactory {
|
||||
supports(objects: Object): boolean;
|
||||
supports(objects: any): boolean;
|
||||
create(cdRef: ChangeDetectorRef): IterableDiffer;
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ export class IterableDiffers {
|
||||
});
|
||||
}
|
||||
|
||||
find(iterable: Object): IterableDifferFactory {
|
||||
find(iterable: any): IterableDifferFactory {
|
||||
var factory = this.factories.find(f => f.supports(iterable));
|
||||
if (isPresent(factory)) {
|
||||
return factory;
|
||||
|
@ -8,7 +8,7 @@ import {Provider, SkipSelfMetadata, OptionalMetadata, Injectable} from 'angular2
|
||||
* A differ that tracks changes made to an object over time.
|
||||
*/
|
||||
export interface KeyValueDiffer {
|
||||
diff(object: Object);
|
||||
diff(object: any);
|
||||
onDestroy();
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ export interface KeyValueDiffer {
|
||||
* Provides a factory for {@link KeyValueDiffer}.
|
||||
*/
|
||||
export interface KeyValueDifferFactory {
|
||||
supports(objects: Object): boolean;
|
||||
supports(objects: any): boolean;
|
||||
create(cdRef: ChangeDetectorRef): KeyValueDiffer;
|
||||
}
|
||||
|
||||
|
@ -16,10 +16,14 @@ export class DirectiveRecord {
|
||||
callOnChanges: boolean;
|
||||
callDoCheck: boolean;
|
||||
callOnInit: boolean;
|
||||
callOnDestroy: boolean;
|
||||
changeDetection: ChangeDetectionStrategy;
|
||||
// array of [emitter property name, eventName]
|
||||
outputs: string[][];
|
||||
|
||||
constructor({directiveIndex, callAfterContentInit, callAfterContentChecked, callAfterViewInit,
|
||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, changeDetection}: {
|
||||
callAfterViewChecked, callOnChanges, callDoCheck, callOnInit, callOnDestroy,
|
||||
changeDetection, outputs}: {
|
||||
directiveIndex?: DirectiveIndex,
|
||||
callAfterContentInit?: boolean,
|
||||
callAfterContentChecked?: boolean,
|
||||
@ -28,7 +32,9 @@ export class DirectiveRecord {
|
||||
callOnChanges?: boolean,
|
||||
callDoCheck?: boolean,
|
||||
callOnInit?: boolean,
|
||||
changeDetection?: ChangeDetectionStrategy
|
||||
callOnDestroy?: boolean,
|
||||
changeDetection?: ChangeDetectionStrategy,
|
||||
outputs?: string[][]
|
||||
} = {}) {
|
||||
this.directiveIndex = directiveIndex;
|
||||
this.callAfterContentInit = normalizeBool(callAfterContentInit);
|
||||
@ -38,7 +44,9 @@ export class DirectiveRecord {
|
||||
this.callAfterViewChecked = normalizeBool(callAfterViewChecked);
|
||||
this.callDoCheck = normalizeBool(callDoCheck);
|
||||
this.callOnInit = normalizeBool(callOnInit);
|
||||
this.callOnDestroy = normalizeBool(callOnDestroy);
|
||||
this.changeDetection = changeDetection;
|
||||
this.outputs = outputs;
|
||||
}
|
||||
|
||||
isDefaultChangeDetection(): boolean {
|
||||
|
@ -11,21 +11,21 @@ import {ChangeDispatcher, ChangeDetectorGenConfig} from './interfaces';
|
||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorState} from './constants';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {ObservableWrapper} from 'angular2/src/facade/async';
|
||||
|
||||
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
values: any[];
|
||||
changes: any[];
|
||||
localPipes: any[];
|
||||
prevContexts: any[];
|
||||
directives: any = null;
|
||||
|
||||
constructor(id: string, dispatcher: ChangeDispatcher, numberOfPropertyProtoRecords: number,
|
||||
constructor(id: string, numberOfPropertyProtoRecords: number,
|
||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
||||
strategy: ChangeDetectionStrategy, private _records: ProtoRecord[],
|
||||
private _eventBindings: EventBinding[], private _directiveRecords: DirectiveRecord[],
|
||||
private _genConfig: ChangeDetectorGenConfig) {
|
||||
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
|
||||
strategy);
|
||||
super(id, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, strategy);
|
||||
var len = _records.length + 1;
|
||||
this.values = ListWrapper.createFixedSize(len);
|
||||
this.localPipes = ListWrapper.createFixedSize(len);
|
||||
@ -104,24 +104,43 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
return this._eventBindings.filter(eb => eb.eventName == eventName && eb.elIndex === elIndex);
|
||||
}
|
||||
|
||||
hydrateDirectives(directives: any): void {
|
||||
hydrateDirectives(dispatcher: ChangeDispatcher): void {
|
||||
this.values[0] = this.context;
|
||||
this.directives = directives;
|
||||
this.dispatcher = dispatcher;
|
||||
|
||||
if (this.strategy === ChangeDetectionStrategy.OnPushObserve) {
|
||||
for (var i = 0; i < this.directiveIndices.length; ++i) {
|
||||
var index = this.directiveIndices[i];
|
||||
super.observeDirective(directives.getDirectiveFor(index), i);
|
||||
super.observeDirective(this._getDirectiveFor(index), i);
|
||||
}
|
||||
}
|
||||
this.outputSubscriptions = [];
|
||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
||||
var r = this._directiveRecords[i];
|
||||
if (isPresent(r.outputs)) {
|
||||
r.outputs.forEach(output => {
|
||||
var eventHandler =
|
||||
<any>this._createEventHandler(r.directiveIndex.elementIndex, output[1]);
|
||||
var directive = this._getDirectiveFor(r.directiveIndex);
|
||||
var getter = reflector.getter(output[0]);
|
||||
this.outputSubscriptions.push(
|
||||
ObservableWrapper.subscribe(getter(directive), eventHandler));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _createEventHandler(boundElementIndex: number, eventName: string): Function {
|
||||
return (event) => this.handleEvent(eventName, boundElementIndex, event);
|
||||
}
|
||||
|
||||
|
||||
dehydrateDirectives(destroyPipes: boolean) {
|
||||
if (destroyPipes) {
|
||||
this._destroyPipes();
|
||||
this._destroyDirectives();
|
||||
}
|
||||
this.values[0] = null;
|
||||
this.directives = null;
|
||||
ListWrapper.fill(this.values, ChangeDetectionUtil.uninitialized, 1);
|
||||
ListWrapper.fill(this.changes, false);
|
||||
ListWrapper.fill(this.localPipes, null);
|
||||
@ -137,6 +156,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyDirectives() {
|
||||
for (var i = 0; i < this._directiveRecords.length; ++i) {
|
||||
var record = this._directiveRecords[i];
|
||||
if (record.callOnDestroy) {
|
||||
this._getDirectiveFor(record.directiveIndex).ngOnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkNoChanges(): void { this.runDetectChanges(true); }
|
||||
|
||||
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
||||
@ -241,12 +270,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _getDirectiveFor(directiveIndex) {
|
||||
return this.directives.getDirectiveFor(directiveIndex);
|
||||
private _getDirectiveFor(directiveIndex: DirectiveIndex) {
|
||||
return this.dispatcher.getDirectiveFor(directiveIndex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _getDetectorFor(directiveIndex) { return this.directives.getDetectorFor(directiveIndex); }
|
||||
private _getDetectorFor(directiveIndex: DirectiveIndex) {
|
||||
return this.dispatcher.getDetectorFor(directiveIndex);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
private _check(proto: ProtoRecord, throwOnChange: boolean, values: any[],
|
||||
|
@ -91,5 +91,22 @@ export class ChangeDetectionError extends WrappedException {
|
||||
* This is an internal Angular error.
|
||||
*/
|
||||
export class DehydratedException extends BaseException {
|
||||
constructor() { super('Attempt to detect changes on a dehydrated detector.'); }
|
||||
constructor() { super('Attempt to use a dehydrated detector.'); }
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an exception thrown by an event handler.
|
||||
*/
|
||||
export class EventEvaluationError extends WrappedException {
|
||||
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
|
||||
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Error context included when an event handler throws an exception.
|
||||
*/
|
||||
export class EventEvaluationErrorContext {
|
||||
constructor(public element: any, public componentElement: any, public context: any,
|
||||
public locals: any, public injector: any) {}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Locals} from './parser/locals';
|
||||
import {BindingTarget, BindingRecord} from './binding_record';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
import {ChangeDetectionStrategy} from './constants';
|
||||
import {ChangeDetectorRef} from './change_detector_ref';
|
||||
|
||||
@ -10,11 +10,14 @@ export class DebugContext {
|
||||
}
|
||||
|
||||
export interface ChangeDispatcher {
|
||||
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
|
||||
getDebugContext(appElement: any, elementIndex: number, directiveIndex: number): DebugContext;
|
||||
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
|
||||
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
|
||||
notifyAfterContentChecked(): void;
|
||||
notifyAfterViewChecked(): void;
|
||||
notifyOnDestroy(): void;
|
||||
getDetectorFor(directiveIndex: DirectiveIndex): ChangeDetector;
|
||||
getDirectiveFor(directiveIndex: DirectiveIndex): any;
|
||||
}
|
||||
|
||||
export interface ChangeDetector {
|
||||
@ -27,16 +30,18 @@ export interface ChangeDetector {
|
||||
removeContentChild(cd: ChangeDetector): void;
|
||||
removeViewChild(cd: ChangeDetector): void;
|
||||
remove(): void;
|
||||
hydrate(context: any, locals: Locals, directives: any, pipes: any): void;
|
||||
hydrate(context: any, locals: Locals, dispatcher: ChangeDispatcher, pipes: any): void;
|
||||
dehydrate(): void;
|
||||
markPathToRootAsCheckOnce(): void;
|
||||
|
||||
handleEvent(eventName: string, elIndex: number, locals: Locals);
|
||||
handleEvent(eventName: string, elIndex: number, event: any);
|
||||
detectChanges(): void;
|
||||
checkNoChanges(): void;
|
||||
destroyRecursive(): void;
|
||||
markAsCheckOnce(): void;
|
||||
}
|
||||
|
||||
export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; }
|
||||
export interface ProtoChangeDetector { instantiate(): ChangeDetector; }
|
||||
|
||||
export class ChangeDetectorGenConfig {
|
||||
constructor(public genDebugInfo: boolean, public logBindingUpdate: boolean,
|
||||
|
@ -3,11 +3,11 @@ library change_detection.jit_proto_change_detector;
|
||||
import 'interfaces.dart' show ChangeDetector, ProtoChangeDetector;
|
||||
|
||||
class JitProtoChangeDetector implements ProtoChangeDetector {
|
||||
JitProtoChangeDetector(definition) : super();
|
||||
JitProtoChangeDetector(definition);
|
||||
|
||||
static bool isSupported() => false;
|
||||
|
||||
ChangeDetector instantiate(dispatcher) {
|
||||
ChangeDetector instantiate() {
|
||||
throw "Jit Change Detection not supported in Dart";
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
|
||||
|
||||
static isSupported(): boolean { return true; }
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector { return this._factory(dispatcher); }
|
||||
instantiate(): ChangeDetector { return this._factory(); }
|
||||
|
||||
/** @internal */
|
||||
_createFactory(definition: ChangeDetectorDefinition) {
|
||||
|
@ -41,5 +41,5 @@ export class Locals {
|
||||
}
|
||||
}
|
||||
|
||||
clearValues(): void { MapWrapper.clearValues(this.current); }
|
||||
clearLocalValues(): void { MapWrapper.clearValues(this.current); }
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
library angular2.src.change_detection.pregen_proto_change_detector;
|
||||
|
||||
import 'package:angular2/src/core/change_detection/interfaces.dart';
|
||||
import 'package:angular2/src/facade/lang.dart' show looseIdentical;
|
||||
|
||||
export 'dart:core' show List;
|
||||
export 'package:angular2/src/core/change_detection/abstract_change_detector.dart'
|
||||
show AbstractChangeDetector;
|
||||
@ -20,34 +17,3 @@ export 'package:angular2/src/core/change_detection/proto_record.dart'
|
||||
export 'package:angular2/src/core/change_detection/change_detection_util.dart'
|
||||
show ChangeDetectionUtil;
|
||||
export 'package:angular2/src/facade/lang.dart' show assertionsEnabled, looseIdentical;
|
||||
|
||||
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
|
||||
ChangeDetectorDefinition definition);
|
||||
|
||||
typedef ChangeDetector InstantiateMethod(dynamic dispatcher);
|
||||
|
||||
/// Implementation of [ProtoChangeDetector] for use by pre-generated change
|
||||
/// detectors in Angular 2 Dart.
|
||||
/// Classes generated by the `TemplateCompiler` use this. The `export`s above
|
||||
/// allow the generated code to `import` a single library and get all
|
||||
/// dependencies.
|
||||
class PregenProtoChangeDetector extends ProtoChangeDetector {
|
||||
/// The [ChangeDetectorDefinition#id]. Strictly informational.
|
||||
final String id;
|
||||
|
||||
/// Closure used to generate an actual [ChangeDetector].
|
||||
final InstantiateMethod _instantiateMethod;
|
||||
|
||||
/// Internal ctor.
|
||||
PregenProtoChangeDetector._(this.id, this._instantiateMethod);
|
||||
|
||||
static bool isSupported() => true;
|
||||
|
||||
factory PregenProtoChangeDetector(
|
||||
InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) {
|
||||
return new PregenProtoChangeDetector._(def.id, instantiateMethod);
|
||||
}
|
||||
|
||||
@override
|
||||
instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher);
|
||||
}
|
||||
|
@ -1,14 +1 @@
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import {ProtoChangeDetector, ChangeDetector} from './interfaces';
|
||||
import {coalesce} from './coalesce';
|
||||
|
||||
export {Function as PregenProtoChangeDetectorFactory};
|
||||
|
||||
export class PregenProtoChangeDetector implements ProtoChangeDetector {
|
||||
static isSupported(): boolean { return false; }
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
throw new BaseException('Pregen change detection not supported in Js');
|
||||
}
|
||||
}
|
||||
// empty file as we only need the dart version
|
@ -54,12 +54,11 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
|
||||
this._directiveIndices = this._definition.directiveRecords.map(d => d.directiveIndex);
|
||||
}
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
instantiate(): ChangeDetector {
|
||||
return new DynamicChangeDetector(
|
||||
this._definition.id, dispatcher, this._propertyBindingRecords.length,
|
||||
this._propertyBindingTargets, this._directiveIndices, this._definition.strategy,
|
||||
this._propertyBindingRecords, this._eventBindingRecords, this._definition.directiveRecords,
|
||||
this._definition.genConfig);
|
||||
this._definition.id, this._propertyBindingRecords.length, this._propertyBindingTargets,
|
||||
this._directiveIndices, this._definition.strategy, this._propertyBindingRecords,
|
||||
this._eventBindingRecords, this._definition.directiveRecords, this._definition.genConfig);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {Type, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, MapWrapper, Predicate} from 'angular2/src/facade/collection';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ElementInjector} from 'angular2/src/core/linker/element_injector';
|
||||
import {AppView, ViewType} from 'angular2/src/core/linker/view';
|
||||
import {internalView} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
import {AppElement} from 'angular2/src/core/linker/element';
|
||||
import {AppView} from 'angular2/src/core/linker/view';
|
||||
import {ElementRef, ElementRef_} from 'angular2/src/core/linker/element_ref';
|
||||
|
||||
/**
|
||||
@ -103,79 +103,68 @@ export abstract class DebugElement {
|
||||
}
|
||||
|
||||
export class DebugElement_ extends DebugElement {
|
||||
/** @internal */
|
||||
_elementInjector: ElementInjector;
|
||||
|
||||
constructor(private _parentView: AppView, private _boundElementIndex: number) {
|
||||
super();
|
||||
this._elementInjector = this._parentView.elementInjectors[this._boundElementIndex];
|
||||
}
|
||||
constructor(private _appElement: AppElement) { super(); }
|
||||
|
||||
get componentInstance(): any {
|
||||
if (!isPresent(this._elementInjector)) {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return null;
|
||||
}
|
||||
return this._elementInjector.getComponent();
|
||||
return this._appElement.getComponent();
|
||||
}
|
||||
|
||||
get nativeElement(): any { return this.elementRef.nativeElement; }
|
||||
|
||||
get elementRef(): ElementRef { return this._parentView.elementRefs[this._boundElementIndex]; }
|
||||
get elementRef(): ElementRef { return this._appElement.ref; }
|
||||
|
||||
getDirectiveInstance(directiveIndex: number): any {
|
||||
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
return this._appElement.getDirectiveAtIndex(directiveIndex);
|
||||
}
|
||||
|
||||
get children(): DebugElement[] {
|
||||
return this._getChildElements(this._parentView, this._boundElementIndex);
|
||||
return this._getChildElements(this._appElement.parentView, this._appElement);
|
||||
}
|
||||
|
||||
get componentViewChildren(): DebugElement[] {
|
||||
var shadowView = this._parentView.getNestedView(this._boundElementIndex);
|
||||
|
||||
if (!isPresent(shadowView) || shadowView.proto.type !== ViewType.COMPONENT) {
|
||||
if (!isPresent(this._appElement.componentView)) {
|
||||
// The current element is not a component.
|
||||
return [];
|
||||
}
|
||||
|
||||
return this._getChildElements(shadowView, null);
|
||||
return this._getChildElements(this._appElement.componentView, null);
|
||||
}
|
||||
|
||||
triggerEventHandler(eventName: string, eventObj: Event): void {
|
||||
this._parentView.triggerEventHandlers(eventName, eventObj, this._boundElementIndex);
|
||||
this._appElement.parentView.triggerEventHandlers(eventName, eventObj,
|
||||
this._appElement.proto.index);
|
||||
}
|
||||
|
||||
hasDirective(type: Type): boolean {
|
||||
if (!isPresent(this._elementInjector)) {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return false;
|
||||
}
|
||||
return this._elementInjector.hasDirective(type);
|
||||
return this._appElement.hasDirective(type);
|
||||
}
|
||||
|
||||
inject(type: Type): any {
|
||||
if (!isPresent(this._elementInjector)) {
|
||||
if (!isPresent(this._appElement)) {
|
||||
return null;
|
||||
}
|
||||
return this._elementInjector.get(type);
|
||||
return this._appElement.get(type);
|
||||
}
|
||||
|
||||
getLocal(name: string): any { return this._parentView.locals.get(name); }
|
||||
getLocal(name: string): any { return this._appElement.parentView.locals.get(name); }
|
||||
|
||||
/** @internal */
|
||||
_getChildElements(view: AppView, parentBoundElementIndex: number): DebugElement[] {
|
||||
_getChildElements(view: AppView, parentAppElement: AppElement): DebugElement[] {
|
||||
var els = [];
|
||||
var parentElementBinder = null;
|
||||
if (isPresent(parentBoundElementIndex)) {
|
||||
parentElementBinder = view.proto.elementBinders[parentBoundElementIndex - view.elementOffset];
|
||||
}
|
||||
for (var i = 0; i < view.proto.elementBinders.length; ++i) {
|
||||
var binder = view.proto.elementBinders[i];
|
||||
if (binder.parent == parentElementBinder) {
|
||||
els.push(new DebugElement_(view, view.elementOffset + i));
|
||||
for (var i = 0; i < view.appElements.length; ++i) {
|
||||
var appEl = view.appElements[i];
|
||||
if (appEl.parent == parentAppElement) {
|
||||
els.push(new DebugElement_(appEl));
|
||||
|
||||
var views = view.viewContainers[view.elementOffset + i];
|
||||
var views = appEl.nestedViews;
|
||||
if (isPresent(views)) {
|
||||
views.views.forEach(
|
||||
views.forEach(
|
||||
(nextView) => { els = els.concat(this._getChildElements(nextView, null)); });
|
||||
}
|
||||
}
|
||||
@ -191,8 +180,7 @@ export class DebugElement_ extends DebugElement {
|
||||
* @return {DebugElement}
|
||||
*/
|
||||
export function inspectElement(elementRef: ElementRef): DebugElement {
|
||||
return new DebugElement_(internalView((<ElementRef_>elementRef).parentView),
|
||||
(<ElementRef_>elementRef).boundElementIndex);
|
||||
return new DebugElement_((<ElementRef_>elementRef).internalElement);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,8 +222,10 @@ export class NoAnnotationError extends BaseException {
|
||||
signature.push(parameter.map(stringify).join(' '));
|
||||
}
|
||||
}
|
||||
return "Cannot resolve all parameters for " + stringify(typeOrFunc) + "(" +
|
||||
signature.join(', ') + "). " + 'Make sure they all have valid type or annotations.';
|
||||
return "Cannot resolve all parameters for '" + stringify(typeOrFunc) + "'(" +
|
||||
signature.join(', ') + "). " +
|
||||
"Make sure that all the parameters are decorated with Inject or have valid type annotations and that '" +
|
||||
stringify(typeOrFunc) + "' is decorated with Injectable.";
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,7 +143,7 @@ export class ProtoInjectorInlineStrategy implements ProtoInjectorStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): any {
|
||||
getProviderAtIndex(index: number): ResolvedProvider {
|
||||
if (index == 0) return this.provider0;
|
||||
if (index == 1) return this.provider1;
|
||||
if (index == 2) return this.provider2;
|
||||
@ -181,7 +181,7 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||
}
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): any {
|
||||
getProviderAtIndex(index: number): ResolvedProvider {
|
||||
if (index < 0 || index >= this.providers.length) {
|
||||
throw new OutOfBoundsError(index);
|
||||
}
|
||||
@ -194,6 +194,11 @@ export class ProtoInjectorDynamicStrategy implements ProtoInjectorStrategy {
|
||||
}
|
||||
|
||||
export class ProtoInjector {
|
||||
static fromResolvedProviders(providers: ResolvedProvider[]): ProtoInjector {
|
||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||
return new ProtoInjector(bd);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_strategy: ProtoInjectorStrategy;
|
||||
numberOfProviders: number;
|
||||
@ -205,7 +210,9 @@ export class ProtoInjector {
|
||||
new ProtoInjectorInlineStrategy(this, bwv);
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): any { return this._strategy.getProviderAtIndex(index); }
|
||||
getProviderAtIndex(index: number): ResolvedProvider {
|
||||
return this._strategy.getProviderAtIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -215,7 +222,6 @@ export interface InjectorStrategy {
|
||||
getObjAtIndex(index: number): any;
|
||||
getMaxNumberOfObjects(): number;
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void;
|
||||
resetConstructionCounter(): void;
|
||||
instantiateProvider(provider: ResolvedProvider, visibility: Visibility): any;
|
||||
}
|
||||
@ -240,12 +246,6 @@ export class InjectorInlineStrategy implements InjectorStrategy {
|
||||
return this.injector._new(provider, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void {
|
||||
var inj = this.injector;
|
||||
inj._parent = parent;
|
||||
inj._isHost = isHost;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||
var p = this.protoStrategy;
|
||||
var inj = this.injector;
|
||||
@ -346,12 +346,6 @@ export class InjectorDynamicStrategy implements InjectorStrategy {
|
||||
return this.injector._new(provider, visibility);
|
||||
}
|
||||
|
||||
attach(parent: Injector, isHost: boolean): void {
|
||||
var inj = this.injector;
|
||||
inj._parent = parent;
|
||||
inj._isHost = isHost;
|
||||
}
|
||||
|
||||
getObjByKeyId(keyId: number, visibility: Visibility): any {
|
||||
var p = this.protoStrategy;
|
||||
|
||||
@ -516,9 +510,7 @@ export class Injector {
|
||||
* ```
|
||||
*/
|
||||
static fromResolvedProviders(providers: ResolvedProvider[]): Injector {
|
||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||
var proto = new ProtoInjector(bd);
|
||||
return new Injector(proto, null, null);
|
||||
return new Injector(ProtoInjector.fromResolvedProviders(providers));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -531,8 +523,6 @@ export class Injector {
|
||||
/** @internal */
|
||||
_strategy: InjectorStrategy;
|
||||
/** @internal */
|
||||
_isHost: boolean = false;
|
||||
/** @internal */
|
||||
_constructionCounter: number = 0;
|
||||
/** @internal */
|
||||
public _proto: any /* ProtoInjector */;
|
||||
@ -542,6 +532,7 @@ export class Injector {
|
||||
* Private
|
||||
*/
|
||||
constructor(_proto: any /* ProtoInjector */, _parent: Injector = null,
|
||||
private _isHostBoundary: boolean = false,
|
||||
private _depProvider: any /* DependencyProvider */ = null,
|
||||
private _debugContext: Function = null) {
|
||||
this._proto = _proto;
|
||||
@ -549,6 +540,12 @@ export class Injector {
|
||||
this._strategy = _proto._strategy.createInjectorStrategy(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this injector is a boundary to a host.
|
||||
* @internal
|
||||
*/
|
||||
get hostBoundary() { return this._isHostBoundary; }
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
@ -692,7 +689,7 @@ export class Injector {
|
||||
createChildFromResolved(providers: ResolvedProvider[]): Injector {
|
||||
var bd = providers.map(b => new ProviderWithVisibility(b, Visibility.Public));
|
||||
var proto = new ProtoInjector(bd);
|
||||
var inj = new Injector(proto, null, null);
|
||||
var inj = new Injector(proto);
|
||||
inj._parent = this;
|
||||
return inj;
|
||||
}
|
||||
@ -935,7 +932,7 @@ export class Injector {
|
||||
var inj: Injector = this;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
if (inj._isHost) {
|
||||
if (inj._isHostBoundary) {
|
||||
return this._getPrivateDependency(key, optional, inj);
|
||||
} else {
|
||||
inj = inj._parent;
|
||||
@ -946,7 +943,7 @@ export class Injector {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
|
||||
if (isPresent(inj._parent) && inj._isHost) {
|
||||
if (isPresent(inj._parent) && inj._isHostBoundary) {
|
||||
return this._getPrivateDependency(key, optional, inj);
|
||||
} else {
|
||||
inj = inj._parent;
|
||||
@ -968,7 +965,7 @@ export class Injector {
|
||||
var inj: Injector = this;
|
||||
|
||||
if (lowerBoundVisibility instanceof SkipSelfMetadata) {
|
||||
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
inj = inj._parent;
|
||||
}
|
||||
|
||||
@ -976,7 +973,7 @@ export class Injector {
|
||||
var obj = inj._strategy.getObjByKeyId(key.id, providerVisibility);
|
||||
if (obj !== UNDEFINED) return obj;
|
||||
|
||||
providerVisibility = inj._isHost ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
providerVisibility = inj._isHostBoundary ? Visibility.PublicAndPrivate : Visibility.Public;
|
||||
inj = inj._parent;
|
||||
}
|
||||
|
||||
|
@ -161,7 +161,7 @@ export class Provider {
|
||||
* expect(injector.get(String)).toEqual('Value: 3');
|
||||
* ```
|
||||
*
|
||||
* Used in conjuction with dependencies.
|
||||
* Used in conjunction with dependencies.
|
||||
*/
|
||||
useFactory: Function;
|
||||
|
||||
@ -211,7 +211,7 @@ export class Provider {
|
||||
* Creates multiple providers matching the same token (a multi-provider).
|
||||
*
|
||||
* Multi-providers are used for creating pluggable service, where the system comes
|
||||
* with some default providers, and the user can register additonal providers.
|
||||
* with some default providers, and the user can register additional providers.
|
||||
* The combination of the default providers and the additional providers will be
|
||||
* used to drive the behavior of the system.
|
||||
*
|
||||
@ -538,50 +538,62 @@ export function resolveFactory(provider: Provider): ResolvedFactory {
|
||||
* convenience provider syntax.
|
||||
*/
|
||||
export function resolveProvider(provider: Provider): ResolvedProvider {
|
||||
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], false);
|
||||
return new ResolvedProvider_(Key.get(provider.token), [resolveFactory(provider)], provider.multi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a list of Providers.
|
||||
*/
|
||||
export function resolveProviders(providers: Array<Type | Provider | any[]>): ResolvedProvider[] {
|
||||
var normalized = _createListOfProviders(_normalizeProviders(
|
||||
providers, new Map<number, _NormalizedProvider | _NormalizedProvider[]>()));
|
||||
return normalized.map(b => {
|
||||
if (b instanceof _NormalizedProvider) {
|
||||
return new ResolvedProvider_(b.key, [b.resolvedFactory], false);
|
||||
|
||||
} else {
|
||||
var arr = <_NormalizedProvider[]>b;
|
||||
return new ResolvedProvider_(arr[0].key, arr.map(_ => _.resolvedFactory), true);
|
||||
}
|
||||
});
|
||||
var normalized = _normalizeProviders(providers, []);
|
||||
var resolved = normalized.map(resolveProvider);
|
||||
return MapWrapper.values(mergeResolvedProviders(resolved, new Map<number, ResolvedProvider>()));
|
||||
}
|
||||
|
||||
/**
|
||||
* The algorithm works as follows:
|
||||
*
|
||||
* [Provider] -> [_NormalizedProvider|[_NormalizedProvider]] -> [ResolvedProvider]
|
||||
*
|
||||
* _NormalizedProvider is essentially a resolved provider before it was grouped by key.
|
||||
* Merges a list of ResolvedProviders into a list where
|
||||
* each key is contained exactly once and multi providers
|
||||
* have been merged.
|
||||
*/
|
||||
class _NormalizedProvider {
|
||||
constructor(public key: Key, public resolvedFactory: ResolvedFactory) {}
|
||||
}
|
||||
|
||||
function _createListOfProviders(flattenedProviders: Map<number, any>): any[] {
|
||||
return MapWrapper.values(flattenedProviders);
|
||||
export function mergeResolvedProviders(
|
||||
providers: ResolvedProvider[],
|
||||
normalizedProvidersMap: Map<number, ResolvedProvider>): Map<number, ResolvedProvider> {
|
||||
for (var i = 0; i < providers.length; i++) {
|
||||
var provider = providers[i];
|
||||
var existing = normalizedProvidersMap.get(provider.key.id);
|
||||
if (isPresent(existing)) {
|
||||
if (provider.multiProvider !== existing.multiProvider) {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existing, provider);
|
||||
}
|
||||
if (provider.multiProvider) {
|
||||
for (var j = 0; j < provider.resolvedFactories.length; j++) {
|
||||
existing.resolvedFactories.push(provider.resolvedFactories[j]);
|
||||
}
|
||||
} else {
|
||||
normalizedProvidersMap.set(provider.key.id, provider);
|
||||
}
|
||||
} else {
|
||||
var resolvedProvider;
|
||||
if (provider.multiProvider) {
|
||||
resolvedProvider = new ResolvedProvider_(
|
||||
provider.key, ListWrapper.clone(provider.resolvedFactories), provider.multiProvider);
|
||||
} else {
|
||||
resolvedProvider = provider;
|
||||
}
|
||||
normalizedProvidersMap.set(provider.key.id, resolvedProvider);
|
||||
}
|
||||
}
|
||||
return normalizedProvidersMap;
|
||||
}
|
||||
|
||||
function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder | any[]>,
|
||||
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>):
|
||||
Map<number, _NormalizedProvider | _NormalizedProvider[]> {
|
||||
res: Provider[]): Provider[] {
|
||||
providers.forEach(b => {
|
||||
if (b instanceof Type) {
|
||||
_normalizeProvider(provide(b, {useClass: b}), res);
|
||||
res.push(provide(b, {useClass: b}));
|
||||
|
||||
} else if (b instanceof Provider) {
|
||||
_normalizeProvider(b, res);
|
||||
res.push(b);
|
||||
|
||||
} else if (b instanceof Array) {
|
||||
_normalizeProviders(b, res);
|
||||
@ -597,36 +609,6 @@ function _normalizeProviders(providers: Array<Type | Provider | ProviderBuilder
|
||||
return res;
|
||||
}
|
||||
|
||||
function _normalizeProvider(b: Provider,
|
||||
res: Map<number, _NormalizedProvider | _NormalizedProvider[]>): void {
|
||||
var key = Key.get(b.token);
|
||||
var factory = resolveFactory(b);
|
||||
var normalized = new _NormalizedProvider(key, factory);
|
||||
|
||||
if (b.multi) {
|
||||
var existingProvider = res.get(key.id);
|
||||
|
||||
if (existingProvider instanceof Array) {
|
||||
existingProvider.push(normalized);
|
||||
|
||||
} else if (isBlank(existingProvider)) {
|
||||
res.set(key.id, [normalized]);
|
||||
|
||||
} else {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
|
||||
}
|
||||
|
||||
} else {
|
||||
var existingProvider = res.get(key.id);
|
||||
|
||||
if (existingProvider instanceof Array) {
|
||||
throw new MixingMultiProvidersWithRegularProvidersError(existingProvider, b);
|
||||
}
|
||||
|
||||
res.set(key.id, normalized);
|
||||
}
|
||||
}
|
||||
|
||||
function _constructDependencies(factoryFunction: Function, dependencies: any[]): Dependency[] {
|
||||
if (isBlank(dependencies)) {
|
||||
return _dependenciesFor(factoryFunction);
|
||||
|
@ -17,6 +17,6 @@ export {QueryList} from './linker/query_list';
|
||||
export {DynamicComponentLoader} from './linker/dynamic_component_loader';
|
||||
export {ElementRef} from './linker/element_ref';
|
||||
export {TemplateRef} from './linker/template_ref';
|
||||
export {ViewRef, HostViewRef, ProtoViewRef} from './linker/view_ref';
|
||||
export {EmbeddedViewRef, HostViewRef, ViewRef, HostViewFactoryRef} from './linker/view_ref';
|
||||
export {ViewContainerRef} from './linker/view_container_ref';
|
||||
export {ComponentRef} from './linker/dynamic_component_loader';
|
@ -1,12 +1,12 @@
|
||||
import {ProtoViewRef} from 'angular2/src/core/linker/view_ref';
|
||||
import {ProtoViewFactory} from 'angular2/src/core/linker/proto_view_factory';
|
||||
import {HostViewFactoryRef} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
import {Type, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
|
||||
import {HostViewFactory} from 'angular2/src/core/linker/view';
|
||||
import {HostViewFactoryRef_} from 'angular2/src/core/linker/view_ref';
|
||||
|
||||
/**
|
||||
* Low-level service for compiling {@link Component}s into {@link ProtoViewRef ProtoViews}s, which
|
||||
@ -16,37 +16,25 @@ import {CompiledHostTemplate} from 'angular2/src/core/linker/template_commands';
|
||||
* both compiles and instantiates a Component.
|
||||
*/
|
||||
export abstract class Compiler {
|
||||
abstract compileInHost(componentType: Type): Promise<ProtoViewRef>;
|
||||
abstract compileInHost(componentType: Type): Promise<HostViewFactoryRef>;
|
||||
abstract clearCache();
|
||||
}
|
||||
|
||||
function _isCompiledHostTemplate(type: any): boolean {
|
||||
return type instanceof CompiledHostTemplate;
|
||||
function isHostViewFactory(type: any): boolean {
|
||||
return type instanceof HostViewFactory;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class Compiler_ extends Compiler {
|
||||
constructor(private _protoViewFactory: ProtoViewFactory) { super(); }
|
||||
|
||||
compileInHost(componentType: Type): Promise<ProtoViewRef> {
|
||||
compileInHost(componentType: Type): Promise<HostViewFactoryRef_> {
|
||||
var metadatas = reflector.annotations(componentType);
|
||||
var compiledHostTemplate = metadatas.find(_isCompiledHostTemplate);
|
||||
var hostViewFactory = metadatas.find(isHostViewFactory);
|
||||
|
||||
if (isBlank(compiledHostTemplate)) {
|
||||
throw new BaseException(
|
||||
`No precompiled template for component ${stringify(componentType)} found`);
|
||||
if (isBlank(hostViewFactory)) {
|
||||
throw new BaseException(`No precompiled component ${stringify(componentType)} found`);
|
||||
}
|
||||
return PromiseWrapper.resolve(this._createProtoView(compiledHostTemplate));
|
||||
return PromiseWrapper.resolve(new HostViewFactoryRef_(hostViewFactory));
|
||||
}
|
||||
|
||||
private _createProtoView(compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
|
||||
return this._protoViewFactory.createHost(compiledHostTemplate).ref;
|
||||
}
|
||||
|
||||
clearCache() { this._protoViewFactory.clearCache(); }
|
||||
}
|
||||
|
||||
export function internalCreateProtoView(compiler: Compiler,
|
||||
compiledHostTemplate: CompiledHostTemplate): ProtoViewRef {
|
||||
return (<any>compiler)._createProtoView(compiledHostTemplate);
|
||||
clearCache() {}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import {resolveForwardRef, Injectable} from 'angular2/src/core/di';
|
||||
import {Type, isPresent, isBlank, stringify} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import {
|
||||
DirectiveMetadata,
|
||||
ComponentMetadata,
|
||||
@ -38,7 +39,7 @@ export class DirectiveResolver {
|
||||
var metadata = typeMetadata.find(_isDirectiveMetadata);
|
||||
if (isPresent(metadata)) {
|
||||
var propertyMetadata = reflector.propMetadata(type);
|
||||
return this._mergeWithPropertyMetadata(metadata, propertyMetadata);
|
||||
return this._mergeWithPropertyMetadata(metadata, propertyMetadata, type);
|
||||
}
|
||||
}
|
||||
|
||||
@ -46,7 +47,8 @@ export class DirectiveResolver {
|
||||
}
|
||||
|
||||
private _mergeWithPropertyMetadata(dm: DirectiveMetadata,
|
||||
propertyMetadata: {[key: string]: any[]}): DirectiveMetadata {
|
||||
propertyMetadata: {[key: string]: any[]},
|
||||
directiveType: Type): DirectiveMetadata {
|
||||
var inputs = [];
|
||||
var outputs = [];
|
||||
var host: {[key: string]: string} = {};
|
||||
@ -100,13 +102,27 @@ export class DirectiveResolver {
|
||||
}
|
||||
});
|
||||
});
|
||||
return this._merge(dm, inputs, outputs, host, queries);
|
||||
return this._merge(dm, inputs, outputs, host, queries, directiveType);
|
||||
}
|
||||
|
||||
private _merge(dm: DirectiveMetadata, inputs: string[], outputs: string[],
|
||||
host: {[key: string]: string}, queries: {[key: string]: any}): DirectiveMetadata {
|
||||
host: {[key: string]: string}, queries: {[key: string]: any},
|
||||
directiveType: Type): DirectiveMetadata {
|
||||
var mergedInputs = isPresent(dm.inputs) ? ListWrapper.concat(dm.inputs, inputs) : inputs;
|
||||
var mergedOutputs = isPresent(dm.outputs) ? ListWrapper.concat(dm.outputs, outputs) : outputs;
|
||||
|
||||
var mergedOutputs;
|
||||
if (isPresent(dm.outputs)) {
|
||||
dm.outputs.forEach((propName: string) => {
|
||||
if (ListWrapper.contains(outputs, propName)) {
|
||||
throw new BaseException(
|
||||
`Output event '${propName}' defined multiple times in '${stringify(directiveType)}'`);
|
||||
}
|
||||
});
|
||||
mergedOutputs = ListWrapper.concat(dm.outputs, outputs);
|
||||
} else {
|
||||
mergedOutputs = outputs;
|
||||
}
|
||||
|
||||
var mergedHost = isPresent(dm.host) ? StringMapWrapper.merge(dm.host, host) : host;
|
||||
var mergedQueries =
|
||||
isPresent(dm.queries) ? StringMapWrapper.merge(dm.queries, queries) : queries;
|
||||
@ -138,3 +154,5 @@ export class DirectiveResolver {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_DIRECTIVE_RESOLVER = new DirectiveResolver();
|
||||
|
@ -3,8 +3,8 @@ import {Compiler} from './compiler';
|
||||
import {isType, Type, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
import {Promise} from 'angular2/src/facade/async';
|
||||
import {AppViewManager} from 'angular2/src/core/linker/view_manager';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {ViewRef, HostViewRef} from './view_ref';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {HostViewRef} from './view_ref';
|
||||
|
||||
/**
|
||||
* Represents an instance of a Component created via {@link DynamicComponentLoader}.
|
||||
@ -42,7 +42,9 @@ export abstract class ComponentRef {
|
||||
/**
|
||||
* The {@link ViewRef} of the Host View of this Component instance.
|
||||
*/
|
||||
get hostView(): HostViewRef { return this.location.parentView; }
|
||||
get hostView(): HostViewRef {
|
||||
return (<ElementRef_>this.location).internalElement.parentView.ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -140,7 +142,7 @@ export abstract class DynamicComponentLoader {
|
||||
* ```
|
||||
*/
|
||||
abstract loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
||||
onDispose?: () => void): Promise<ComponentRef>;
|
||||
onDispose?: () => void, projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||
|
||||
/**
|
||||
* Creates an instance of a Component and attaches it to a View Container located inside of the
|
||||
@ -190,7 +192,8 @@ export abstract class DynamicComponentLoader {
|
||||
* ```
|
||||
*/
|
||||
abstract loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
||||
providers?: ResolvedProvider[]): Promise<ComponentRef>;
|
||||
providers?: ResolvedProvider[],
|
||||
projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||
|
||||
/**
|
||||
* Creates an instance of a Component and attaches it to the View Container found at the
|
||||
@ -232,19 +235,19 @@ export abstract class DynamicComponentLoader {
|
||||
* <child-component>Child</child-component>
|
||||
* ```
|
||||
*/
|
||||
abstract loadNextToLocation(type: Type, location: ElementRef,
|
||||
providers?: ResolvedProvider[]): Promise<ComponentRef>;
|
||||
abstract loadNextToLocation(type: Type, location: ElementRef, providers?: ResolvedProvider[],
|
||||
projectableNodes?: any[][]): Promise<ComponentRef>;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
||||
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) { super(); }
|
||||
|
||||
loadAsRoot(type: Type, overrideSelector: string, injector: Injector,
|
||||
onDispose?: () => void): Promise<ComponentRef> {
|
||||
loadAsRoot(type: Type, overrideSelector: string, injector: Injector, onDispose?: () => void,
|
||||
projectableNodes?: any[][]): Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||
var hostViewRef =
|
||||
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
||||
var hostViewRef = this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector,
|
||||
injector, projectableNodes);
|
||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
@ -259,24 +262,25 @@ export class DynamicComponentLoader_ extends DynamicComponentLoader {
|
||||
}
|
||||
|
||||
loadIntoLocation(type: Type, hostLocation: ElementRef, anchorName: string,
|
||||
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
|
||||
providers: ResolvedProvider[] = null,
|
||||
projectableNodes: any[][] = null): Promise<ComponentRef> {
|
||||
return this.loadNextToLocation(
|
||||
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
|
||||
providers);
|
||||
type, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName), providers,
|
||||
projectableNodes);
|
||||
}
|
||||
|
||||
loadNextToLocation(type: Type, location: ElementRef,
|
||||
providers: ResolvedProvider[] = null): Promise<ComponentRef> {
|
||||
loadNextToLocation(type: Type, location: ElementRef, providers: ResolvedProvider[] = null,
|
||||
projectableNodes: any[][] = null): Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(type).then(hostProtoViewRef => {
|
||||
var viewContainer = this._viewManager.getViewContainer(location);
|
||||
var hostViewRef =
|
||||
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, providers);
|
||||
var hostViewRef = viewContainer.createHostView(hostProtoViewRef, viewContainer.length,
|
||||
providers, projectableNodes);
|
||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
var dispose = () => {
|
||||
var index = viewContainer.indexOf(<ViewRef>hostViewRef);
|
||||
if (index !== -1) {
|
||||
var index = viewContainer.indexOf(hostViewRef);
|
||||
if (!hostViewRef.destroyed && index !== -1) {
|
||||
viewContainer.remove(index);
|
||||
}
|
||||
};
|
||||
|
867
modules/angular2/src/core/linker/element.ts
Normal file
867
modules/angular2/src/core/linker/element.ts
Normal file
@ -0,0 +1,867 @@
|
||||
import {
|
||||
isPresent,
|
||||
isBlank,
|
||||
Type,
|
||||
stringify,
|
||||
CONST_EXPR,
|
||||
StringWrapper
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
Injector,
|
||||
Key,
|
||||
Dependency,
|
||||
provide,
|
||||
Provider,
|
||||
ResolvedProvider,
|
||||
NoProviderError,
|
||||
AbstractProviderError,
|
||||
CyclicDependencyError,
|
||||
resolveForwardRef,
|
||||
Injectable
|
||||
} from 'angular2/src/core/di';
|
||||
import {mergeResolvedProviders} from 'angular2/src/core/di/provider';
|
||||
import {
|
||||
UNDEFINED,
|
||||
ProtoInjector,
|
||||
Visibility,
|
||||
InjectorInlineStrategy,
|
||||
InjectorDynamicStrategy,
|
||||
ProviderWithVisibility,
|
||||
DependencyProvider
|
||||
} from 'angular2/src/core/di/injector';
|
||||
import {resolveProvider, ResolvedFactory, ResolvedProvider_} from 'angular2/src/core/di/provider';
|
||||
|
||||
import {AttributeMetadata, QueryMetadata} from '../metadata/di';
|
||||
|
||||
import {AppView} from './view';
|
||||
import {ViewType} from './view_type';
|
||||
import {ElementRef_} from './element_ref';
|
||||
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {Renderer} from 'angular2/src/core/render/api';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {DirectiveMetadata, ComponentMetadata} from '../metadata/directives';
|
||||
import {
|
||||
ChangeDetector,
|
||||
ChangeDetectorRef
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {QueryList} from './query_list';
|
||||
import {reflector} from 'angular2/src/core/reflection/reflection';
|
||||
import {SetterFn} from 'angular2/src/core/reflection/types';
|
||||
import {AfterViewChecked} from 'angular2/src/core/linker/interfaces';
|
||||
import {PipeProvider} from 'angular2/src/core/pipes/pipe_provider';
|
||||
|
||||
import {ViewContainerRef_} from "./view_container_ref";
|
||||
import {ResolvedMetadataCache} from './resolved_metadata_cache';
|
||||
|
||||
var _staticKeys;
|
||||
|
||||
export class StaticKeys {
|
||||
templateRefId: number;
|
||||
viewContainerId: number;
|
||||
changeDetectorRefId: number;
|
||||
elementRefId: number;
|
||||
rendererId: number;
|
||||
|
||||
constructor() {
|
||||
this.templateRefId = Key.get(TemplateRef).id;
|
||||
this.viewContainerId = Key.get(ViewContainerRef).id;
|
||||
this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
|
||||
this.elementRefId = Key.get(ElementRef).id;
|
||||
this.rendererId = Key.get(Renderer).id;
|
||||
}
|
||||
|
||||
static instance(): StaticKeys {
|
||||
if (isBlank(_staticKeys)) _staticKeys = new StaticKeys();
|
||||
return _staticKeys;
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectiveDependency extends Dependency {
|
||||
constructor(key: Key, optional: boolean, lowerBoundVisibility: Object,
|
||||
upperBoundVisibility: Object, properties: any[], public attributeName: string,
|
||||
public queryDecorator: QueryMetadata) {
|
||||
super(key, optional, lowerBoundVisibility, upperBoundVisibility, properties);
|
||||
this._verify();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_verify(): void {
|
||||
var count = 0;
|
||||
if (isPresent(this.queryDecorator)) count++;
|
||||
if (isPresent(this.attributeName)) count++;
|
||||
if (count > 1)
|
||||
throw new BaseException(
|
||||
'A directive injectable can contain only one of the following @Attribute or @Query.');
|
||||
}
|
||||
|
||||
static createFrom(d: Dependency): DirectiveDependency {
|
||||
return new DirectiveDependency(
|
||||
d.key, d.optional, d.lowerBoundVisibility, d.upperBoundVisibility, d.properties,
|
||||
DirectiveDependency._attributeName(d.properties), DirectiveDependency._query(d.properties));
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
static _attributeName(properties: any[]): string {
|
||||
var p = <AttributeMetadata>properties.find(p => p instanceof AttributeMetadata);
|
||||
return isPresent(p) ? p.attributeName : null;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
static _query(properties: any[]): QueryMetadata {
|
||||
return <QueryMetadata>properties.find(p => p instanceof QueryMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectiveProvider extends ResolvedProvider_ {
|
||||
constructor(key: Key, factory: Function, deps: Dependency[], public isComponent: boolean,
|
||||
public providers: ResolvedProvider[], public viewProviders: ResolvedProvider[],
|
||||
public queries: QueryMetadataWithSetter[]) {
|
||||
super(key, [new ResolvedFactory(factory, deps)], false);
|
||||
}
|
||||
|
||||
get displayName(): string { return this.key.displayName; }
|
||||
|
||||
static createFromType(type: Type, meta: DirectiveMetadata): DirectiveProvider {
|
||||
var provider = new Provider(type, {useClass: type});
|
||||
if (isBlank(meta)) {
|
||||
meta = new DirectiveMetadata();
|
||||
}
|
||||
var rb = resolveProvider(provider);
|
||||
var rf = rb.resolvedFactories[0];
|
||||
var deps: DirectiveDependency[] = rf.dependencies.map(DirectiveDependency.createFrom);
|
||||
var isComponent = meta instanceof ComponentMetadata;
|
||||
var resolvedProviders = isPresent(meta.providers) ? Injector.resolve(meta.providers) : null;
|
||||
var resolvedViewProviders = meta instanceof ComponentMetadata && isPresent(meta.viewProviders) ?
|
||||
Injector.resolve(meta.viewProviders) :
|
||||
null;
|
||||
var queries = [];
|
||||
if (isPresent(meta.queries)) {
|
||||
StringMapWrapper.forEach(meta.queries, (meta, fieldName) => {
|
||||
var setter = reflector.setter(fieldName);
|
||||
queries.push(new QueryMetadataWithSetter(setter, meta));
|
||||
});
|
||||
}
|
||||
// queries passed into the constructor.
|
||||
// TODO: remove this after constructor queries are no longer supported
|
||||
deps.forEach(d => {
|
||||
if (isPresent(d.queryDecorator)) {
|
||||
queries.push(new QueryMetadataWithSetter(null, d.queryDecorator));
|
||||
}
|
||||
});
|
||||
return new DirectiveProvider(rb.key, rf.factory, deps, isComponent, resolvedProviders,
|
||||
resolvedViewProviders, queries);
|
||||
}
|
||||
}
|
||||
|
||||
export class QueryMetadataWithSetter {
|
||||
constructor(public setter: SetterFn, public metadata: QueryMetadata) {}
|
||||
}
|
||||
|
||||
|
||||
function setProvidersVisibility(providers: ResolvedProvider[], visibility: Visibility,
|
||||
result: Map<number, Visibility>) {
|
||||
for (var i = 0; i < providers.length; i++) {
|
||||
result.set(providers[i].key.id, visibility);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppProtoElement {
|
||||
protoInjector: ProtoInjector;
|
||||
|
||||
static create(metadataCache: ResolvedMetadataCache, index: number,
|
||||
attributes: {[key: string]: string}, directiveTypes: Type[],
|
||||
directiveVariableBindings: {[key: string]: number}): AppProtoElement {
|
||||
var componentDirProvider = null;
|
||||
var mergedProvidersMap: Map<number, ResolvedProvider> = new Map<number, ResolvedProvider>();
|
||||
var providerVisibilityMap: Map<number, Visibility> = new Map<number, Visibility>();
|
||||
var providers = ListWrapper.createGrowableSize(directiveTypes.length);
|
||||
|
||||
var protoQueryRefs = [];
|
||||
for (var i = 0; i < directiveTypes.length; i++) {
|
||||
var dirProvider = metadataCache.getResolvedDirectiveMetadata(directiveTypes[i]);
|
||||
providers[i] = new ProviderWithVisibility(
|
||||
dirProvider, dirProvider.isComponent ? Visibility.PublicAndPrivate : Visibility.Public);
|
||||
|
||||
if (dirProvider.isComponent) {
|
||||
componentDirProvider = dirProvider;
|
||||
} else {
|
||||
if (isPresent(dirProvider.providers)) {
|
||||
mergeResolvedProviders(dirProvider.providers, mergedProvidersMap);
|
||||
setProvidersVisibility(dirProvider.providers, Visibility.Public, providerVisibilityMap);
|
||||
}
|
||||
}
|
||||
if (isPresent(dirProvider.viewProviders)) {
|
||||
mergeResolvedProviders(dirProvider.viewProviders, mergedProvidersMap);
|
||||
setProvidersVisibility(dirProvider.viewProviders, Visibility.Private,
|
||||
providerVisibilityMap);
|
||||
}
|
||||
for (var queryIdx = 0; queryIdx < dirProvider.queries.length; queryIdx++) {
|
||||
var q = dirProvider.queries[queryIdx];
|
||||
protoQueryRefs.push(new ProtoQueryRef(i, q.setter, q.metadata));
|
||||
}
|
||||
}
|
||||
if (isPresent(componentDirProvider) && isPresent(componentDirProvider.providers)) {
|
||||
// directive providers need to be prioritized over component providers
|
||||
mergeResolvedProviders(componentDirProvider.providers, mergedProvidersMap);
|
||||
setProvidersVisibility(componentDirProvider.providers, Visibility.Public,
|
||||
providerVisibilityMap);
|
||||
}
|
||||
mergedProvidersMap.forEach((provider, _) => {
|
||||
providers.push(
|
||||
new ProviderWithVisibility(provider, providerVisibilityMap.get(provider.key.id)));
|
||||
});
|
||||
|
||||
return new AppProtoElement(isPresent(componentDirProvider), index, attributes, providers,
|
||||
protoQueryRefs, directiveVariableBindings);
|
||||
}
|
||||
|
||||
constructor(public firstProviderIsComponent: boolean, public index: number,
|
||||
public attributes: {[key: string]: string}, pwvs: ProviderWithVisibility[],
|
||||
public protoQueryRefs: ProtoQueryRef[],
|
||||
public directiveVariableBindings: {[key: string]: number}) {
|
||||
var length = pwvs.length;
|
||||
if (length > 0) {
|
||||
this.protoInjector = new ProtoInjector(pwvs);
|
||||
} else {
|
||||
this.protoInjector = null;
|
||||
this.protoQueryRefs = [];
|
||||
}
|
||||
}
|
||||
|
||||
getProviderAtIndex(index: number): any { return this.protoInjector.getProviderAtIndex(index); }
|
||||
}
|
||||
|
||||
class _Context {
|
||||
constructor(public element: any, public componentElement: any, public injector: any) {}
|
||||
}
|
||||
|
||||
export class InjectorWithHostBoundary {
|
||||
constructor(public injector: Injector, public hostInjectorBoundary: boolean) {}
|
||||
}
|
||||
|
||||
export class AppElement implements DependencyProvider, ElementRef, AfterViewChecked {
|
||||
static getViewParentInjector(parentViewType: ViewType, containerAppElement: AppElement,
|
||||
imperativelyCreatedProviders: ResolvedProvider[],
|
||||
rootInjector: Injector): InjectorWithHostBoundary {
|
||||
var parentInjector;
|
||||
var hostInjectorBoundary;
|
||||
switch (parentViewType) {
|
||||
case ViewType.COMPONENT:
|
||||
parentInjector = containerAppElement._injector;
|
||||
hostInjectorBoundary = true;
|
||||
break;
|
||||
case ViewType.EMBEDDED:
|
||||
parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
|
||||
containerAppElement._injector.parent :
|
||||
containerAppElement._injector;
|
||||
hostInjectorBoundary = containerAppElement._injector.hostBoundary;
|
||||
break;
|
||||
case ViewType.HOST:
|
||||
if (isPresent(containerAppElement)) {
|
||||
// host view is attached to a container
|
||||
parentInjector = isPresent(containerAppElement.proto.protoInjector) ?
|
||||
containerAppElement._injector.parent :
|
||||
containerAppElement._injector;
|
||||
if (isPresent(imperativelyCreatedProviders)) {
|
||||
var imperativeProvidersWithVisibility = imperativelyCreatedProviders.map(
|
||||
p => new ProviderWithVisibility(p, Visibility.Public));
|
||||
// The imperative injector is similar to having an element between
|
||||
// the dynamic-loaded component and its parent => no boundary between
|
||||
// the component and imperativelyCreatedInjector.
|
||||
parentInjector = new Injector(new ProtoInjector(imperativeProvidersWithVisibility),
|
||||
parentInjector, true, null, null);
|
||||
hostInjectorBoundary = false;
|
||||
} else {
|
||||
hostInjectorBoundary = containerAppElement._injector.hostBoundary;
|
||||
}
|
||||
} else {
|
||||
// bootstrap
|
||||
parentInjector = rootInjector;
|
||||
hostInjectorBoundary = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return new InjectorWithHostBoundary(parentInjector, hostInjectorBoundary);
|
||||
}
|
||||
|
||||
public nestedViews: AppView[] = null;
|
||||
public componentView: AppView = null;
|
||||
|
||||
private _queryStrategy: _QueryStrategy;
|
||||
private _injector: Injector;
|
||||
private _strategy: _ElementDirectiveStrategy;
|
||||
public ref: ElementRef_;
|
||||
|
||||
constructor(public proto: AppProtoElement, public parentView: AppView, public parent: AppElement,
|
||||
public nativeElement: any, public embeddedViewFactory: Function) {
|
||||
this.ref = new ElementRef_(this);
|
||||
var parentInjector = isPresent(parent) ? parent._injector : parentView.parentInjector;
|
||||
if (isPresent(this.proto.protoInjector)) {
|
||||
var isBoundary;
|
||||
if (isPresent(parent) && isPresent(parent.proto.protoInjector)) {
|
||||
isBoundary = false;
|
||||
} else {
|
||||
isBoundary = parentView.hostInjectorBoundary;
|
||||
}
|
||||
this._queryStrategy = this._buildQueryStrategy();
|
||||
this._injector = new Injector(this.proto.protoInjector, parentInjector, isBoundary, this,
|
||||
() => this._debugContext());
|
||||
|
||||
// we couple ourselves to the injector strategy to avoid polymorphic calls
|
||||
var injectorStrategy = <any>this._injector.internalStrategy;
|
||||
this._strategy = injectorStrategy instanceof InjectorInlineStrategy ?
|
||||
new ElementDirectiveInlineStrategy(injectorStrategy, this) :
|
||||
new ElementDirectiveDynamicStrategy(injectorStrategy, this);
|
||||
this._strategy.init();
|
||||
} else {
|
||||
this._queryStrategy = null;
|
||||
this._injector = parentInjector;
|
||||
this._strategy = null;
|
||||
}
|
||||
}
|
||||
|
||||
attachComponentView(componentView: AppView) { this.componentView = componentView; }
|
||||
|
||||
private _debugContext(): any {
|
||||
var c = this.parentView.getDebugContext(this, null, null);
|
||||
return isPresent(c) ? new _Context(c.element, c.componentElement, c.injector) : null;
|
||||
}
|
||||
|
||||
hasVariableBinding(name: string): boolean {
|
||||
var vb = this.proto.directiveVariableBindings;
|
||||
return isPresent(vb) && StringMapWrapper.contains(vb, name);
|
||||
}
|
||||
|
||||
getVariableBinding(name: string): any {
|
||||
var index = this.proto.directiveVariableBindings[name];
|
||||
return isPresent(index) ? this.getDirectiveAtIndex(<number>index) : this.getElementRef();
|
||||
}
|
||||
|
||||
get(token: any): any { return this._injector.get(token); }
|
||||
|
||||
hasDirective(type: Type): boolean { return isPresent(this._injector.getOptional(type)); }
|
||||
|
||||
getComponent(): any { return isPresent(this._strategy) ? this._strategy.getComponent() : null; }
|
||||
|
||||
getInjector(): Injector { return this._injector; }
|
||||
|
||||
getElementRef(): ElementRef { return this.ref; }
|
||||
|
||||
getViewContainerRef(): ViewContainerRef { return new ViewContainerRef_(this); }
|
||||
|
||||
getTemplateRef(): TemplateRef {
|
||||
if (isPresent(this.embeddedViewFactory)) {
|
||||
return new TemplateRef_(this.ref);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
getDependency(injector: Injector, provider: ResolvedProvider, dep: Dependency): any {
|
||||
if (provider instanceof DirectiveProvider) {
|
||||
var dirDep = <DirectiveDependency>dep;
|
||||
|
||||
if (isPresent(dirDep.attributeName)) return this._buildAttribute(dirDep);
|
||||
|
||||
if (isPresent(dirDep.queryDecorator))
|
||||
return this._queryStrategy.findQuery(dirDep.queryDecorator).list;
|
||||
|
||||
if (dirDep.key.id === StaticKeys.instance().changeDetectorRefId) {
|
||||
// We provide the component's view change detector to components and
|
||||
// the surrounding component's change detector to directives.
|
||||
if (this.proto.firstProviderIsComponent) {
|
||||
// Note: The component view is not yet created when
|
||||
// this method is called!
|
||||
return new _ComponentViewChangeDetectorRef(this);
|
||||
} else {
|
||||
return this.parentView.changeDetector.ref;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirDep.key.id === StaticKeys.instance().elementRefId) {
|
||||
return this.getElementRef();
|
||||
}
|
||||
|
||||
if (dirDep.key.id === StaticKeys.instance().viewContainerId) {
|
||||
return this.getViewContainerRef();
|
||||
}
|
||||
|
||||
if (dirDep.key.id === StaticKeys.instance().templateRefId) {
|
||||
var tr = this.getTemplateRef();
|
||||
if (isBlank(tr) && !dirDep.optional) {
|
||||
throw new NoProviderError(null, dirDep.key);
|
||||
}
|
||||
return tr;
|
||||
}
|
||||
|
||||
if (dirDep.key.id === StaticKeys.instance().rendererId) {
|
||||
return this.parentView.renderer;
|
||||
}
|
||||
|
||||
} else if (provider instanceof PipeProvider) {
|
||||
if (dep.key.id === StaticKeys.instance().changeDetectorRefId) {
|
||||
// We provide the component's view change detector to components and
|
||||
// the surrounding component's change detector to directives.
|
||||
if (this.proto.firstProviderIsComponent) {
|
||||
// Note: The component view is not yet created when
|
||||
// this method is called!
|
||||
return new _ComponentViewChangeDetectorRef(this);
|
||||
} else {
|
||||
return this.parentView.changeDetector;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
private _buildAttribute(dep: DirectiveDependency): string {
|
||||
var attributes = this.proto.attributes;
|
||||
if (isPresent(attributes) && StringMapWrapper.contains(attributes, dep.attributeName)) {
|
||||
return attributes[dep.attributeName];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
|
||||
var templateRef = this.getTemplateRef();
|
||||
if (query.selector === TemplateRef && isPresent(templateRef)) {
|
||||
list.push(templateRef);
|
||||
}
|
||||
if (this._strategy != null) {
|
||||
this._strategy.addDirectivesMatchingQuery(query, list);
|
||||
}
|
||||
}
|
||||
|
||||
private _buildQueryStrategy(): _QueryStrategy {
|
||||
if (this.proto.protoQueryRefs.length === 0) {
|
||||
return _emptyQueryStrategy;
|
||||
} else if (this.proto.protoQueryRefs.length <=
|
||||
InlineQueryStrategy.NUMBER_OF_SUPPORTED_QUERIES) {
|
||||
return new InlineQueryStrategy(this);
|
||||
} else {
|
||||
return new DynamicQueryStrategy(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
getDirectiveAtIndex(index: number): any { return this._injector.getAt(index); }
|
||||
|
||||
ngAfterViewChecked(): void {
|
||||
if (isPresent(this._queryStrategy)) this._queryStrategy.updateViewQueries();
|
||||
}
|
||||
|
||||
ngAfterContentChecked(): void {
|
||||
if (isPresent(this._queryStrategy)) this._queryStrategy.updateContentQueries();
|
||||
}
|
||||
|
||||
traverseAndSetQueriesAsDirty(): void {
|
||||
var inj: AppElement = this;
|
||||
while (isPresent(inj)) {
|
||||
inj._setQueriesAsDirty();
|
||||
inj = inj.parent;
|
||||
}
|
||||
}
|
||||
|
||||
private _setQueriesAsDirty(): void {
|
||||
if (isPresent(this._queryStrategy)) {
|
||||
this._queryStrategy.setContentQueriesAsDirty();
|
||||
}
|
||||
if (this.parentView.proto.type === ViewType.COMPONENT) {
|
||||
this.parentView.containerAppElement._queryStrategy.setViewQueriesAsDirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface _QueryStrategy {
|
||||
setContentQueriesAsDirty(): void;
|
||||
setViewQueriesAsDirty(): void;
|
||||
updateContentQueries(): void;
|
||||
updateViewQueries(): void;
|
||||
findQuery(query: QueryMetadata): QueryRef;
|
||||
}
|
||||
|
||||
class _EmptyQueryStrategy implements _QueryStrategy {
|
||||
setContentQueriesAsDirty(): void {}
|
||||
setViewQueriesAsDirty(): void {}
|
||||
updateContentQueries(): void {}
|
||||
updateViewQueries(): void {}
|
||||
findQuery(query: QueryMetadata): QueryRef {
|
||||
throw new BaseException(`Cannot find query for directive ${query}.`);
|
||||
}
|
||||
}
|
||||
|
||||
var _emptyQueryStrategy = new _EmptyQueryStrategy();
|
||||
|
||||
class InlineQueryStrategy implements _QueryStrategy {
|
||||
static NUMBER_OF_SUPPORTED_QUERIES = 3;
|
||||
|
||||
query0: QueryRef;
|
||||
query1: QueryRef;
|
||||
query2: QueryRef;
|
||||
|
||||
constructor(ei: AppElement) {
|
||||
var protoRefs = ei.proto.protoQueryRefs;
|
||||
if (protoRefs.length > 0) this.query0 = new QueryRef(protoRefs[0], ei);
|
||||
if (protoRefs.length > 1) this.query1 = new QueryRef(protoRefs[1], ei);
|
||||
if (protoRefs.length > 2) this.query2 = new QueryRef(protoRefs[2], ei);
|
||||
}
|
||||
|
||||
setContentQueriesAsDirty(): void {
|
||||
if (isPresent(this.query0) && !this.query0.isViewQuery) this.query0.dirty = true;
|
||||
if (isPresent(this.query1) && !this.query1.isViewQuery) this.query1.dirty = true;
|
||||
if (isPresent(this.query2) && !this.query2.isViewQuery) this.query2.dirty = true;
|
||||
}
|
||||
|
||||
setViewQueriesAsDirty(): void {
|
||||
if (isPresent(this.query0) && this.query0.isViewQuery) this.query0.dirty = true;
|
||||
if (isPresent(this.query1) && this.query1.isViewQuery) this.query1.dirty = true;
|
||||
if (isPresent(this.query2) && this.query2.isViewQuery) this.query2.dirty = true;
|
||||
}
|
||||
|
||||
updateContentQueries() {
|
||||
if (isPresent(this.query0) && !this.query0.isViewQuery) {
|
||||
this.query0.update();
|
||||
}
|
||||
if (isPresent(this.query1) && !this.query1.isViewQuery) {
|
||||
this.query1.update();
|
||||
}
|
||||
if (isPresent(this.query2) && !this.query2.isViewQuery) {
|
||||
this.query2.update();
|
||||
}
|
||||
}
|
||||
|
||||
updateViewQueries() {
|
||||
if (isPresent(this.query0) && this.query0.isViewQuery) {
|
||||
this.query0.update();
|
||||
}
|
||||
if (isPresent(this.query1) && this.query1.isViewQuery) {
|
||||
this.query1.update();
|
||||
}
|
||||
if (isPresent(this.query2) && this.query2.isViewQuery) {
|
||||
this.query2.update();
|
||||
}
|
||||
}
|
||||
|
||||
findQuery(query: QueryMetadata): QueryRef {
|
||||
if (isPresent(this.query0) && this.query0.protoQueryRef.query === query) {
|
||||
return this.query0;
|
||||
}
|
||||
if (isPresent(this.query1) && this.query1.protoQueryRef.query === query) {
|
||||
return this.query1;
|
||||
}
|
||||
if (isPresent(this.query2) && this.query2.protoQueryRef.query === query) {
|
||||
return this.query2;
|
||||
}
|
||||
throw new BaseException(`Cannot find query for directive ${query}.`);
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicQueryStrategy implements _QueryStrategy {
|
||||
queries: QueryRef[];
|
||||
|
||||
constructor(ei: AppElement) {
|
||||
this.queries = ei.proto.protoQueryRefs.map(p => new QueryRef(p, ei));
|
||||
}
|
||||
|
||||
setContentQueriesAsDirty(): void {
|
||||
for (var i = 0; i < this.queries.length; ++i) {
|
||||
var q = this.queries[i];
|
||||
if (!q.isViewQuery) q.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
setViewQueriesAsDirty(): void {
|
||||
for (var i = 0; i < this.queries.length; ++i) {
|
||||
var q = this.queries[i];
|
||||
if (q.isViewQuery) q.dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
updateContentQueries() {
|
||||
for (var i = 0; i < this.queries.length; ++i) {
|
||||
var q = this.queries[i];
|
||||
if (!q.isViewQuery) {
|
||||
q.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateViewQueries() {
|
||||
for (var i = 0; i < this.queries.length; ++i) {
|
||||
var q = this.queries[i];
|
||||
if (q.isViewQuery) {
|
||||
q.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
findQuery(query: QueryMetadata): QueryRef {
|
||||
for (var i = 0; i < this.queries.length; ++i) {
|
||||
var q = this.queries[i];
|
||||
if (q.protoQueryRef.query === query) {
|
||||
return q;
|
||||
}
|
||||
}
|
||||
throw new BaseException(`Cannot find query for directive ${query}.`);
|
||||
}
|
||||
}
|
||||
|
||||
interface _ElementDirectiveStrategy {
|
||||
getComponent(): any;
|
||||
isComponentKey(key: Key): boolean;
|
||||
addDirectivesMatchingQuery(q: QueryMetadata, res: any[]): void;
|
||||
init(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy used by the `ElementInjector` when the number of providers is 10 or less.
|
||||
* In such a case, inlining fields is beneficial for performances.
|
||||
*/
|
||||
class ElementDirectiveInlineStrategy implements _ElementDirectiveStrategy {
|
||||
constructor(public injectorStrategy: InjectorInlineStrategy, public _ei: AppElement) {}
|
||||
|
||||
init(): void {
|
||||
var i = this.injectorStrategy;
|
||||
var p = i.protoStrategy;
|
||||
i.resetConstructionCounter();
|
||||
|
||||
if (p.provider0 instanceof DirectiveProvider && isPresent(p.keyId0) && i.obj0 === UNDEFINED)
|
||||
i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
|
||||
if (p.provider1 instanceof DirectiveProvider && isPresent(p.keyId1) && i.obj1 === UNDEFINED)
|
||||
i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
|
||||
if (p.provider2 instanceof DirectiveProvider && isPresent(p.keyId2) && i.obj2 === UNDEFINED)
|
||||
i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
|
||||
if (p.provider3 instanceof DirectiveProvider && isPresent(p.keyId3) && i.obj3 === UNDEFINED)
|
||||
i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
|
||||
if (p.provider4 instanceof DirectiveProvider && isPresent(p.keyId4) && i.obj4 === UNDEFINED)
|
||||
i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
|
||||
if (p.provider5 instanceof DirectiveProvider && isPresent(p.keyId5) && i.obj5 === UNDEFINED)
|
||||
i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
|
||||
if (p.provider6 instanceof DirectiveProvider && isPresent(p.keyId6) && i.obj6 === UNDEFINED)
|
||||
i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
|
||||
if (p.provider7 instanceof DirectiveProvider && isPresent(p.keyId7) && i.obj7 === UNDEFINED)
|
||||
i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
|
||||
if (p.provider8 instanceof DirectiveProvider && isPresent(p.keyId8) && i.obj8 === UNDEFINED)
|
||||
i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
|
||||
if (p.provider9 instanceof DirectiveProvider && isPresent(p.keyId9) && i.obj9 === UNDEFINED)
|
||||
i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
|
||||
}
|
||||
|
||||
getComponent(): any { return this.injectorStrategy.obj0; }
|
||||
|
||||
isComponentKey(key: Key): boolean {
|
||||
return this._ei.proto.firstProviderIsComponent && isPresent(key) &&
|
||||
key.id === this.injectorStrategy.protoStrategy.keyId0;
|
||||
}
|
||||
|
||||
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
|
||||
var i = this.injectorStrategy;
|
||||
var p = i.protoStrategy;
|
||||
if (isPresent(p.provider0) && p.provider0.key.token === query.selector) {
|
||||
if (i.obj0 === UNDEFINED) i.obj0 = i.instantiateProvider(p.provider0, p.visibility0);
|
||||
list.push(i.obj0);
|
||||
}
|
||||
if (isPresent(p.provider1) && p.provider1.key.token === query.selector) {
|
||||
if (i.obj1 === UNDEFINED) i.obj1 = i.instantiateProvider(p.provider1, p.visibility1);
|
||||
list.push(i.obj1);
|
||||
}
|
||||
if (isPresent(p.provider2) && p.provider2.key.token === query.selector) {
|
||||
if (i.obj2 === UNDEFINED) i.obj2 = i.instantiateProvider(p.provider2, p.visibility2);
|
||||
list.push(i.obj2);
|
||||
}
|
||||
if (isPresent(p.provider3) && p.provider3.key.token === query.selector) {
|
||||
if (i.obj3 === UNDEFINED) i.obj3 = i.instantiateProvider(p.provider3, p.visibility3);
|
||||
list.push(i.obj3);
|
||||
}
|
||||
if (isPresent(p.provider4) && p.provider4.key.token === query.selector) {
|
||||
if (i.obj4 === UNDEFINED) i.obj4 = i.instantiateProvider(p.provider4, p.visibility4);
|
||||
list.push(i.obj4);
|
||||
}
|
||||
if (isPresent(p.provider5) && p.provider5.key.token === query.selector) {
|
||||
if (i.obj5 === UNDEFINED) i.obj5 = i.instantiateProvider(p.provider5, p.visibility5);
|
||||
list.push(i.obj5);
|
||||
}
|
||||
if (isPresent(p.provider6) && p.provider6.key.token === query.selector) {
|
||||
if (i.obj6 === UNDEFINED) i.obj6 = i.instantiateProvider(p.provider6, p.visibility6);
|
||||
list.push(i.obj6);
|
||||
}
|
||||
if (isPresent(p.provider7) && p.provider7.key.token === query.selector) {
|
||||
if (i.obj7 === UNDEFINED) i.obj7 = i.instantiateProvider(p.provider7, p.visibility7);
|
||||
list.push(i.obj7);
|
||||
}
|
||||
if (isPresent(p.provider8) && p.provider8.key.token === query.selector) {
|
||||
if (i.obj8 === UNDEFINED) i.obj8 = i.instantiateProvider(p.provider8, p.visibility8);
|
||||
list.push(i.obj8);
|
||||
}
|
||||
if (isPresent(p.provider9) && p.provider9.key.token === query.selector) {
|
||||
if (i.obj9 === UNDEFINED) i.obj9 = i.instantiateProvider(p.provider9, p.visibility9);
|
||||
list.push(i.obj9);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Strategy used by the `ElementInjector` when the number of bindings is 11 or more.
|
||||
* In such a case, there are too many fields to inline (see ElementInjectorInlineStrategy).
|
||||
*/
|
||||
class ElementDirectiveDynamicStrategy implements _ElementDirectiveStrategy {
|
||||
constructor(public injectorStrategy: InjectorDynamicStrategy, public _ei: AppElement) {}
|
||||
|
||||
init(): void {
|
||||
var inj = this.injectorStrategy;
|
||||
var p = inj.protoStrategy;
|
||||
inj.resetConstructionCounter();
|
||||
|
||||
for (var i = 0; i < p.keyIds.length; i++) {
|
||||
if (p.providers[i] instanceof DirectiveProvider && isPresent(p.keyIds[i]) &&
|
||||
inj.objs[i] === UNDEFINED) {
|
||||
inj.objs[i] = inj.instantiateProvider(p.providers[i], p.visibilities[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getComponent(): any { return this.injectorStrategy.objs[0]; }
|
||||
|
||||
isComponentKey(key: Key): boolean {
|
||||
var p = this.injectorStrategy.protoStrategy;
|
||||
return this._ei.proto.firstProviderIsComponent && isPresent(key) && key.id === p.keyIds[0];
|
||||
}
|
||||
|
||||
addDirectivesMatchingQuery(query: QueryMetadata, list: any[]): void {
|
||||
var ist = this.injectorStrategy;
|
||||
var p = ist.protoStrategy;
|
||||
|
||||
for (var i = 0; i < p.providers.length; i++) {
|
||||
if (p.providers[i].key.token === query.selector) {
|
||||
if (ist.objs[i] === UNDEFINED) {
|
||||
ist.objs[i] = ist.instantiateProvider(p.providers[i], p.visibilities[i]);
|
||||
}
|
||||
list.push(ist.objs[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class ProtoQueryRef {
|
||||
constructor(public dirIndex: number, public setter: SetterFn, public query: QueryMetadata) {}
|
||||
|
||||
get usesPropertySyntax(): boolean { return isPresent(this.setter); }
|
||||
}
|
||||
|
||||
export class QueryRef {
|
||||
public list: QueryList<any>;
|
||||
public dirty: boolean;
|
||||
|
||||
constructor(public protoQueryRef: ProtoQueryRef, private originator: AppElement) {
|
||||
this.list = new QueryList<any>();
|
||||
this.dirty = true;
|
||||
}
|
||||
|
||||
get isViewQuery(): boolean { return this.protoQueryRef.query.isViewQuery; }
|
||||
|
||||
update(): void {
|
||||
if (!this.dirty) return;
|
||||
this._update();
|
||||
this.dirty = false;
|
||||
|
||||
// TODO delete the check once only field queries are supported
|
||||
if (this.protoQueryRef.usesPropertySyntax) {
|
||||
var dir = this.originator.getDirectiveAtIndex(this.protoQueryRef.dirIndex);
|
||||
if (this.protoQueryRef.query.first) {
|
||||
this.protoQueryRef.setter(dir, this.list.length > 0 ? this.list.first : null);
|
||||
} else {
|
||||
this.protoQueryRef.setter(dir, this.list);
|
||||
}
|
||||
}
|
||||
|
||||
this.list.notifyOnChanges();
|
||||
}
|
||||
|
||||
private _update(): void {
|
||||
var aggregator = [];
|
||||
if (this.protoQueryRef.query.isViewQuery) {
|
||||
// intentionally skipping originator for view queries.
|
||||
var nestedView = this.originator.componentView;
|
||||
if (isPresent(nestedView)) this._visitView(nestedView, aggregator);
|
||||
} else {
|
||||
this._visit(this.originator, aggregator);
|
||||
}
|
||||
this.list.reset(aggregator);
|
||||
};
|
||||
|
||||
private _visit(inj: AppElement, aggregator: any[]): void {
|
||||
var view = inj.parentView;
|
||||
var startIdx = inj.proto.index;
|
||||
for (var i = startIdx; i < view.appElements.length; i++) {
|
||||
var curInj = view.appElements[i];
|
||||
// The first injector after inj, that is outside the subtree rooted at
|
||||
// inj has to have a null parent or a parent that is an ancestor of inj.
|
||||
if (i > startIdx && (isBlank(curInj.parent) || curInj.parent.proto.index < startIdx)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!this.protoQueryRef.query.descendants &&
|
||||
!(curInj.parent == this.originator || curInj == this.originator))
|
||||
continue;
|
||||
|
||||
// We visit the view container(VC) views right after the injector that contains
|
||||
// the VC. Theoretically, that might not be the right order if there are
|
||||
// child injectors of said injector. Not clear whether if such case can
|
||||
// even be constructed with the current apis.
|
||||
this._visitInjector(curInj, aggregator);
|
||||
this._visitViewContainerViews(curInj.nestedViews, aggregator);
|
||||
}
|
||||
}
|
||||
|
||||
private _visitInjector(inj: AppElement, aggregator: any[]) {
|
||||
if (this.protoQueryRef.query.isVarBindingQuery) {
|
||||
this._aggregateVariableBinding(inj, aggregator);
|
||||
} else {
|
||||
this._aggregateDirective(inj, aggregator);
|
||||
}
|
||||
}
|
||||
|
||||
private _visitViewContainerViews(views: AppView[], aggregator: any[]) {
|
||||
if (isPresent(views)) {
|
||||
for (var j = 0; j < views.length; j++) {
|
||||
this._visitView(views[j], aggregator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _visitView(view: AppView, aggregator: any[]) {
|
||||
for (var i = 0; i < view.appElements.length; i++) {
|
||||
var inj = view.appElements[i];
|
||||
this._visitInjector(inj, aggregator);
|
||||
this._visitViewContainerViews(inj.nestedViews, aggregator);
|
||||
}
|
||||
}
|
||||
|
||||
private _aggregateVariableBinding(inj: AppElement, aggregator: any[]): void {
|
||||
var vb = this.protoQueryRef.query.varBindings;
|
||||
for (var i = 0; i < vb.length; ++i) {
|
||||
if (inj.hasVariableBinding(vb[i])) {
|
||||
aggregator.push(inj.getVariableBinding(vb[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _aggregateDirective(inj: AppElement, aggregator: any[]): void {
|
||||
inj.addDirectivesMatchingQuery(this.protoQueryRef.query, aggregator);
|
||||
}
|
||||
}
|
||||
|
||||
class _ComponentViewChangeDetectorRef extends ChangeDetectorRef {
|
||||
constructor(private _appElement: AppElement) { super(); }
|
||||
|
||||
markForCheck(): void { this._appElement.componentView.changeDetector.ref.markForCheck(); }
|
||||
detach(): void { this._appElement.componentView.changeDetector.ref.detach(); }
|
||||
detectChanges(): void { this._appElement.componentView.changeDetector.ref.detectChanges(); }
|
||||
checkNoChanges(): void { this._appElement.componentView.changeDetector.ref.checkNoChanges(); }
|
||||
reattach(): void { this._appElement.componentView.changeDetector.ref.reattach(); }
|
||||
}
|
@ -1,16 +0,0 @@
|
||||
import {isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as eiModule from './element_injector';
|
||||
import {DirectiveProvider} from './element_injector';
|
||||
import * as viewModule from './view';
|
||||
|
||||
export class ElementBinder {
|
||||
constructor(public index: number, public parent: ElementBinder, public distanceToParent: number,
|
||||
public protoElementInjector: eiModule.ProtoElementInjector,
|
||||
public componentDirective: DirectiveProvider,
|
||||
public nestedProtoView: viewModule.AppProtoView) {
|
||||
if (isBlank(index)) {
|
||||
throw new BaseException('null index not allowed.');
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,5 @@
|
||||
import {BaseException, unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ViewRef, ViewRef_} from './view_ref';
|
||||
import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/render/api';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {AppElement} from './element';
|
||||
|
||||
/**
|
||||
* Represents a location in a View that has an injection, change-detection and render context
|
||||
@ -12,23 +11,7 @@ import {RenderViewRef, RenderElementRef, Renderer} from 'angular2/src/core/rende
|
||||
* An `ElementRef` is backed by a render-specific element. In the browser, this is usually a DOM
|
||||
* element.
|
||||
*/
|
||||
export abstract class ElementRef implements RenderElementRef {
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Reference to the {@link ViewRef} that this `ElementRef` is part of.
|
||||
*/
|
||||
parentView: ViewRef;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* Index of the element inside the {@link ViewRef}.
|
||||
*
|
||||
* This is used internally by the Angular framework to locate elements.
|
||||
*/
|
||||
boundElementIndex: number;
|
||||
|
||||
export abstract class ElementRef {
|
||||
/**
|
||||
* The underlying native element or `null` if direct access to native elements is not supported
|
||||
* (e.g. when the application runs in a web worker).
|
||||
@ -48,24 +31,13 @@ export abstract class ElementRef implements RenderElementRef {
|
||||
* </p>
|
||||
* </div>
|
||||
*/
|
||||
get nativeElement(): any { return unimplemented(); };
|
||||
|
||||
get renderView(): RenderViewRef { return unimplemented(); }
|
||||
get nativeElement(): any { return unimplemented(); }
|
||||
}
|
||||
|
||||
export class ElementRef_ extends ElementRef {
|
||||
constructor(public parentView: ViewRef,
|
||||
export class ElementRef_ implements ElementRef {
|
||||
constructor(private _appElement: AppElement) {}
|
||||
|
||||
/**
|
||||
* Index of the element inside the {@link ViewRef}.
|
||||
*
|
||||
* This is used internally by the Angular framework to locate elements.
|
||||
*/
|
||||
public boundElementIndex: number, private _renderer: Renderer) {
|
||||
super();
|
||||
}
|
||||
get internalElement(): AppElement { return this._appElement; }
|
||||
|
||||
get renderView(): RenderViewRef { return (<ViewRef_>this.parentView).render; }
|
||||
set renderView(value) { unimplemented(); }
|
||||
get nativeElement(): any { return this._renderer.getNativeElementSync(this); }
|
||||
get nativeElement() { return this._appElement.nativeElement; }
|
||||
}
|
||||
|
@ -1,22 +0,0 @@
|
||||
export const EVENT_TARGET_SEPARATOR = ':';
|
||||
|
||||
export class EventConfig {
|
||||
constructor(public fieldName: string, public eventName: string, public isLongForm: boolean) {}
|
||||
|
||||
static parse(eventConfig: string): EventConfig {
|
||||
var fieldName = eventConfig, eventName = eventConfig, isLongForm = false;
|
||||
var separatorIdx = eventConfig.indexOf(EVENT_TARGET_SEPARATOR);
|
||||
if (separatorIdx > -1) {
|
||||
// long format: 'fieldName: eventName'
|
||||
fieldName = eventConfig.substring(0, separatorIdx).trim();
|
||||
eventName = eventConfig.substring(separatorIdx + 1).trim();
|
||||
isLongForm = true;
|
||||
}
|
||||
return new EventConfig(fieldName, eventName, isLongForm);
|
||||
}
|
||||
|
||||
getFullName(): string {
|
||||
return this.isLongForm ? `${this.fieldName}${EVENT_TARGET_SEPARATOR}${this.eventName}` :
|
||||
this.eventName;
|
||||
}
|
||||
}
|
@ -31,3 +31,5 @@ export class PipeResolver {
|
||||
throw new BaseException(`No Pipe decorator found on ${stringify(type)}`);
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_PIPE_RESOLVER = new PipeResolver();
|
||||
|
@ -1,341 +0,0 @@
|
||||
import {isPresent, isBlank, Type, isArray, isNumber} from 'angular2/src/facade/lang';
|
||||
|
||||
import {RenderProtoViewRef, RenderComponentTemplate} from 'angular2/src/core/render/api';
|
||||
|
||||
import {Optional, Injectable, Provider, resolveForwardRef, Inject} from 'angular2/src/core/di';
|
||||
|
||||
import {PipeProvider} from '../pipes/pipe_provider';
|
||||
import {ProtoPipes} from '../pipes/pipes';
|
||||
|
||||
import {AppProtoView, AppProtoViewMergeInfo, ViewType} from './view';
|
||||
import {ElementBinder} from './element_binder';
|
||||
import {ProtoElementInjector, DirectiveProvider} from './element_injector';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {ViewResolver} from './view_resolver';
|
||||
import {PipeResolver} from './pipe_resolver';
|
||||
import {ViewMetadata, ViewEncapsulation} from '../metadata/view';
|
||||
import {PLATFORM_PIPES} from 'angular2/src/core/platform_directives_and_pipes';
|
||||
|
||||
import {
|
||||
visitAllCommands,
|
||||
CompiledComponentTemplate,
|
||||
CompiledHostTemplate,
|
||||
TemplateCmd,
|
||||
CommandVisitor,
|
||||
EmbeddedTemplateCmd,
|
||||
BeginComponentCmd,
|
||||
BeginElementCmd,
|
||||
IBeginElementCmd,
|
||||
TextCmd,
|
||||
NgContentCmd
|
||||
} from './template_commands';
|
||||
|
||||
import {Renderer} from 'angular2/src/core/render/api';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
|
||||
|
||||
@Injectable()
|
||||
export class ProtoViewFactory {
|
||||
private _cache: Map<string, AppProtoView> = new Map<string, AppProtoView>();
|
||||
private _nextTemplateId: number = 0;
|
||||
|
||||
constructor(private _renderer: Renderer,
|
||||
@Optional() @Inject(PLATFORM_PIPES) private _platformPipes: Array<Type | any[]>,
|
||||
private _directiveResolver: DirectiveResolver, private _viewResolver: ViewResolver,
|
||||
private _pipeResolver: PipeResolver, @Inject(APP_ID) private _appId: string) {}
|
||||
|
||||
clearCache() { this._cache.clear(); }
|
||||
|
||||
createHost(compiledHostTemplate: CompiledHostTemplate): AppProtoView {
|
||||
var compiledTemplate = compiledHostTemplate.template;
|
||||
var result = this._cache.get(compiledTemplate.id);
|
||||
if (isBlank(result)) {
|
||||
var emptyMap: {[key: string]: PipeProvider} = {};
|
||||
var shortId = `${this._appId}-${this._nextTemplateId++}`;
|
||||
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
|
||||
compiledTemplate.id, shortId, ViewEncapsulation.None, compiledTemplate.commands, []));
|
||||
result =
|
||||
new AppProtoView(compiledTemplate.id, compiledTemplate.commands, ViewType.HOST, true,
|
||||
compiledTemplate.changeDetectorFactory, null, new ProtoPipes(emptyMap));
|
||||
this._cache.set(compiledTemplate.id, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _createComponent(cmd: BeginComponentCmd): AppProtoView {
|
||||
var nestedProtoView = this._cache.get(cmd.templateId);
|
||||
if (isBlank(nestedProtoView)) {
|
||||
var component = cmd.directives[0];
|
||||
var view = this._viewResolver.resolve(component);
|
||||
var compiledTemplate = cmd.templateGetter();
|
||||
var styles = _flattenStyleArr(compiledTemplate.styles, []);
|
||||
var shortId = `${this._appId}-${this._nextTemplateId++}`;
|
||||
this._renderer.registerComponentTemplate(new RenderComponentTemplate(
|
||||
compiledTemplate.id, shortId, cmd.encapsulation, compiledTemplate.commands, styles));
|
||||
var boundPipes = this._flattenPipes(view).map(pipe => this._bindPipe(pipe));
|
||||
|
||||
nestedProtoView = new AppProtoView(
|
||||
compiledTemplate.id, compiledTemplate.commands, ViewType.COMPONENT, true,
|
||||
compiledTemplate.changeDetectorFactory, null, ProtoPipes.fromProviders(boundPipes));
|
||||
// Note: The cache is updated before recursing
|
||||
// to be able to resolve cycles
|
||||
this._cache.set(compiledTemplate.id, nestedProtoView);
|
||||
this._initializeProtoView(nestedProtoView, null);
|
||||
}
|
||||
return nestedProtoView;
|
||||
}
|
||||
|
||||
private _createEmbeddedTemplate(cmd: EmbeddedTemplateCmd, parent: AppProtoView): AppProtoView {
|
||||
var nestedProtoView = new AppProtoView(
|
||||
parent.templateId, cmd.children, ViewType.EMBEDDED, cmd.isMerged, cmd.changeDetectorFactory,
|
||||
arrayToMap(cmd.variableNameAndValues, true), new ProtoPipes(parent.pipes.config));
|
||||
if (cmd.isMerged) {
|
||||
this.initializeProtoViewIfNeeded(nestedProtoView);
|
||||
}
|
||||
return nestedProtoView;
|
||||
}
|
||||
|
||||
initializeProtoViewIfNeeded(protoView: AppProtoView) {
|
||||
if (!protoView.isInitialized()) {
|
||||
var render = this._renderer.createProtoView(protoView.templateId, protoView.templateCmds);
|
||||
this._initializeProtoView(protoView, render);
|
||||
}
|
||||
}
|
||||
|
||||
private _initializeProtoView(protoView: AppProtoView, render: RenderProtoViewRef) {
|
||||
var initializer = new _ProtoViewInitializer(protoView, this._directiveResolver, this);
|
||||
visitAllCommands(initializer, protoView.templateCmds);
|
||||
var mergeInfo =
|
||||
new AppProtoViewMergeInfo(initializer.mergeEmbeddedViewCount, initializer.mergeElementCount,
|
||||
initializer.mergeViewCount);
|
||||
protoView.init(render, initializer.elementBinders, initializer.boundTextCount, mergeInfo,
|
||||
initializer.variableLocations);
|
||||
}
|
||||
|
||||
private _bindPipe(typeOrProvider): PipeProvider {
|
||||
let meta = this._pipeResolver.resolve(typeOrProvider);
|
||||
return PipeProvider.createFromType(typeOrProvider, meta);
|
||||
}
|
||||
|
||||
private _flattenPipes(view: ViewMetadata): any[] {
|
||||
let pipes = [];
|
||||
if (isPresent(this._platformPipes)) {
|
||||
_flattenArray(this._platformPipes, pipes);
|
||||
}
|
||||
if (isPresent(view.pipes)) {
|
||||
_flattenArray(view.pipes, pipes);
|
||||
}
|
||||
return pipes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createComponent(protoViewFactory: ProtoViewFactory, cmd: BeginComponentCmd): AppProtoView {
|
||||
return (<any>protoViewFactory)._createComponent(cmd);
|
||||
}
|
||||
|
||||
function createEmbeddedTemplate(protoViewFactory: ProtoViewFactory, cmd: EmbeddedTemplateCmd,
|
||||
parent: AppProtoView): AppProtoView {
|
||||
return (<any>protoViewFactory)._createEmbeddedTemplate(cmd, parent);
|
||||
}
|
||||
|
||||
class _ProtoViewInitializer implements CommandVisitor {
|
||||
variableLocations: Map<string, number> = new Map<string, number>();
|
||||
boundTextCount: number = 0;
|
||||
boundElementIndex: number = 0;
|
||||
elementBinderStack: ElementBinder[] = [];
|
||||
distanceToParentElementBinder: number = 0;
|
||||
distanceToParentProtoElementInjector: number = 0;
|
||||
elementBinders: ElementBinder[] = [];
|
||||
mergeEmbeddedViewCount: number = 0;
|
||||
mergeElementCount: number = 0;
|
||||
mergeViewCount: number = 1;
|
||||
|
||||
constructor(private _protoView: AppProtoView, private _directiveResolver: DirectiveResolver,
|
||||
private _protoViewFactory: ProtoViewFactory) {}
|
||||
|
||||
visitText(cmd: TextCmd, context: any): any {
|
||||
if (cmd.isBound) {
|
||||
this.boundTextCount++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitNgContent(cmd: NgContentCmd, context: any): any { return null; }
|
||||
visitBeginElement(cmd: BeginElementCmd, context: any): any {
|
||||
if (cmd.isBound) {
|
||||
this._visitBeginBoundElement(cmd, null);
|
||||
} else {
|
||||
this._visitBeginElement(cmd, null, null);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
visitEndElement(context: any): any { return this._visitEndElement(); }
|
||||
visitBeginComponent(cmd: BeginComponentCmd, context: any): any {
|
||||
var nestedProtoView = createComponent(this._protoViewFactory, cmd);
|
||||
return this._visitBeginBoundElement(cmd, nestedProtoView);
|
||||
}
|
||||
visitEndComponent(context: any): any { return this._visitEndElement(); }
|
||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any {
|
||||
var nestedProtoView = createEmbeddedTemplate(this._protoViewFactory, cmd, this._protoView);
|
||||
if (cmd.isMerged) {
|
||||
this.mergeEmbeddedViewCount++;
|
||||
}
|
||||
this._visitBeginBoundElement(cmd, nestedProtoView);
|
||||
return this._visitEndElement();
|
||||
}
|
||||
|
||||
private _visitBeginBoundElement(cmd: IBeginElementCmd, nestedProtoView: AppProtoView): any {
|
||||
if (isPresent(nestedProtoView) && nestedProtoView.isMergable) {
|
||||
this.mergeElementCount += nestedProtoView.mergeInfo.elementCount;
|
||||
this.mergeViewCount += nestedProtoView.mergeInfo.viewCount;
|
||||
this.mergeEmbeddedViewCount += nestedProtoView.mergeInfo.embeddedViewCount;
|
||||
}
|
||||
var elementBinder = _createElementBinder(
|
||||
this._directiveResolver, nestedProtoView, this.elementBinderStack, this.boundElementIndex,
|
||||
this.distanceToParentElementBinder, this.distanceToParentProtoElementInjector, cmd);
|
||||
this.elementBinders.push(elementBinder);
|
||||
var protoElementInjector = elementBinder.protoElementInjector;
|
||||
for (var i = 0; i < cmd.variableNameAndValues.length; i += 2) {
|
||||
this.variableLocations.set(<string>cmd.variableNameAndValues[i], this.boundElementIndex);
|
||||
}
|
||||
this.boundElementIndex++;
|
||||
this.mergeElementCount++;
|
||||
return this._visitBeginElement(cmd, elementBinder, protoElementInjector);
|
||||
}
|
||||
|
||||
private _visitBeginElement(cmd: IBeginElementCmd, elementBinder: ElementBinder,
|
||||
protoElementInjector: ProtoElementInjector): any {
|
||||
this.distanceToParentElementBinder =
|
||||
isPresent(elementBinder) ? 1 : this.distanceToParentElementBinder + 1;
|
||||
this.distanceToParentProtoElementInjector =
|
||||
isPresent(protoElementInjector) ? 1 : this.distanceToParentProtoElementInjector + 1;
|
||||
this.elementBinderStack.push(elementBinder);
|
||||
return null;
|
||||
}
|
||||
|
||||
private _visitEndElement(): any {
|
||||
var parentElementBinder = this.elementBinderStack.pop();
|
||||
var parentProtoElementInjector =
|
||||
isPresent(parentElementBinder) ? parentElementBinder.protoElementInjector : null;
|
||||
this.distanceToParentElementBinder = isPresent(parentElementBinder) ?
|
||||
parentElementBinder.distanceToParent :
|
||||
this.distanceToParentElementBinder - 1;
|
||||
this.distanceToParentProtoElementInjector = isPresent(parentProtoElementInjector) ?
|
||||
parentProtoElementInjector.distanceToParent :
|
||||
this.distanceToParentProtoElementInjector - 1;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _createElementBinder(directiveResolver: DirectiveResolver, nestedProtoView: AppProtoView,
|
||||
elementBinderStack: ElementBinder[], boundElementIndex: number,
|
||||
distanceToParentBinder: number, distanceToParentPei: number,
|
||||
beginElementCmd: IBeginElementCmd): ElementBinder {
|
||||
var parentElementBinder: ElementBinder = null;
|
||||
var parentProtoElementInjector: ProtoElementInjector = null;
|
||||
if (distanceToParentBinder > 0) {
|
||||
parentElementBinder = elementBinderStack[elementBinderStack.length - distanceToParentBinder];
|
||||
}
|
||||
if (isBlank(parentElementBinder)) {
|
||||
distanceToParentBinder = -1;
|
||||
}
|
||||
if (distanceToParentPei > 0) {
|
||||
var peiBinder = elementBinderStack[elementBinderStack.length - distanceToParentPei];
|
||||
if (isPresent(peiBinder)) {
|
||||
parentProtoElementInjector = peiBinder.protoElementInjector;
|
||||
}
|
||||
}
|
||||
if (isBlank(parentProtoElementInjector)) {
|
||||
distanceToParentPei = -1;
|
||||
}
|
||||
var componentDirectiveProvider: DirectiveProvider = null;
|
||||
var isEmbeddedTemplate = false;
|
||||
var directiveProviders: DirectiveProvider[] =
|
||||
beginElementCmd.directives.map(type => provideDirective(directiveResolver, type));
|
||||
if (beginElementCmd instanceof BeginComponentCmd) {
|
||||
componentDirectiveProvider = directiveProviders[0];
|
||||
} else if (beginElementCmd instanceof EmbeddedTemplateCmd) {
|
||||
isEmbeddedTemplate = true;
|
||||
}
|
||||
|
||||
var protoElementInjector = null;
|
||||
// Create a protoElementInjector for any element that either has bindings *or* has one
|
||||
// or more var- defined *or* for <template> elements:
|
||||
// - Elements with a var- defined need a their own element injector
|
||||
// so that, when hydrating, $implicit can be set to the element.
|
||||
// - <template> elements need their own ElementInjector so that we can query their TemplateRef
|
||||
var hasVariables = beginElementCmd.variableNameAndValues.length > 0;
|
||||
if (directiveProviders.length > 0 || hasVariables || isEmbeddedTemplate) {
|
||||
var directiveVariableBindings = new Map<string, number>();
|
||||
if (!isEmbeddedTemplate) {
|
||||
directiveVariableBindings = createDirectiveVariableBindings(
|
||||
beginElementCmd.variableNameAndValues, directiveProviders);
|
||||
}
|
||||
protoElementInjector = ProtoElementInjector.create(
|
||||
parentProtoElementInjector, boundElementIndex, directiveProviders,
|
||||
isPresent(componentDirectiveProvider), distanceToParentPei, directiveVariableBindings);
|
||||
protoElementInjector.attributes = arrayToMap(beginElementCmd.attrNameAndValues, false);
|
||||
}
|
||||
|
||||
return new ElementBinder(boundElementIndex, parentElementBinder, distanceToParentBinder,
|
||||
protoElementInjector, componentDirectiveProvider, nestedProtoView);
|
||||
}
|
||||
|
||||
function provideDirective(directiveResolver: DirectiveResolver, type: Type): DirectiveProvider {
|
||||
let annotation = directiveResolver.resolve(type);
|
||||
return DirectiveProvider.createFromType(type, annotation);
|
||||
}
|
||||
|
||||
export function createDirectiveVariableBindings(
|
||||
variableNameAndValues: Array<string | number>,
|
||||
directiveProviders: DirectiveProvider[]): Map<string, number> {
|
||||
var directiveVariableBindings = new Map<string, number>();
|
||||
for (var i = 0; i < variableNameAndValues.length; i += 2) {
|
||||
var templateName = <string>variableNameAndValues[i];
|
||||
var dirIndex = <number>variableNameAndValues[i + 1];
|
||||
if (isNumber(dirIndex)) {
|
||||
directiveVariableBindings.set(templateName, dirIndex);
|
||||
} else {
|
||||
// a variable without a directive index -> reference the element
|
||||
directiveVariableBindings.set(templateName, null);
|
||||
}
|
||||
}
|
||||
return directiveVariableBindings;
|
||||
}
|
||||
|
||||
|
||||
function arrayToMap(arr: string[], inverse: boolean): Map<string, string> {
|
||||
var result = new Map<string, string>();
|
||||
for (var i = 0; i < arr.length; i += 2) {
|
||||
if (inverse) {
|
||||
result.set(arr[i + 1], arr[i]);
|
||||
} else {
|
||||
result.set(arr[i], arr[i + 1]);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function _flattenArray(tree: any[], out: Array<Type | Provider | any[]>): void {
|
||||
for (var i = 0; i < tree.length; i++) {
|
||||
var item = resolveForwardRef(tree[i]);
|
||||
if (isArray(item)) {
|
||||
_flattenArray(item, out);
|
||||
} else {
|
||||
out.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _flattenStyleArr(arr: Array<string | any[]>, out: string[]): string[] {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var entry = arr[i];
|
||||
if (isArray(entry)) {
|
||||
_flattenStyleArr(<any[]>entry, out);
|
||||
} else {
|
||||
out.push(<string>entry);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
35
modules/angular2/src/core/linker/resolved_metadata_cache.ts
Normal file
35
modules/angular2/src/core/linker/resolved_metadata_cache.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import {Injectable} from '../di';
|
||||
import {Type, isBlank} from 'angular2/src/facade/lang';
|
||||
import {DirectiveProvider} from './element';
|
||||
import {DirectiveResolver, CODEGEN_DIRECTIVE_RESOLVER} from './directive_resolver';
|
||||
import {PipeProvider} from '../pipes/pipe_provider';
|
||||
import {PipeResolver, CODEGEN_PIPE_RESOLVER} from './pipe_resolver';
|
||||
|
||||
@Injectable()
|
||||
export class ResolvedMetadataCache {
|
||||
private _directiveCache: Map<Type, DirectiveProvider> = new Map<Type, DirectiveProvider>();
|
||||
private _pipeCache: Map<Type, PipeProvider> = new Map<Type, PipeProvider>();
|
||||
|
||||
constructor(private _directiveResolver: DirectiveResolver, private _pipeResolver: PipeResolver) {}
|
||||
|
||||
getResolvedDirectiveMetadata(type: Type): DirectiveProvider {
|
||||
var result = this._directiveCache.get(type);
|
||||
if (isBlank(result)) {
|
||||
result = DirectiveProvider.createFromType(type, this._directiveResolver.resolve(type));
|
||||
this._directiveCache.set(type, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
getResolvedPipeMetadata(type: Type): PipeProvider {
|
||||
var result = this._pipeCache.get(type);
|
||||
if (isBlank(result)) {
|
||||
result = PipeProvider.createFromType(type, this._pipeResolver.resolve(type));
|
||||
this._pipeCache.set(type, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export var CODEGEN_RESOLVED_METADATA_CACHE =
|
||||
new ResolvedMetadataCache(CODEGEN_DIRECTIVE_RESOLVER, CODEGEN_PIPE_RESOLVER);
|
@ -1,141 +0,0 @@
|
||||
import {Type, CONST_EXPR, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {
|
||||
RenderTemplateCmd,
|
||||
RenderCommandVisitor,
|
||||
RenderBeginElementCmd,
|
||||
RenderTextCmd,
|
||||
RenderNgContentCmd,
|
||||
RenderBeginComponentCmd,
|
||||
RenderEmbeddedTemplateCmd
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
// Export ViewEncapsulation so that compiled templates only need to depend
|
||||
// on template_commands.
|
||||
export {ViewEncapsulation} from 'angular2/src/core/metadata';
|
||||
|
||||
/**
|
||||
* A compiled host template.
|
||||
*
|
||||
* This is const as we are storing it as annotation
|
||||
* for the compiled component type.
|
||||
*/
|
||||
@CONST()
|
||||
export class CompiledHostTemplate {
|
||||
constructor(public template: CompiledComponentTemplate) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* A compiled template.
|
||||
*/
|
||||
@CONST()
|
||||
export class CompiledComponentTemplate {
|
||||
constructor(public id: string, public changeDetectorFactory: Function,
|
||||
public commands: TemplateCmd[], public styles: string[]) {}
|
||||
}
|
||||
|
||||
const EMPTY_ARR = CONST_EXPR([]);
|
||||
|
||||
export interface TemplateCmd extends RenderTemplateCmd {
|
||||
visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class TextCmd implements TemplateCmd, RenderTextCmd {
|
||||
constructor(public value: string, public isBound: boolean, public ngContentIndex: number) {}
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitText(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class NgContentCmd implements TemplateCmd, RenderNgContentCmd {
|
||||
isBound: boolean = false;
|
||||
constructor(public index: number, public ngContentIndex: number) {}
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitNgContent(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
export abstract class IBeginElementCmd extends RenderBeginElementCmd implements TemplateCmd {
|
||||
get variableNameAndValues(): Array<string | number> { return unimplemented(); }
|
||||
get eventTargetAndNames(): string[] { return unimplemented(); }
|
||||
get directives(): Type[] { return unimplemented(); }
|
||||
abstract visit(visitor: RenderCommandVisitor, context: any): any;
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class BeginElementCmd implements TemplateCmd, IBeginElementCmd, RenderBeginElementCmd {
|
||||
constructor(public name: string, public attrNameAndValues: string[],
|
||||
public eventTargetAndNames: string[],
|
||||
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||
public isBound: boolean, public ngContentIndex: number) {}
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitBeginElement(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@CONST()
|
||||
export class EndElementCmd implements TemplateCmd {
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitEndElement(context);
|
||||
}
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class BeginComponentCmd implements TemplateCmd, IBeginElementCmd, RenderBeginComponentCmd {
|
||||
isBound: boolean = true;
|
||||
constructor(public name: string, public attrNameAndValues: string[],
|
||||
public eventTargetAndNames: string[],
|
||||
public variableNameAndValues: Array<string | number>, public directives: Type[],
|
||||
public encapsulation: ViewEncapsulation, public ngContentIndex: number,
|
||||
// Note: the template needs to be stored as a function
|
||||
// so that we can resolve cycles
|
||||
public templateGetter: Function /*() => CompiledComponentTemplate*/) {}
|
||||
|
||||
get templateId(): string { return this.templateGetter().id; }
|
||||
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitBeginComponent(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class EndComponentCmd implements TemplateCmd {
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitEndComponent(context);
|
||||
}
|
||||
}
|
||||
|
||||
@CONST()
|
||||
export class EmbeddedTemplateCmd implements TemplateCmd, IBeginElementCmd,
|
||||
RenderEmbeddedTemplateCmd {
|
||||
isBound: boolean = true;
|
||||
name: string = null;
|
||||
eventTargetAndNames: string[] = EMPTY_ARR;
|
||||
constructor(public attrNameAndValues: string[], public variableNameAndValues: string[],
|
||||
public directives: Type[], public isMerged: boolean, public ngContentIndex: number,
|
||||
public changeDetectorFactory: Function, public children: TemplateCmd[]) {}
|
||||
visit(visitor: RenderCommandVisitor, context: any): any {
|
||||
return visitor.visitEmbeddedTemplate(this, context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export interface CommandVisitor extends RenderCommandVisitor {
|
||||
visitText(cmd: TextCmd, context: any): any;
|
||||
visitNgContent(cmd: NgContentCmd, context: any): any;
|
||||
visitBeginElement(cmd: BeginElementCmd, context: any): any;
|
||||
visitEndElement(context: any): any;
|
||||
visitBeginComponent(cmd: BeginComponentCmd, context: any): any;
|
||||
visitEndComponent(context: any): any;
|
||||
visitEmbeddedTemplate(cmd: EmbeddedTemplateCmd, context: any): any;
|
||||
}
|
||||
|
||||
export function visitAllCommands(visitor: CommandVisitor, cmds: TemplateCmd[],
|
||||
context: any = null) {
|
||||
for (var i = 0; i < cmds.length; i++) {
|
||||
cmds[i].visit(visitor, context);
|
||||
}
|
||||
}
|
@ -1,6 +1,4 @@
|
||||
import {internalView, ProtoViewRef} from './view_ref';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import * as viewModule from './view';
|
||||
|
||||
/**
|
||||
* Represents an Embedded Template that can be used to instantiate Embedded Views.
|
||||
@ -27,34 +25,11 @@ export abstract class TemplateRef {
|
||||
*
|
||||
*/
|
||||
// TODO(i): rename to anchor or location
|
||||
elementRef: ElementRef;
|
||||
|
||||
/**
|
||||
* Allows you to check if this Embedded Template defines Local Variable with name matching `name`.
|
||||
*/
|
||||
abstract hasLocal(name: string): boolean;
|
||||
get elementRef(): ElementRef { return null; }
|
||||
}
|
||||
|
||||
export class TemplateRef_ extends TemplateRef {
|
||||
constructor(elementRef: ElementRef) {
|
||||
super();
|
||||
this.elementRef = elementRef;
|
||||
}
|
||||
constructor(private _elementRef: ElementRef_) { super(); }
|
||||
|
||||
private _getProtoView(): viewModule.AppProtoView {
|
||||
let elementRef = <ElementRef_>this.elementRef;
|
||||
var parentView = internalView(elementRef.parentView);
|
||||
return parentView.proto.elementBinders[elementRef.boundElementIndex - parentView.elementOffset]
|
||||
.nestedProtoView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reference to the ProtoView used for creating Embedded Views that are based on the compiled
|
||||
* Embedded Template.
|
||||
*/
|
||||
get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
|
||||
|
||||
hasLocal(name: string): boolean {
|
||||
return this._getProtoView().templateVariableBindings.has(name);
|
||||
}
|
||||
get elementRef(): ElementRef_ { return this._elementRef; }
|
||||
}
|
||||
|
@ -10,86 +10,53 @@ import {
|
||||
DirectiveIndex,
|
||||
BindingTarget,
|
||||
Locals,
|
||||
ProtoChangeDetector
|
||||
ProtoChangeDetector,
|
||||
ChangeDetectorRef
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ResolvedProvider, Injectable, Injector} from 'angular2/src/core/di';
|
||||
import {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
||||
|
||||
import {AppProtoElement, AppElement, DirectiveProvider} from './element';
|
||||
import {
|
||||
ProtoElementInjector,
|
||||
ElementInjector,
|
||||
PreBuiltObjects,
|
||||
DirectiveProvider
|
||||
} from './element_injector';
|
||||
import {ElementBinder} from './element_binder';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
isPresent,
|
||||
isBlank,
|
||||
Type,
|
||||
isArray,
|
||||
isNumber,
|
||||
CONST,
|
||||
CONST_EXPR
|
||||
} from 'angular2/src/facade/lang';
|
||||
import {BaseException, WrappedException} from 'angular2/src/facade/exceptions';
|
||||
import * as renderApi from 'angular2/src/core/render/api';
|
||||
import {RenderEventDispatcher} from 'angular2/src/core/render/api';
|
||||
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {Renderer, RootRenderer} from 'angular2/src/core/render/api';
|
||||
import {ViewRef_, HostViewFactoryRef} from './view_ref';
|
||||
import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
|
||||
import {camelCaseToDashCase} from 'angular2/src/core/render/util';
|
||||
import {TemplateCmd} from './template_commands';
|
||||
import {ViewRef_, ProtoViewRef_} from "./view_ref";
|
||||
|
||||
export {DebugContext} from 'angular2/src/core/change_detection/interfaces';
|
||||
import {Pipes} from 'angular2/src/core/pipes/pipes';
|
||||
import {AppViewManager_, AppViewManager} from './view_manager';
|
||||
import {ResolvedMetadataCache} from './resolved_metadata_cache';
|
||||
import {ViewType} from './view_type';
|
||||
|
||||
const REFLECT_PREFIX: string = 'ng-reflect-';
|
||||
|
||||
export enum ViewType {
|
||||
// A view that contains the host element with bound component directive.
|
||||
// Contains a COMPONENT view
|
||||
HOST,
|
||||
// The view of the component
|
||||
// Can contain 0 to n EMBEDDED views
|
||||
COMPONENT,
|
||||
// A view that is embedded into another View via a <template> element
|
||||
// inside of a COMPONENT view
|
||||
EMBEDDED
|
||||
}
|
||||
|
||||
export class AppViewContainer {
|
||||
// The order in this list matches the DOM order.
|
||||
views: AppView[] = [];
|
||||
}
|
||||
const EMPTY_CONTEXT = CONST_EXPR(new Object());
|
||||
|
||||
/**
|
||||
* Cost of making objects: http://jsperf.com/instantiate-size-of-object
|
||||
*
|
||||
*/
|
||||
export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
// AppViews that have been merged in depth first order.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
views: AppView[] = null;
|
||||
// root elementInjectors of this AppView
|
||||
// This list is local to this AppView and not shared with other Views.
|
||||
rootElementInjectors: ElementInjector[];
|
||||
// ElementInjectors of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
elementInjectors: ElementInjector[] = null;
|
||||
// ViewContainers of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
viewContainers: AppViewContainer[] = null;
|
||||
// PreBuiltObjects of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
preBuiltObjects: PreBuiltObjects[] = null;
|
||||
// ElementRef of all AppViews in views grouped by view.
|
||||
// This list is shared between all merged views. Use this.elementOffset to get the local
|
||||
// entries.
|
||||
elementRefs: ElementRef[];
|
||||
|
||||
ref: ViewRef;
|
||||
changeDetector: ChangeDetector = null;
|
||||
export class AppView implements ChangeDispatcher {
|
||||
ref: ViewRef_;
|
||||
rootNodesOrAppElements: any[];
|
||||
allNodes: any[];
|
||||
disposables: Function[];
|
||||
appElements: AppElement[];
|
||||
|
||||
/**
|
||||
* The context against which data-binding expressions in this view are evaluated against.
|
||||
* This is always a component instance.
|
||||
*/
|
||||
|
||||
context: any = null;
|
||||
|
||||
/**
|
||||
@ -99,70 +66,133 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
*/
|
||||
locals: Locals;
|
||||
|
||||
constructor(public renderer: renderApi.Renderer, public proto: AppProtoView,
|
||||
public viewOffset: number, public elementOffset: number, public textOffset: number,
|
||||
protoLocals: Map<string, any>, public render: renderApi.RenderViewRef,
|
||||
public renderFragment: renderApi.RenderFragmentRef,
|
||||
public containerElementInjector: ElementInjector) {
|
||||
this.ref = new ViewRef_(this);
|
||||
pipes: Pipes;
|
||||
|
||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); // TODO optimize this
|
||||
parentInjector: Injector;
|
||||
|
||||
/**
|
||||
* Whether root injectors of this view
|
||||
* have a hostBoundary.
|
||||
*/
|
||||
hostInjectorBoundary: boolean;
|
||||
|
||||
destroyed: boolean = false;
|
||||
|
||||
constructor(public proto: AppProtoView, public renderer: Renderer,
|
||||
public viewManager: AppViewManager_, public projectableNodes: Array<any | any[]>,
|
||||
public containerAppElement: AppElement,
|
||||
imperativelyCreatedProviders: ResolvedProvider[], rootInjector: Injector,
|
||||
public changeDetector: ChangeDetector) {
|
||||
this.ref = new ViewRef_(this);
|
||||
var injectorWithHostBoundary = AppElement.getViewParentInjector(
|
||||
this.proto.type, containerAppElement, imperativelyCreatedProviders, rootInjector);
|
||||
this.parentInjector = injectorWithHostBoundary.injector;
|
||||
this.hostInjectorBoundary = injectorWithHostBoundary.hostInjectorBoundary;
|
||||
var pipes;
|
||||
var context;
|
||||
switch (proto.type) {
|
||||
case ViewType.COMPONENT:
|
||||
pipes = new Pipes(proto.protoPipes, containerAppElement.getInjector());
|
||||
context = containerAppElement.getComponent();
|
||||
break;
|
||||
case ViewType.EMBEDDED:
|
||||
pipes = containerAppElement.parentView.pipes;
|
||||
context = containerAppElement.parentView.context;
|
||||
break;
|
||||
case ViewType.HOST:
|
||||
pipes = null;
|
||||
context = EMPTY_CONTEXT;
|
||||
break;
|
||||
}
|
||||
this.pipes = pipes;
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
init(changeDetector: ChangeDetector, elementInjectors: ElementInjector[],
|
||||
rootElementInjectors: ElementInjector[], preBuiltObjects: PreBuiltObjects[],
|
||||
views: AppView[], elementRefs: ElementRef[], viewContainers: AppViewContainer[]) {
|
||||
this.changeDetector = changeDetector;
|
||||
this.elementInjectors = elementInjectors;
|
||||
this.rootElementInjectors = rootElementInjectors;
|
||||
this.preBuiltObjects = preBuiltObjects;
|
||||
this.views = views;
|
||||
this.elementRefs = elementRefs;
|
||||
this.viewContainers = viewContainers;
|
||||
init(rootNodesOrAppElements: any[], allNodes: any[], disposables: Function[],
|
||||
appElements: AppElement[]) {
|
||||
this.rootNodesOrAppElements = rootNodesOrAppElements;
|
||||
this.allNodes = allNodes;
|
||||
this.disposables = disposables;
|
||||
this.appElements = appElements;
|
||||
var localsMap = new Map<string, any>();
|
||||
StringMapWrapper.forEach(this.proto.templateVariableBindings,
|
||||
(templateName, _) => { localsMap.set(templateName, null); });
|
||||
for (var i = 0; i < appElements.length; i++) {
|
||||
var appEl = appElements[i];
|
||||
StringMapWrapper.forEach(appEl.proto.directiveVariableBindings, (directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
localsMap.set(name, appEl.nativeElement);
|
||||
} else {
|
||||
localsMap.set(name, appEl.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
var parentLocals = null;
|
||||
if (this.proto.type !== ViewType.COMPONENT) {
|
||||
parentLocals =
|
||||
isPresent(this.containerAppElement) ? this.containerAppElement.parentView.locals : null;
|
||||
}
|
||||
if (this.proto.type === ViewType.COMPONENT) {
|
||||
// Note: the render nodes have been attached to their host element
|
||||
// in the ViewFactory already.
|
||||
this.containerAppElement.attachComponentView(this);
|
||||
this.containerAppElement.parentView.changeDetector.addViewChild(this.changeDetector);
|
||||
}
|
||||
this.locals = new Locals(parentLocals, localsMap);
|
||||
this.changeDetector.hydrate(this.context, this.locals, this, this.pipes);
|
||||
this.viewManager.onViewCreated(this);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.destroyed) {
|
||||
throw new BaseException('This view has already been destroyed!');
|
||||
}
|
||||
this.changeDetector.destroyRecursive();
|
||||
}
|
||||
|
||||
notifyOnDestroy() {
|
||||
this.destroyed = true;
|
||||
var hostElement =
|
||||
this.proto.type === ViewType.COMPONENT ? this.containerAppElement.nativeElement : null;
|
||||
this.renderer.destroyView(hostElement, this.allNodes);
|
||||
for (var i = 0; i < this.disposables.length; i++) {
|
||||
this.disposables[i]();
|
||||
}
|
||||
this.viewManager.onViewDestroyed(this);
|
||||
}
|
||||
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this.changeDetector.ref; }
|
||||
|
||||
get flatRootNodes(): any[] { return flattenNestedViewRenderNodes(this.rootNodesOrAppElements); }
|
||||
|
||||
hasLocal(contextName: string): boolean {
|
||||
return StringMapWrapper.contains(this.proto.templateVariableBindings, contextName);
|
||||
}
|
||||
|
||||
setLocal(contextName: string, value: any): void {
|
||||
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
|
||||
if (!this.proto.templateVariableBindings.has(contextName)) {
|
||||
if (!this.hasLocal(contextName)) {
|
||||
return;
|
||||
}
|
||||
var templateName = this.proto.templateVariableBindings.get(contextName);
|
||||
var templateName = this.proto.templateVariableBindings[contextName];
|
||||
this.locals.set(templateName, value);
|
||||
}
|
||||
|
||||
hydrated(): boolean { return isPresent(this.context); }
|
||||
|
||||
/**
|
||||
* Triggers the event handlers for the element and the directives.
|
||||
*
|
||||
* This method is intended to be called from directive EventEmitters.
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {*} eventObj
|
||||
* @param {number} boundElementIndex
|
||||
*/
|
||||
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): void {
|
||||
var locals = new Map<string, any>();
|
||||
locals.set('$event', eventObj);
|
||||
this.dispatchEvent(boundElementIndex, eventName, locals);
|
||||
}
|
||||
|
||||
// dispatch to element injector or text nodes based on context
|
||||
notifyOnBinding(b: BindingTarget, currentValue: any): void {
|
||||
if (b.isTextNode()) {
|
||||
this.renderer.setText(this.render, b.elementIndex + this.textOffset, currentValue);
|
||||
this.renderer.setText(this.allNodes[b.elementIndex], currentValue);
|
||||
} else {
|
||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
||||
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
||||
if (b.isElementProperty()) {
|
||||
this.renderer.setElementProperty(elementRef, b.name, currentValue);
|
||||
this.renderer.setElementProperty(nativeElement, b.name, currentValue);
|
||||
} else if (b.isElementAttribute()) {
|
||||
this.renderer.setElementAttribute(elementRef, b.name,
|
||||
this.renderer.setElementAttribute(nativeElement, b.name,
|
||||
isPresent(currentValue) ? `${currentValue}` : null);
|
||||
} else if (b.isElementClass()) {
|
||||
this.renderer.setElementClass(elementRef, b.name, currentValue);
|
||||
this.renderer.setElementClass(nativeElement, b.name, currentValue);
|
||||
} else if (b.isElementStyle()) {
|
||||
var unit = isPresent(b.unit) ? b.unit : '';
|
||||
this.renderer.setElementStyle(elementRef, b.name,
|
||||
this.renderer.setElementStyle(nativeElement, b.name,
|
||||
isPresent(currentValue) ? `${currentValue}${unit}` : null);
|
||||
} else {
|
||||
throw new BaseException('Unsupported directive record');
|
||||
@ -172,57 +202,39 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
|
||||
logBindingUpdate(b: BindingTarget, value: any): void {
|
||||
if (b.isDirective() || b.isElementProperty()) {
|
||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
||||
var nativeElement = this.appElements[b.elementIndex].nativeElement;
|
||||
this.renderer.setBindingDebugInfo(
|
||||
elementRef, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
||||
nativeElement, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
|
||||
}
|
||||
}
|
||||
|
||||
notifyAfterContentChecked(): void {
|
||||
var eiCount = this.proto.elementBinders.length;
|
||||
var ei = this.elementInjectors;
|
||||
for (var i = eiCount - 1; i >= 0; i--) {
|
||||
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterContentChecked();
|
||||
var count = this.appElements.length;
|
||||
for (var i = count - 1; i >= 0; i--) {
|
||||
this.appElements[i].ngAfterContentChecked();
|
||||
}
|
||||
}
|
||||
|
||||
notifyAfterViewChecked(): void {
|
||||
var eiCount = this.proto.elementBinders.length;
|
||||
var ei = this.elementInjectors;
|
||||
for (var i = eiCount - 1; i >= 0; i--) {
|
||||
if (isPresent(ei[i + this.elementOffset])) ei[i + this.elementOffset].ngAfterViewChecked();
|
||||
var count = this.appElements.length;
|
||||
for (var i = count - 1; i >= 0; i--) {
|
||||
this.appElements[i].ngAfterViewChecked();
|
||||
}
|
||||
}
|
||||
|
||||
getDirectiveFor(directive: DirectiveIndex): any {
|
||||
var elementInjector = this.elementInjectors[this.elementOffset + directive.elementIndex];
|
||||
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
|
||||
}
|
||||
|
||||
getNestedView(boundElementIndex: number): AppView {
|
||||
var eli = this.elementInjectors[boundElementIndex];
|
||||
return isPresent(eli) ? eli.getNestedView() : null;
|
||||
}
|
||||
|
||||
getContainerElement(): ElementRef {
|
||||
return isPresent(this.containerElementInjector) ?
|
||||
this.containerElementInjector.getElementRef() :
|
||||
null;
|
||||
}
|
||||
|
||||
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext {
|
||||
getDebugContext(appElement: AppElement, elementIndex: number,
|
||||
directiveIndex: number): DebugContext {
|
||||
try {
|
||||
var offsettedIndex = this.elementOffset + elementIndex;
|
||||
var hasRefForIndex = offsettedIndex < this.elementRefs.length;
|
||||
if (isBlank(appElement) && elementIndex < this.appElements.length) {
|
||||
appElement = this.appElements[elementIndex];
|
||||
}
|
||||
var container = this.containerAppElement;
|
||||
|
||||
var elementRef = hasRefForIndex ? this.elementRefs[this.elementOffset + elementIndex] : null;
|
||||
var container = this.getContainerElement();
|
||||
var ei = hasRefForIndex ? this.elementInjectors[this.elementOffset + elementIndex] : null;
|
||||
|
||||
var element = isPresent(elementRef) ? elementRef.nativeElement : null;
|
||||
var element = isPresent(appElement) ? appElement.nativeElement : null;
|
||||
var componentElement = isPresent(container) ? container.nativeElement : null;
|
||||
var directive = isPresent(directiveIndex) ? this.getDirectiveFor(directiveIndex) : null;
|
||||
var injector = isPresent(ei) ? ei.getInjector() : null;
|
||||
var directive =
|
||||
isPresent(directiveIndex) ? appElement.getDirectiveAtIndex(directiveIndex) : null;
|
||||
var injector = isPresent(appElement) ? appElement.getInjector() : null;
|
||||
|
||||
return new DebugContext(element, componentElement, directive, this.context,
|
||||
_localsToStringMap(this.locals), injector);
|
||||
@ -234,43 +246,28 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
||||
}
|
||||
}
|
||||
|
||||
getDetectorFor(directive: DirectiveIndex): any {
|
||||
var childView = this.getNestedView(this.elementOffset + directive.elementIndex);
|
||||
return isPresent(childView) ? childView.changeDetector : null;
|
||||
getDirectiveFor(directive: DirectiveIndex): any {
|
||||
return this.appElements[directive.elementIndex].getDirectiveAtIndex(directive.directiveIndex);
|
||||
}
|
||||
|
||||
invokeElementMethod(elementIndex: number, methodName: string, args: any[]) {
|
||||
this.renderer.invokeElementMethod(this.elementRefs[elementIndex], methodName, args);
|
||||
getDetectorFor(directive: DirectiveIndex): ChangeDetector {
|
||||
var componentView = this.appElements[directive.elementIndex].componentView;
|
||||
return isPresent(componentView) ? componentView.changeDetector : null;
|
||||
}
|
||||
|
||||
// implementation of RenderEventDispatcher#dispatchRenderEvent
|
||||
dispatchRenderEvent(boundElementIndex: number, eventName: string,
|
||||
locals: Map<string, any>): boolean {
|
||||
var elementRef = this.elementRefs[boundElementIndex];
|
||||
var view = internalView(elementRef.parentView);
|
||||
return view.dispatchEvent(elementRef.boundElementIndex, eventName, locals);
|
||||
/**
|
||||
* Triggers the event handlers for the element and the directives.
|
||||
*
|
||||
* This method is intended to be called from directive EventEmitters.
|
||||
*
|
||||
* @param {string} eventName
|
||||
* @param {*} eventObj
|
||||
* @param {number} boundElementIndex
|
||||
* @return false if preventDefault must be applied to the DOM event
|
||||
*/
|
||||
triggerEventHandlers(eventName: string, eventObj: Event, boundElementIndex: number): boolean {
|
||||
return this.changeDetector.handleEvent(eventName, boundElementIndex, eventObj);
|
||||
}
|
||||
|
||||
|
||||
// returns false if preventDefault must be applied to the DOM event
|
||||
dispatchEvent(boundElementIndex: number, eventName: string, locals: Map<string, any>): boolean {
|
||||
try {
|
||||
if (this.hydrated()) {
|
||||
return !this.changeDetector.handleEvent(eventName, boundElementIndex - this.elementOffset,
|
||||
new Locals(this.locals, locals));
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
var c = this.getDebugContext(boundElementIndex - this.elementOffset, null);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||
c.injector) :
|
||||
null;
|
||||
throw new EventEvaluationError(eventName, e, e.stack, context);
|
||||
}
|
||||
}
|
||||
|
||||
get ownBindersCount(): number { return this.proto.elementBinders.length; }
|
||||
}
|
||||
|
||||
function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
||||
@ -283,69 +280,61 @@ function _localsToStringMap(locals: Locals): {[key: string]: any} {
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Error context included when an event handler throws an exception.
|
||||
*/
|
||||
class _Context {
|
||||
constructor(public element: any, public componentElement: any, public context: any,
|
||||
public locals: any, public injector: any) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an exception thrown by an event handler.
|
||||
*/
|
||||
class EventEvaluationError extends WrappedException {
|
||||
constructor(eventName: string, originalException: any, originalStack: any, context: any) {
|
||||
super(`Error during evaluation of "${eventName}"`, originalException, originalStack, context);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppProtoViewMergeInfo {
|
||||
constructor(public embeddedViewCount: number, public elementCount: number,
|
||||
public viewCount: number) {}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
export class AppProtoView {
|
||||
ref: ProtoViewRef;
|
||||
protoLocals: Map<string, any>;
|
||||
|
||||
elementBinders: ElementBinder[] = null;
|
||||
mergeInfo: AppProtoViewMergeInfo = null;
|
||||
variableLocations: Map<string, number> = null;
|
||||
textBindingCount = null;
|
||||
render: renderApi.RenderProtoViewRef = null;
|
||||
|
||||
constructor(public templateId: string, public templateCmds: TemplateCmd[], public type: ViewType,
|
||||
public isMergable: boolean, public changeDetectorFactory: Function,
|
||||
public templateVariableBindings: Map<string, string>, public pipes: ProtoPipes) {
|
||||
this.ref = new ProtoViewRef_(this);
|
||||
static create(metadataCache: ResolvedMetadataCache, type: ViewType, pipes: Type[],
|
||||
templateVariableBindings: {[key: string]: string}): AppProtoView {
|
||||
var protoPipes = null;
|
||||
if (isPresent(pipes) && pipes.length > 0) {
|
||||
var boundPipes = ListWrapper.createFixedSize(pipes.length);
|
||||
for (var i = 0; i < pipes.length; i++) {
|
||||
boundPipes[i] = metadataCache.getResolvedPipeMetadata(pipes[i]);
|
||||
}
|
||||
protoPipes = ProtoPipes.fromProviders(boundPipes);
|
||||
}
|
||||
return new AppProtoView(type, protoPipes, templateVariableBindings);
|
||||
}
|
||||
|
||||
init(render: renderApi.RenderProtoViewRef, elementBinders: ElementBinder[],
|
||||
textBindingCount: number, mergeInfo: AppProtoViewMergeInfo,
|
||||
variableLocations: Map<string, number>) {
|
||||
this.render = render;
|
||||
this.elementBinders = elementBinders;
|
||||
this.textBindingCount = textBindingCount;
|
||||
this.mergeInfo = mergeInfo;
|
||||
this.variableLocations = variableLocations;
|
||||
this.protoLocals = new Map<string, any>();
|
||||
if (isPresent(this.templateVariableBindings)) {
|
||||
this.templateVariableBindings.forEach(
|
||||
(templateName, _) => { this.protoLocals.set(templateName, null); });
|
||||
}
|
||||
if (isPresent(variableLocations)) {
|
||||
// The view's locals needs to have a full set of variable names at construction time
|
||||
// in order to prevent new variables from being set later in the lifecycle. Since we don't
|
||||
// want
|
||||
// to actually create variable bindings for the $implicit bindings, add to the
|
||||
// protoLocals manually.
|
||||
variableLocations.forEach((_, templateName) => { this.protoLocals.set(templateName, null); });
|
||||
}
|
||||
}
|
||||
|
||||
isInitialized(): boolean { return isPresent(this.elementBinders); }
|
||||
constructor(public type: ViewType, public protoPipes: ProtoPipes,
|
||||
public templateVariableBindings: {[key: string]: string}) {}
|
||||
}
|
||||
|
||||
|
||||
@CONST()
|
||||
export class HostViewFactory {
|
||||
constructor(public selector: string, public viewFactory: Function) {}
|
||||
}
|
||||
|
||||
export function flattenNestedViewRenderNodes(nodes: any[]): any[] {
|
||||
return _flattenNestedViewRenderNodes(nodes, []);
|
||||
}
|
||||
|
||||
function _flattenNestedViewRenderNodes(nodes: any[], renderNodes: any[]): any[] {
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
var node = nodes[i];
|
||||
if (node instanceof AppElement) {
|
||||
var appEl = <AppElement>node;
|
||||
renderNodes.push(appEl.nativeElement);
|
||||
if (isPresent(appEl.nestedViews)) {
|
||||
for (var k = 0; k < appEl.nestedViews.length; k++) {
|
||||
_flattenNestedViewRenderNodes(appEl.nestedViews[k].rootNodesOrAppElements, renderNodes);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
renderNodes.push(node);
|
||||
}
|
||||
}
|
||||
return renderNodes;
|
||||
}
|
||||
|
||||
export function checkSlotCount(componentName: string, expectedSlotCount: number,
|
||||
projectableNodes: any[][]): void {
|
||||
var givenSlotCount = isPresent(projectableNodes) ? projectableNodes.length : 0;
|
||||
if (givenSlotCount < expectedSlotCount) {
|
||||
throw new BaseException(
|
||||
`The component ${componentName} has ${expectedSlotCount} <ng-content> elements,` +
|
||||
` but only ${givenSlotCount} slots were provided.`);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,18 @@ import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import {ResolvedProvider} from 'angular2/src/core/di';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
import * as avmModule from './view_manager';
|
||||
import * as viewModule from './view';
|
||||
import {AppElement} from './element';
|
||||
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {TemplateRef} from './template_ref';
|
||||
import {ViewRef, HostViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {
|
||||
EmbeddedViewRef,
|
||||
HostViewRef,
|
||||
HostViewFactoryRef,
|
||||
HostViewFactoryRef_,
|
||||
ViewRef,
|
||||
ViewRef_
|
||||
} from './view_ref';
|
||||
|
||||
/**
|
||||
* Represents a container where one or more Views can be attached.
|
||||
@ -35,7 +41,7 @@ export abstract class ViewContainerRef {
|
||||
* Anchor element that specifies the location of this container in the containing View.
|
||||
* <!-- TODO: rename to anchorElement -->
|
||||
*/
|
||||
public element: ElementRef;
|
||||
get element(): ElementRef { return unimplemented(); }
|
||||
|
||||
/**
|
||||
* Destroys all Views in this container.
|
||||
@ -64,7 +70,7 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* Returns the {@link ViewRef} for the newly created View.
|
||||
*/
|
||||
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): ViewRef;
|
||||
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* Instantiates a single {@link Component} and inserts its Host View into this container at the
|
||||
@ -80,8 +86,9 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
||||
*/
|
||||
abstract createHostView(protoViewRef?: ProtoViewRef, index?: number,
|
||||
dynamicallyCreatedProviders?: ResolvedProvider[]): HostViewRef;
|
||||
abstract createHostView(hostViewFactoryRef: HostViewFactoryRef, index?: number,
|
||||
dynamicallyCreatedProviders?: ResolvedProvider[],
|
||||
projectableNodes?: any[][]): HostViewRef;
|
||||
|
||||
/**
|
||||
* Inserts a View identified by a {@link ViewRef} into the container at the specified `index`.
|
||||
@ -90,7 +97,7 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* Returns the inserted {@link ViewRef}.
|
||||
*/
|
||||
abstract insert(viewRef: ViewRef, index?: number): ViewRef;
|
||||
abstract insert(viewRef: EmbeddedViewRef, index?: number): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* Returns the index of the View, specified via {@link ViewRef}, within the current container or
|
||||
@ -110,58 +117,60 @@ export abstract class ViewContainerRef {
|
||||
*
|
||||
* If the `index` param is omitted, the last {@link ViewRef} is detached.
|
||||
*/
|
||||
abstract detach(index?: number): ViewRef;
|
||||
abstract detach(index?: number): EmbeddedViewRef;
|
||||
}
|
||||
|
||||
export class ViewContainerRef_ extends ViewContainerRef {
|
||||
constructor(public viewManager: avmModule.AppViewManager, element: ElementRef) {
|
||||
super();
|
||||
this.element = element;
|
||||
constructor(private _element: AppElement) { super(); }
|
||||
|
||||
get(index: number): EmbeddedViewRef { return this._element.nestedViews[index].ref; }
|
||||
get length(): number {
|
||||
var views = this._element.nestedViews;
|
||||
return isPresent(views) ? views.length : 0;
|
||||
}
|
||||
|
||||
private _getViews(): Array<viewModule.AppView> {
|
||||
let element = <ElementRef_>this.element;
|
||||
var vc = internalView(element.parentView).viewContainers[element.boundElementIndex];
|
||||
return isPresent(vc) ? vc.views : [];
|
||||
}
|
||||
|
||||
get(index: number): ViewRef { return this._getViews()[index].ref; }
|
||||
get length(): number { return this._getViews().length; }
|
||||
get element(): ElementRef_ { return this._element.ref; }
|
||||
|
||||
// TODO(rado): profile and decide whether bounds checks should be added
|
||||
// to the methods below.
|
||||
createEmbeddedView(templateRef: TemplateRef, index: number = -1): ViewRef {
|
||||
createEmbeddedView(templateRef: TemplateRef, index: number = -1): EmbeddedViewRef {
|
||||
if (index == -1) index = this.length;
|
||||
return this.viewManager.createEmbeddedViewInContainer(this.element, index, templateRef);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.createEmbeddedViewInContainer(this._element.ref, index, templateRef);
|
||||
}
|
||||
|
||||
createHostView(protoViewRef: ProtoViewRef = null, index: number = -1,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[] = null): HostViewRef {
|
||||
createHostView(hostViewFactoryRef: HostViewFactoryRef, index: number = -1,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[] = null,
|
||||
projectableNodes: any[][] = null): HostViewRef {
|
||||
if (index == -1) index = this.length;
|
||||
return this.viewManager.createHostViewInContainer(this.element, index, protoViewRef,
|
||||
dynamicallyCreatedProviders);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.createHostViewInContainer(this._element.ref, index, hostViewFactoryRef,
|
||||
dynamicallyCreatedProviders, projectableNodes);
|
||||
}
|
||||
|
||||
// TODO(i): refactor insert+remove into move
|
||||
insert(viewRef: ViewRef, index: number = -1): ViewRef {
|
||||
insert(viewRef: ViewRef, index: number = -1): EmbeddedViewRef {
|
||||
if (index == -1) index = this.length;
|
||||
return this.viewManager.attachViewInContainer(this.element, index, viewRef);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.attachViewInContainer(this._element.ref, index, viewRef);
|
||||
}
|
||||
|
||||
indexOf(viewRef: ViewRef): number {
|
||||
return ListWrapper.indexOf(this._getViews(), internalView(viewRef));
|
||||
return ListWrapper.indexOf(this._element.nestedViews, (<ViewRef_>viewRef).internalView);
|
||||
}
|
||||
|
||||
// TODO(i): rename to destroy
|
||||
remove(index: number = -1): void {
|
||||
if (index == -1) index = this.length - 1;
|
||||
this.viewManager.destroyViewInContainer(this.element, index);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.destroyViewInContainer(this._element.ref, index);
|
||||
// view is intentionally not returned to the client.
|
||||
}
|
||||
|
||||
// TODO(i): refactor insert+remove into move
|
||||
detach(index: number = -1): ViewRef {
|
||||
detach(index: number = -1): EmbeddedViewRef {
|
||||
if (index == -1) index = this.length - 1;
|
||||
return this.viewManager.detachViewInContainer(this.element, index);
|
||||
var vm = this._element.parentView.viewManager;
|
||||
return vm.detachViewInContainer(this._element.ref, index);
|
||||
}
|
||||
}
|
||||
|
@ -6,24 +6,28 @@ import {
|
||||
ResolvedProvider,
|
||||
forwardRef
|
||||
} from 'angular2/src/core/di';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {isPresent, isBlank, isArray} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as viewModule from './view';
|
||||
import {AppView, HostViewFactory, flattenNestedViewRenderNodes} from './view';
|
||||
import {AppElement} from './element';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {ProtoViewRef, ViewRef, HostViewRef, internalView, internalProtoView} from './view_ref';
|
||||
import {
|
||||
HostViewFactoryRef,
|
||||
HostViewFactoryRef_,
|
||||
EmbeddedViewRef,
|
||||
HostViewRef,
|
||||
ViewRef,
|
||||
ViewRef_
|
||||
} from './view_ref';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
RenderFragmentRef,
|
||||
RenderViewWithFragments
|
||||
} from 'angular2/src/core/render/api';
|
||||
import {AppViewManagerUtils} from './view_manager_utils';
|
||||
import {AppViewPool} from './view_pool';
|
||||
import {AppViewListener} from './view_listener';
|
||||
import {RootRenderer, RenderComponentType} from 'angular2/src/core/render/api';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
import {ProtoViewFactory} from './proto_view_factory';
|
||||
import {APP_ID} from 'angular2/src/core/application_tokens';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {ViewType} from './view_type';
|
||||
|
||||
/**
|
||||
* Service exposing low level API for creating, moving and destroying Views.
|
||||
@ -40,13 +44,7 @@ export abstract class AppViewManager {
|
||||
/**
|
||||
* Returns the {@link ElementRef} that makes up the specified Host View.
|
||||
*/
|
||||
getHostElement(hostViewRef: HostViewRef): ElementRef {
|
||||
var hostView = internalView(<ViewRef>hostViewRef);
|
||||
if (hostView.proto.type !== viewModule.ViewType.HOST) {
|
||||
throw new BaseException('This operation is only allowed on host views');
|
||||
}
|
||||
return hostView.elementRefs[hostView.elementOffset];
|
||||
}
|
||||
abstract getHostElement(hostViewRef: HostViewRef): ElementRef;
|
||||
|
||||
/**
|
||||
* Searches the Component View of the Component specified via `hostLocation` and returns the
|
||||
@ -70,7 +68,8 @@ export abstract class AppViewManager {
|
||||
* This as a low-level way to bootstrap an application and upgrade an existing Element to a
|
||||
* Host Element. Most applications should use {@link DynamicComponentLoader#loadAsRoot} instead.
|
||||
*
|
||||
* The Component and its View are created based on the `hostProtoViewRef` which can be obtained
|
||||
* The Component and its View are created based on the `hostProtoComponentRef` which can be
|
||||
* obtained
|
||||
* by compiling the component with {@link Compiler#compileInHost}.
|
||||
*
|
||||
* Use {@link AppViewManager#destroyRootHostView} to destroy the created Component and it's Host
|
||||
@ -101,7 +100,7 @@ export abstract class AppViewManager {
|
||||
* viewRef: ng.ViewRef;
|
||||
*
|
||||
* constructor(public appViewManager: ng.AppViewManager, compiler: ng.Compiler) {
|
||||
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoViewRef) => {
|
||||
* compiler.compileInHost(ChildComponent).then((protoView: ng.ProtoComponentRef) => {
|
||||
* this.viewRef = appViewManager.createRootHostView(protoView, 'some-component', null);
|
||||
* })
|
||||
* }
|
||||
@ -115,8 +114,8 @@ export abstract class AppViewManager {
|
||||
* ng.bootstrap(MyApp);
|
||||
* ```
|
||||
*/
|
||||
abstract createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
||||
injector: Injector): HostViewRef;
|
||||
abstract createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string,
|
||||
injector: Injector, projectableNodes?: any[][]): HostViewRef;
|
||||
|
||||
/**
|
||||
* Destroys the Host View created via {@link AppViewManager#createRootHostView}.
|
||||
@ -140,7 +139,7 @@ export abstract class AppViewManager {
|
||||
// TODO(i): this low-level version of ViewContainerRef#createEmbeddedView doesn't add anything new
|
||||
// we should make it private, otherwise we have two apis to do the same thing.
|
||||
abstract createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
templateRef: TemplateRef): ViewRef;
|
||||
templateRef: TemplateRef): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* Instantiates a single {@link Component} and inserts its Host View into the View Container
|
||||
@ -150,16 +149,16 @@ export abstract class AppViewManager {
|
||||
* The component is instantiated using its {@link ProtoViewRef `protoViewRef`} which can be
|
||||
* obtained via {@link Compiler#compileInHost}.
|
||||
*
|
||||
* You can optionally specify `imperativelyCreatedInjector`, which configure the {@link Injector}
|
||||
* You can optionally specify `dynamicallyCreatedProviders`, which configure the {@link Injector}
|
||||
* that will be created for the Host View.
|
||||
*
|
||||
* Returns the {@link HostViewRef} of the Host View created for the newly instantiated Component.
|
||||
*
|
||||
* Use {@link AppViewManager#destroyViewInContainer} to destroy the created Host View.
|
||||
*/
|
||||
abstract createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
protoViewRef: ProtoViewRef,
|
||||
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef;
|
||||
abstract createHostViewInContainer(
|
||||
viewContainerLocation: ElementRef, index: number, hostViewFactoryRef: HostViewFactoryRef,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[], projectableNodes: any[][]): HostViewRef;
|
||||
|
||||
/**
|
||||
* Destroys an Embedded or Host View attached to a View Container at the specified `index`.
|
||||
@ -174,85 +173,75 @@ export abstract class AppViewManager {
|
||||
*/
|
||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||
abstract attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
viewRef: ViewRef): ViewRef;
|
||||
viewRef: EmbeddedViewRef): EmbeddedViewRef;
|
||||
|
||||
/**
|
||||
* See {@link AppViewManager#attachViewInContainer}.
|
||||
*/
|
||||
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef;
|
||||
abstract detachViewInContainer(viewContainerLocation: ElementRef, index: number): EmbeddedViewRef;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManager_ extends AppViewManager {
|
||||
private _protoViewFactory: ProtoViewFactory;
|
||||
private _nextCompTypeId: number = 0;
|
||||
|
||||
constructor(private _viewPool: AppViewPool, private _viewListener: AppViewListener,
|
||||
private _utils: AppViewManagerUtils, private _renderer: Renderer,
|
||||
@Inject(forwardRef(() => ProtoViewFactory)) _protoViewFactory) {
|
||||
constructor(private _renderer: RootRenderer, private _viewListener: AppViewListener,
|
||||
@Inject(APP_ID) private _appId: string) {
|
||||
super();
|
||||
this._protoViewFactory = _protoViewFactory;
|
||||
}
|
||||
|
||||
getViewContainer(location: ElementRef): ViewContainerRef {
|
||||
var hostView = internalView((<ElementRef_>location).parentView);
|
||||
return hostView.elementInjectors[(<ElementRef_>location).boundElementIndex]
|
||||
.getViewContainerRef();
|
||||
return (<ElementRef_>location).internalElement.getViewContainerRef();
|
||||
}
|
||||
|
||||
getHostElement(hostViewRef: ViewRef): ElementRef {
|
||||
var hostView = (<ViewRef_>hostViewRef).internalView;
|
||||
if (hostView.proto.type !== ViewType.HOST) {
|
||||
throw new BaseException('This operation is only allowed on host views');
|
||||
}
|
||||
return hostView.appElements[0].ref;
|
||||
}
|
||||
|
||||
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
|
||||
var hostView = internalView((<ElementRef_>hostLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
|
||||
var componentView = hostView.getNestedView(boundElementIndex);
|
||||
var appEl = (<ElementRef_>hostLocation).internalElement;
|
||||
var componentView = appEl.componentView;
|
||||
if (isBlank(componentView)) {
|
||||
throw new BaseException(`There is no component directive at element ${boundElementIndex}`);
|
||||
throw new BaseException(`There is no component directive at element ${hostLocation}`);
|
||||
}
|
||||
var binderIdx = componentView.proto.variableLocations.get(variableName);
|
||||
if (isBlank(binderIdx)) {
|
||||
throw new BaseException(`Could not find variable ${variableName}`);
|
||||
for (var i = 0; i < componentView.appElements.length; i++) {
|
||||
var compAppEl = componentView.appElements[i];
|
||||
if (StringMapWrapper.contains(compAppEl.proto.directiveVariableBindings, variableName)) {
|
||||
return compAppEl.ref;
|
||||
}
|
||||
}
|
||||
return componentView.elementRefs[componentView.elementOffset + binderIdx];
|
||||
throw new BaseException(`Could not find variable ${variableName}`);
|
||||
}
|
||||
|
||||
getComponent(hostLocation: ElementRef): any {
|
||||
var hostView = internalView((<ElementRef_>hostLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>hostLocation).boundElementIndex;
|
||||
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
||||
return (<ElementRef_>hostLocation).internalElement.getComponent();
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#createRootHostView()');
|
||||
|
||||
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
||||
injector: Injector): HostViewRef {
|
||||
createRootHostView(hostViewFactoryRef: HostViewFactoryRef, overrideSelector: string,
|
||||
injector: Injector, projectableNodes: any[][] = null): HostViewRef {
|
||||
var s = this._createRootHostViewScope();
|
||||
var hostProtoView: viewModule.AppProtoView = internalProtoView(hostProtoViewRef);
|
||||
this._protoViewFactory.initializeProtoViewIfNeeded(hostProtoView);
|
||||
var hostElementSelector = overrideSelector;
|
||||
if (isBlank(hostElementSelector)) {
|
||||
hostElementSelector = hostProtoView.elementBinders[0].componentDirective.metadata.selector;
|
||||
}
|
||||
var renderViewWithFragments = this._renderer.createRootHostView(
|
||||
hostProtoView.render, hostProtoView.mergeInfo.embeddedViewCount + 1, hostElementSelector);
|
||||
var hostView = this._createMainView(hostProtoView, renderViewWithFragments);
|
||||
|
||||
this._renderer.hydrateView(hostView.render);
|
||||
this._utils.hydrateRootHostView(hostView, injector);
|
||||
return wtfLeave(s, hostView.ref);
|
||||
var hostViewFactory = (<HostViewFactoryRef_>hostViewFactoryRef).internalHostViewFactory;
|
||||
var selector = isPresent(overrideSelector) ? overrideSelector : hostViewFactory.selector;
|
||||
var view = hostViewFactory.viewFactory(this._renderer, this, null, projectableNodes, selector,
|
||||
null, injector);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyRootHostViewScope: WtfScopeFn = wtfCreateScope('AppViewManager#destroyRootHostView()');
|
||||
|
||||
destroyRootHostView(hostViewRef: HostViewRef) {
|
||||
// Note: Don't put the hostView into the view pool
|
||||
// as it is depending on the element for which it was created.
|
||||
destroyRootHostView(hostViewRef: ViewRef) {
|
||||
var s = this._destroyRootHostViewScope();
|
||||
var hostView = internalView(<ViewRef>hostViewRef);
|
||||
this._renderer.detachFragment(hostView.renderFragment);
|
||||
this._renderer.dehydrateView(hostView.render);
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._viewListener.onViewDestroyed(hostView);
|
||||
this._renderer.destroyView(hostView.render);
|
||||
var hostView = (<ViewRef_>hostViewRef).internalView;
|
||||
hostView.renderer.detachView(flattenNestedViewRenderNodes(hostView.rootNodesOrAppElements));
|
||||
hostView.destroy();
|
||||
wtfLeave(s);
|
||||
}
|
||||
|
||||
@ -261,15 +250,14 @@ export class AppViewManager_ extends AppViewManager {
|
||||
wtfCreateScope('AppViewManager#createEmbeddedViewInContainer()');
|
||||
|
||||
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
templateRef: TemplateRef): ViewRef {
|
||||
templateRef: TemplateRef): EmbeddedViewRef {
|
||||
var s = this._createEmbeddedViewInContainerScope();
|
||||
var protoView = internalProtoView((<TemplateRef_>templateRef).protoViewRef);
|
||||
if (protoView.type !== viewModule.ViewType.EMBEDDED) {
|
||||
throw new BaseException('This method can only be called with embedded ProtoViews!');
|
||||
}
|
||||
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
|
||||
return wtfLeave(s, this._createViewInContainer(viewContainerLocation, index, protoView,
|
||||
templateRef.elementRef, null));
|
||||
var contextEl = (<TemplateRef_>templateRef).elementRef.internalElement;
|
||||
var view: AppView =
|
||||
contextEl.embeddedViewFactory(contextEl.parentView.renderer, this, contextEl,
|
||||
contextEl.parentView.projectableNodes, null, null, null);
|
||||
this._attachViewToContainer(view, (<ElementRef_>viewContainerLocation).internalElement, index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -277,70 +265,19 @@ export class AppViewManager_ extends AppViewManager {
|
||||
wtfCreateScope('AppViewManager#createHostViewInContainer()');
|
||||
|
||||
createHostViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
protoViewRef: ProtoViewRef,
|
||||
imperativelyCreatedInjector: ResolvedProvider[]): HostViewRef {
|
||||
hostViewFactoryRef: HostViewFactoryRef,
|
||||
dynamicallyCreatedProviders: ResolvedProvider[],
|
||||
projectableNodes: any[][]): HostViewRef {
|
||||
var s = this._createHostViewInContainerScope();
|
||||
var protoView = internalProtoView(protoViewRef);
|
||||
if (protoView.type !== viewModule.ViewType.HOST) {
|
||||
throw new BaseException('This method can only be called with host ProtoViews!');
|
||||
}
|
||||
this._protoViewFactory.initializeProtoViewIfNeeded(protoView);
|
||||
return wtfLeave(
|
||||
s, this._createViewInContainer(viewContainerLocation, index, protoView,
|
||||
viewContainerLocation, imperativelyCreatedInjector));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* See {@link AppViewManager#destroyViewInContainer}.
|
||||
* @internal
|
||||
*/
|
||||
_createViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
protoView: viewModule.AppProtoView, context: ElementRef,
|
||||
imperativelyCreatedInjector: ResolvedProvider[]): ViewRef {
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
var contextView = internalView((<ElementRef_>context).parentView);
|
||||
var contextBoundElementIndex = (<ElementRef_>context).boundElementIndex;
|
||||
var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
|
||||
var view;
|
||||
if (protoView.type === viewModule.ViewType.EMBEDDED && isPresent(embeddedFragmentView) &&
|
||||
!embeddedFragmentView.hydrated()) {
|
||||
// Case 1: instantiate the first view of a template that has been merged into a parent
|
||||
view = embeddedFragmentView;
|
||||
this._attachRenderView(parentView, boundElementIndex, index, view);
|
||||
} else {
|
||||
// Case 2: instantiate another copy of the template or a host ProtoView.
|
||||
// This is a separate case
|
||||
// as we only inline one copy of the template into the parent view.
|
||||
view = this._createPooledView(protoView);
|
||||
this._attachRenderView(parentView, boundElementIndex, index, view);
|
||||
this._renderer.hydrateView(view.render);
|
||||
}
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
|
||||
contextBoundElementIndex, index, view);
|
||||
|
||||
try {
|
||||
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
|
||||
contextBoundElementIndex, index,
|
||||
imperativelyCreatedInjector);
|
||||
} catch (e) {
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
|
||||
throw e;
|
||||
}
|
||||
return view.ref;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_attachRenderView(parentView: viewModule.AppView, boundElementIndex: number, index: number,
|
||||
view: viewModule.AppView) {
|
||||
var elementRef = parentView.elementRefs[boundElementIndex];
|
||||
if (index === 0) {
|
||||
this._renderer.attachFragmentAfterElement(elementRef, view.renderFragment);
|
||||
} else {
|
||||
var prevView = parentView.viewContainers[boundElementIndex].views[index - 1];
|
||||
this._renderer.attachFragmentAfterFragment(prevView.renderFragment, view.renderFragment);
|
||||
}
|
||||
// TODO(tbosch): This should be specifiable via an additional argument!
|
||||
var viewContainerLocation_ = <ElementRef_>viewContainerLocation;
|
||||
var contextEl = viewContainerLocation_.internalElement;
|
||||
var hostViewFactory = (<HostViewFactoryRef_>hostViewFactoryRef).internalHostViewFactory;
|
||||
var view = hostViewFactory.viewFactory(
|
||||
contextEl.parentView.renderer, contextEl.parentView.viewManager, contextEl,
|
||||
projectableNodes, null, dynamicallyCreatedProviders, null);
|
||||
this._attachViewToContainer(view, viewContainerLocation_.internalElement, index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
@ -348,9 +285,9 @@ export class AppViewManager_ extends AppViewManager {
|
||||
|
||||
destroyViewInContainer(viewContainerLocation: ElementRef, index: number) {
|
||||
var s = this._destroyViewInContainerScope();
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
this._destroyViewInContainer(parentView, boundElementIndex, index);
|
||||
var view =
|
||||
this._detachViewInContainer((<ElementRef_>viewContainerLocation).internalElement, index);
|
||||
view.destroy();
|
||||
wtfLeave(s);
|
||||
}
|
||||
|
||||
@ -359,108 +296,85 @@ export class AppViewManager_ extends AppViewManager {
|
||||
|
||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||
attachViewInContainer(viewContainerLocation: ElementRef, index: number,
|
||||
viewRef: ViewRef): ViewRef {
|
||||
viewRef: ViewRef): EmbeddedViewRef {
|
||||
var viewRef_ = <ViewRef_>viewRef;
|
||||
var s = this._attachViewInContainerScope();
|
||||
var view = internalView(viewRef);
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
// TODO(tbosch): the public methods attachViewInContainer/detachViewInContainer
|
||||
// are used for moving elements without the same container.
|
||||
// We will change this into an atomic `move` operation, which should preserve the
|
||||
// previous parent injector (see https://github.com/angular/angular/issues/1377).
|
||||
// Right now we are destroying any special
|
||||
// context view that might have been used.
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, null, null, index, view);
|
||||
this._attachRenderView(parentView, boundElementIndex, index, view);
|
||||
return wtfLeave(s, viewRef);
|
||||
this._attachViewToContainer(viewRef_.internalView,
|
||||
(<ElementRef_>viewContainerLocation).internalElement, index);
|
||||
return wtfLeave(s, viewRef_);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_detachViewInContainerScope = wtfCreateScope('AppViewMananger#detachViewInContainer()');
|
||||
|
||||
// TODO(i): refactor detachViewInContainer+attachViewInContainer to moveViewInContainer
|
||||
detachViewInContainer(viewContainerLocation: ElementRef, index: number): ViewRef {
|
||||
detachViewInContainer(viewContainerLocation: ElementRef, index: number): EmbeddedViewRef {
|
||||
var s = this._detachViewInContainerScope();
|
||||
var parentView = internalView((<ElementRef_>viewContainerLocation).parentView);
|
||||
var boundElementIndex = (<ElementRef_>viewContainerLocation).boundElementIndex;
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
|
||||
this._renderer.detachFragment(view.renderFragment);
|
||||
var view =
|
||||
this._detachViewInContainer((<ElementRef_>viewContainerLocation).internalElement, index);
|
||||
return wtfLeave(s, view.ref);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_createMainView(protoView: viewModule.AppProtoView,
|
||||
renderViewWithFragments: RenderViewWithFragments): viewModule.AppView {
|
||||
var mergedParentView =
|
||||
this._utils.createView(protoView, renderViewWithFragments, this, this._renderer);
|
||||
this._renderer.setEventDispatcher(mergedParentView.render, mergedParentView);
|
||||
this._viewListener.onViewCreated(mergedParentView);
|
||||
return mergedParentView;
|
||||
}
|
||||
onViewCreated(view: AppView) { this._viewListener.onViewCreated(view); }
|
||||
|
||||
/** @internal */
|
||||
_createPooledView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
||||
var view = this._viewPool.getView(protoView);
|
||||
if (isBlank(view)) {
|
||||
view = this._createMainView(
|
||||
protoView,
|
||||
this._renderer.createView(protoView.render, protoView.mergeInfo.embeddedViewCount + 1));
|
||||
onViewDestroyed(view: AppView) { this._viewListener.onViewDestroyed(view); }
|
||||
|
||||
/** @internal */
|
||||
createRenderComponentType(encapsulation: ViewEncapsulation,
|
||||
styles: Array<string | any[]>): RenderComponentType {
|
||||
return new RenderComponentType(`${this._appId}-${this._nextCompTypeId++}`, encapsulation,
|
||||
styles);
|
||||
}
|
||||
|
||||
private _attachViewToContainer(view: AppView, vcAppElement: AppElement, viewIndex: number) {
|
||||
if (view.proto.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
var nestedViews = vcAppElement.nestedViews;
|
||||
if (nestedViews == null) {
|
||||
nestedViews = [];
|
||||
vcAppElement.nestedViews = nestedViews;
|
||||
}
|
||||
ListWrapper.insert(nestedViews, viewIndex, view);
|
||||
var refNode;
|
||||
if (viewIndex > 0) {
|
||||
var prevView = nestedViews[viewIndex - 1];
|
||||
refNode = prevView.rootNodesOrAppElements.length > 0 ?
|
||||
prevView.rootNodesOrAppElements[prevView.rootNodesOrAppElements.length - 1] :
|
||||
null;
|
||||
} else {
|
||||
refNode = vcAppElement.nativeElement;
|
||||
}
|
||||
if (isPresent(refNode)) {
|
||||
var refRenderNode;
|
||||
if (refNode instanceof AppElement) {
|
||||
refRenderNode = (<AppElement>refNode).nativeElement;
|
||||
} else {
|
||||
refRenderNode = refNode;
|
||||
}
|
||||
view.renderer.attachViewAfter(refRenderNode,
|
||||
flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
|
||||
}
|
||||
// TODO: This is only needed when a view is destroyed,
|
||||
// not when it is detached for reordering with ng-for...
|
||||
vcAppElement.parentView.changeDetector.addContentChild(view.changeDetector);
|
||||
vcAppElement.traverseAndSetQueriesAsDirty();
|
||||
}
|
||||
|
||||
private _detachViewInContainer(vcAppElement: AppElement, viewIndex: number): AppView {
|
||||
var view = ListWrapper.removeAt(vcAppElement.nestedViews, viewIndex);
|
||||
if (view.proto.type === ViewType.COMPONENT) {
|
||||
throw new BaseException(`Component views can't be moved!`);
|
||||
}
|
||||
vcAppElement.traverseAndSetQueriesAsDirty();
|
||||
|
||||
view.renderer.detachView(flattenNestedViewRenderNodes(view.rootNodesOrAppElements));
|
||||
|
||||
// TODO: This is only needed when a view is destroyed,
|
||||
// not when it is detached for reordering with ng-for...
|
||||
view.changeDetector.remove();
|
||||
return view;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyPooledView(view: viewModule.AppView) {
|
||||
var wasReturned = this._viewPool.returnView(view);
|
||||
if (!wasReturned) {
|
||||
this._viewListener.onViewDestroyed(view);
|
||||
this._renderer.destroyView(view.render);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_destroyViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
index: number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
|
||||
this._viewDehydrateRecurse(view);
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, index);
|
||||
if (view.viewOffset > 0) {
|
||||
// Case 1: a view that is part of another view.
|
||||
// Just detach the fragment
|
||||
this._renderer.detachFragment(view.renderFragment);
|
||||
} else {
|
||||
// Case 2: a view that is not part of another view.
|
||||
// dehydrate and destroy it.
|
||||
this._renderer.dehydrateView(view.render);
|
||||
this._renderer.detachFragment(view.renderFragment);
|
||||
this._destroyPooledView(view);
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_viewDehydrateRecurse(view: viewModule.AppView) {
|
||||
if (view.hydrated()) {
|
||||
this._utils.dehydrateView(view);
|
||||
}
|
||||
var viewContainers = view.viewContainers;
|
||||
var startViewOffset = view.viewOffset;
|
||||
var endViewOffset = view.viewOffset + view.proto.mergeInfo.viewCount - 1;
|
||||
var elementOffset = view.elementOffset;
|
||||
for (var viewIdx = startViewOffset; viewIdx <= endViewOffset; viewIdx++) {
|
||||
var currView = view.views[viewIdx];
|
||||
for (var binderIdx = 0; binderIdx < currView.proto.elementBinders.length;
|
||||
binderIdx++, elementOffset++) {
|
||||
var vc = viewContainers[elementOffset];
|
||||
if (isPresent(vc)) {
|
||||
for (var j = vc.views.length - 1; j >= 0; j--) {
|
||||
this._destroyViewInContainer(currView, elementOffset, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,266 +0,0 @@
|
||||
import {Injector, Provider, Injectable, ResolvedProvider} from 'angular2/src/core/di';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import * as eli from './element_injector';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import * as viewModule from './view';
|
||||
import * as avmModule from './view_manager';
|
||||
import {ElementRef, ElementRef_} from './element_ref';
|
||||
import {TemplateRef, TemplateRef_} from './template_ref';
|
||||
import {Renderer, RenderViewWithFragments} from 'angular2/src/core/render/api';
|
||||
import {Locals} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {Pipes} from 'angular2/src/core/pipes/pipes';
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManagerUtils {
|
||||
constructor() {}
|
||||
|
||||
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
|
||||
var eli = parentView.elementInjectors[boundElementIndex];
|
||||
return eli.getComponent();
|
||||
}
|
||||
|
||||
createView(mergedParentViewProto: viewModule.AppProtoView,
|
||||
renderViewWithFragments: RenderViewWithFragments,
|
||||
viewManager: avmModule.AppViewManager, renderer: Renderer): viewModule.AppView {
|
||||
var renderFragments = renderViewWithFragments.fragmentRefs;
|
||||
var renderView = renderViewWithFragments.viewRef;
|
||||
|
||||
var elementCount = mergedParentViewProto.mergeInfo.elementCount;
|
||||
var viewCount = mergedParentViewProto.mergeInfo.viewCount;
|
||||
var elementRefs: ElementRef[] = ListWrapper.createFixedSize(elementCount);
|
||||
var viewContainers = ListWrapper.createFixedSize(elementCount);
|
||||
var preBuiltObjects: eli.PreBuiltObjects[] = ListWrapper.createFixedSize(elementCount);
|
||||
var elementInjectors: eli.ElementInjector[] = ListWrapper.createFixedSize(elementCount);
|
||||
var views = ListWrapper.createFixedSize(viewCount);
|
||||
|
||||
var elementOffset = 0;
|
||||
var textOffset = 0;
|
||||
var fragmentIdx = 0;
|
||||
var containerElementIndicesByViewIndex: number[] = ListWrapper.createFixedSize(viewCount);
|
||||
for (var viewOffset = 0; viewOffset < viewCount; viewOffset++) {
|
||||
var containerElementIndex = containerElementIndicesByViewIndex[viewOffset];
|
||||
var containerElementInjector =
|
||||
isPresent(containerElementIndex) ? elementInjectors[containerElementIndex] : null;
|
||||
var parentView =
|
||||
isPresent(containerElementInjector) ? preBuiltObjects[containerElementIndex].view : null;
|
||||
var protoView =
|
||||
isPresent(containerElementIndex) ?
|
||||
parentView.proto.elementBinders[containerElementIndex - parentView.elementOffset]
|
||||
.nestedProtoView :
|
||||
mergedParentViewProto;
|
||||
var renderFragment = null;
|
||||
if (viewOffset === 0 || protoView.type === viewModule.ViewType.EMBEDDED) {
|
||||
renderFragment = renderFragments[fragmentIdx++];
|
||||
}
|
||||
var currentView = new viewModule.AppView(renderer, protoView, viewOffset, elementOffset,
|
||||
textOffset, protoView.protoLocals, renderView,
|
||||
renderFragment, containerElementInjector);
|
||||
views[viewOffset] = currentView;
|
||||
if (isPresent(containerElementIndex)) {
|
||||
preBuiltObjects[containerElementIndex].nestedView = currentView;
|
||||
}
|
||||
var rootElementInjectors = [];
|
||||
var nestedViewOffset = viewOffset + 1;
|
||||
for (var binderIdx = 0; binderIdx < protoView.elementBinders.length; binderIdx++) {
|
||||
var binder = protoView.elementBinders[binderIdx];
|
||||
var boundElementIndex = elementOffset + binderIdx;
|
||||
var elementInjector = null;
|
||||
|
||||
if (isPresent(binder.nestedProtoView) && binder.nestedProtoView.isMergable) {
|
||||
containerElementIndicesByViewIndex[nestedViewOffset] = boundElementIndex;
|
||||
nestedViewOffset += binder.nestedProtoView.mergeInfo.viewCount;
|
||||
}
|
||||
|
||||
// elementInjectors and rootElementInjectors
|
||||
var protoElementInjector = binder.protoElementInjector;
|
||||
if (isPresent(protoElementInjector)) {
|
||||
if (isPresent(protoElementInjector.parent)) {
|
||||
var parentElementInjector =
|
||||
elementInjectors[elementOffset + protoElementInjector.parent.index];
|
||||
elementInjector = protoElementInjector.instantiate(parentElementInjector);
|
||||
} else {
|
||||
elementInjector = protoElementInjector.instantiate(null);
|
||||
rootElementInjectors.push(elementInjector);
|
||||
}
|
||||
}
|
||||
elementInjectors[boundElementIndex] = elementInjector;
|
||||
|
||||
// elementRefs
|
||||
var el = new ElementRef_(currentView.ref, boundElementIndex, renderer);
|
||||
elementRefs[el.boundElementIndex] = el;
|
||||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
var templateRef = isPresent(binder.nestedProtoView) &&
|
||||
binder.nestedProtoView.type === viewModule.ViewType.EMBEDDED ?
|
||||
new TemplateRef_(el) :
|
||||
null;
|
||||
preBuiltObjects[boundElementIndex] =
|
||||
new eli.PreBuiltObjects(viewManager, currentView, el, templateRef);
|
||||
}
|
||||
}
|
||||
currentView.init(protoView.changeDetectorFactory(currentView), elementInjectors,
|
||||
rootElementInjectors, preBuiltObjects, views, elementRefs, viewContainers);
|
||||
if (isPresent(parentView) && protoView.type === viewModule.ViewType.COMPONENT) {
|
||||
parentView.changeDetector.addViewChild(currentView.changeDetector);
|
||||
}
|
||||
elementOffset += protoView.elementBinders.length;
|
||||
textOffset += protoView.textBindingCount;
|
||||
}
|
||||
return views[0];
|
||||
}
|
||||
|
||||
hydrateRootHostView(hostView: viewModule.AppView, injector: Injector) {
|
||||
this._hydrateView(hostView, injector, null, new Object(), null);
|
||||
}
|
||||
|
||||
// Misnomer: this method is attaching next to the view container.
|
||||
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||
index: number, view: viewModule.AppView) {
|
||||
if (isBlank(contextView)) {
|
||||
contextView = parentView;
|
||||
contextBoundElementIndex = boundElementIndex;
|
||||
}
|
||||
parentView.changeDetector.addContentChild(view.changeDetector);
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
if (isBlank(viewContainer)) {
|
||||
viewContainer = new viewModule.AppViewContainer();
|
||||
parentView.viewContainers[boundElementIndex] = viewContainer;
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, index, view);
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||
|
||||
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
|
||||
if (isPresent(elementInjector.parent)) {
|
||||
view.rootElementInjectors[i].link(elementInjector.parent);
|
||||
}
|
||||
}
|
||||
elementInjector.traverseAndSetQueriesAsDirty();
|
||||
}
|
||||
|
||||
detachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number, index: number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
|
||||
parentView.elementInjectors[boundElementIndex].traverseAndSetQueriesAsDirty();
|
||||
|
||||
view.changeDetector.remove();
|
||||
ListWrapper.removeAt(viewContainer.views, index);
|
||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||
var inj = view.rootElementInjectors[i];
|
||||
inj.unlink();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||
index: number, imperativelyCreatedProviders: ResolvedProvider[]) {
|
||||
if (isBlank(contextView)) {
|
||||
contextView = parentView;
|
||||
contextBoundElementIndex = boundElementIndex;
|
||||
}
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[index];
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||
|
||||
var injector = isPresent(imperativelyCreatedProviders) ?
|
||||
Injector.fromResolvedProviders(imperativelyCreatedProviders) :
|
||||
null;
|
||||
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
|
||||
contextView.locals);
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_hydrateView(initView: viewModule.AppView, imperativelyCreatedInjector: Injector,
|
||||
hostElementInjector: eli.ElementInjector, context: Object, parentLocals: Locals) {
|
||||
var viewIdx = initView.viewOffset;
|
||||
var endViewOffset = viewIdx + initView.proto.mergeInfo.viewCount - 1;
|
||||
while (viewIdx <= endViewOffset) {
|
||||
var currView = initView.views[viewIdx];
|
||||
var currProtoView = currView.proto;
|
||||
if (currView !== initView && currView.proto.type === viewModule.ViewType.EMBEDDED) {
|
||||
// Don't hydrate components of embedded fragment views.
|
||||
viewIdx += currView.proto.mergeInfo.viewCount;
|
||||
} else {
|
||||
if (currView !== initView) {
|
||||
// hydrate a nested component view
|
||||
imperativelyCreatedInjector = null;
|
||||
parentLocals = null;
|
||||
hostElementInjector = currView.containerElementInjector;
|
||||
context = hostElementInjector.getComponent();
|
||||
}
|
||||
currView.context = context;
|
||||
currView.locals.parent = parentLocals;
|
||||
var binders = currProtoView.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var boundElementIndex = binderIdx + currView.elementOffset;
|
||||
var elementInjector = initView.elementInjectors[boundElementIndex];
|
||||
|
||||
if (isPresent(elementInjector)) {
|
||||
elementInjector.hydrate(imperativelyCreatedInjector, hostElementInjector,
|
||||
currView.preBuiltObjects[boundElementIndex]);
|
||||
this._populateViewLocals(currView, elementInjector, boundElementIndex);
|
||||
this._setUpEventEmitters(currView, elementInjector, boundElementIndex);
|
||||
}
|
||||
}
|
||||
var pipes = isPresent(hostElementInjector) ?
|
||||
new Pipes(currView.proto.pipes, hostElementInjector.getInjector()) :
|
||||
null;
|
||||
currView.changeDetector.hydrate(currView.context, currView.locals, currView, pipes);
|
||||
viewIdx++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_populateViewLocals(view: viewModule.AppView, elementInjector: eli.ElementInjector,
|
||||
boundElementIdx: number): void {
|
||||
if (isPresent(elementInjector.getDirectiveVariableBindings())) {
|
||||
elementInjector.getDirectiveVariableBindings().forEach((directiveIndex, name) => {
|
||||
if (isBlank(directiveIndex)) {
|
||||
view.locals.set(name, view.elementRefs[boundElementIdx].nativeElement);
|
||||
} else {
|
||||
view.locals.set(name, elementInjector.getDirectiveAtIndex(directiveIndex));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
_setUpEventEmitters(view: viewModule.AppView, elementInjector: eli.ElementInjector,
|
||||
boundElementIndex: number) {
|
||||
var emitters = elementInjector.getEventEmitterAccessors();
|
||||
for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
||||
var directiveEmitters = emitters[directiveIndex];
|
||||
var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
|
||||
for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
|
||||
var eventEmitterAccessor = directiveEmitters[eventIndex];
|
||||
eventEmitterAccessor.subscribe(view, boundElementIndex, directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dehydrateView(initView: viewModule.AppView) {
|
||||
var endViewOffset = initView.viewOffset + initView.proto.mergeInfo.viewCount - 1;
|
||||
for (var viewIdx = initView.viewOffset; viewIdx <= endViewOffset; viewIdx++) {
|
||||
var currView = initView.views[viewIdx];
|
||||
if (currView.hydrated()) {
|
||||
if (isPresent(currView.locals)) {
|
||||
currView.locals.clearValues();
|
||||
}
|
||||
currView.context = null;
|
||||
currView.changeDetector.dehydrate();
|
||||
var binders = currView.proto.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var eli = initView.elementInjectors[currView.elementOffset + binderIdx];
|
||||
if (isPresent(eli)) {
|
||||
eli.dehydrate();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,42 +0,0 @@
|
||||
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
|
||||
|
||||
import {isPresent, isBlank, CONST_EXPR} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, Map} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as viewModule from './view';
|
||||
|
||||
export const APP_VIEW_POOL_CAPACITY = CONST_EXPR(new OpaqueToken('AppViewPool.viewPoolCapacity'));
|
||||
|
||||
@Injectable()
|
||||
export class AppViewPool {
|
||||
/** @internal */
|
||||
_poolCapacityPerProtoView: number;
|
||||
/** @internal */
|
||||
_pooledViewsPerProtoView = new Map<viewModule.AppProtoView, Array<viewModule.AppView>>();
|
||||
|
||||
constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
|
||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||
}
|
||||
|
||||
getView(protoView: viewModule.AppProtoView): viewModule.AppView {
|
||||
var pooledViews = this._pooledViewsPerProtoView.get(protoView);
|
||||
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
||||
return pooledViews.pop();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
returnView(view: viewModule.AppView): boolean {
|
||||
var protoView = view.proto;
|
||||
var pooledViews = this._pooledViewsPerProtoView.get(protoView);
|
||||
if (isBlank(pooledViews)) {
|
||||
pooledViews = [];
|
||||
this._pooledViewsPerProtoView.set(protoView, pooledViews);
|
||||
}
|
||||
var haveRemainingCapacity = pooledViews.length < this._poolCapacityPerProtoView;
|
||||
if (haveRemainingCapacity) {
|
||||
pooledViews.push(view);
|
||||
}
|
||||
return haveRemainingCapacity;
|
||||
}
|
||||
}
|
@ -1,20 +1,16 @@
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
import {unimplemented} from 'angular2/src/facade/exceptions';
|
||||
import * as viewModule from './view';
|
||||
import {ChangeDetectorRef} from '../change_detection/change_detector_ref';
|
||||
import {RenderViewRef, RenderFragmentRef} from 'angular2/src/core/render/api';
|
||||
import {AppView, HostViewFactory} from './view';
|
||||
|
||||
// This is a workaround for privacy in Dart as we don't have library parts
|
||||
export function internalView(viewRef: ViewRef): viewModule.AppView {
|
||||
return (<ViewRef_>viewRef)._view;
|
||||
export abstract class ViewRef {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); };
|
||||
|
||||
get destroyed(): boolean { return unimplemented(); }
|
||||
}
|
||||
|
||||
// This is a workaround for privacy in Dart as we don't have library parts
|
||||
export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppProtoView {
|
||||
return isPresent(protoViewRef) ? (<ProtoViewRef_>protoViewRef)._protoView : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Represents a View containing a single Element that is the Host Element of a {@link Component}
|
||||
* instance.
|
||||
@ -24,11 +20,8 @@ export function internalProtoView(protoViewRef: ProtoViewRef): viewModule.AppPro
|
||||
* of the higher-level APIs: {@link AppViewManager#createRootHostView},
|
||||
* {@link AppViewManager#createHostViewInContainer}, {@link ViewContainerRef#createHostView}.
|
||||
*/
|
||||
export interface HostViewRef {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
changeDetectorRef: ChangeDetectorRef;
|
||||
export abstract class HostViewRef extends ViewRef {
|
||||
get rootNodes(): any[] { return unimplemented(); };
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,96 +77,43 @@ export interface HostViewRef {
|
||||
* <!-- /ViewRef: outer-0 -->
|
||||
* ```
|
||||
*/
|
||||
export abstract class ViewRef implements HostViewRef {
|
||||
export abstract class EmbeddedViewRef extends ViewRef {
|
||||
/**
|
||||
* Sets `value` of local variable called `variableName` in this View.
|
||||
*/
|
||||
abstract setLocal(variableName: string, value: any): void;
|
||||
|
||||
get changeDetectorRef(): ChangeDetectorRef { return unimplemented(); }
|
||||
set changeDetectorRef(value: ChangeDetectorRef) {
|
||||
unimplemented(); // TODO: https://github.com/Microsoft/TypeScript/issues/12
|
||||
}
|
||||
/**
|
||||
* Checks whether this view has a local variable called `variableName`.
|
||||
*/
|
||||
abstract hasLocal(variableName: string): boolean;
|
||||
|
||||
get rootNodes(): any[] { return unimplemented(); };
|
||||
}
|
||||
|
||||
export class ViewRef_ extends ViewRef {
|
||||
private _changeDetectorRef: ChangeDetectorRef = null;
|
||||
/** @internal */
|
||||
public _view: viewModule.AppView;
|
||||
constructor(_view: viewModule.AppView) {
|
||||
super();
|
||||
this._view = _view;
|
||||
}
|
||||
export class ViewRef_ implements EmbeddedViewRef, HostViewRef {
|
||||
constructor(private _view: AppView) { this._view = _view; }
|
||||
|
||||
/**
|
||||
* Return `RenderViewRef`
|
||||
*/
|
||||
get render(): RenderViewRef { return this._view.render; }
|
||||
|
||||
/**
|
||||
* Return `RenderFragmentRef`
|
||||
*/
|
||||
get renderFragment(): RenderFragmentRef { return this._view.renderFragment; }
|
||||
get internalView(): AppView { return this._view; }
|
||||
|
||||
/**
|
||||
* Return `ChangeDetectorRef`
|
||||
*/
|
||||
get changeDetectorRef(): ChangeDetectorRef {
|
||||
if (this._changeDetectorRef === null) {
|
||||
this._changeDetectorRef = this._view.changeDetector.ref;
|
||||
}
|
||||
return this._changeDetectorRef;
|
||||
}
|
||||
get changeDetectorRef(): ChangeDetectorRef { return this._view.changeDetector.ref; }
|
||||
|
||||
get rootNodes(): any[] { return this._view.flatRootNodes; }
|
||||
|
||||
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
|
||||
|
||||
hasLocal(variableName: string): boolean { return this._view.hasLocal(variableName); }
|
||||
|
||||
get destroyed(): boolean { return this._view.destroyed; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents an Angular ProtoView.
|
||||
*
|
||||
* A ProtoView is a prototypical {@link ViewRef View} that is the result of Template compilation and
|
||||
* is used by Angular to efficiently create an instance of this View based on the compiled Template.
|
||||
*
|
||||
* Most ProtoViews are created and used internally by Angular and you don't need to know about them,
|
||||
* except in advanced use-cases where you compile components yourself via the low-level
|
||||
* {@link Compiler#compileInHost} API.
|
||||
*
|
||||
*
|
||||
* ### Example
|
||||
*
|
||||
* Given this template:
|
||||
*
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <li *ngFor="var item of items">{{item}}</li>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Angular desugars and compiles the template into two ProtoViews:
|
||||
*
|
||||
* Outer ProtoView:
|
||||
* ```
|
||||
* Count: {{items.length}}
|
||||
* <ul>
|
||||
* <template ngFor var-item [ngForOf]="items"></template>
|
||||
* </ul>
|
||||
* ```
|
||||
*
|
||||
* Inner ProtoView:
|
||||
* ```
|
||||
* <li>{{item}}</li>
|
||||
* ```
|
||||
*
|
||||
* Notice that the original template is broken down into two separate ProtoViews.
|
||||
*/
|
||||
export abstract class ProtoViewRef {}
|
||||
export abstract class HostViewFactoryRef {}
|
||||
|
||||
export class ProtoViewRef_ extends ProtoViewRef {
|
||||
/** @internal */
|
||||
public _protoView: viewModule.AppProtoView;
|
||||
constructor(_protoView: viewModule.AppProtoView) {
|
||||
super();
|
||||
this._protoView = _protoView;
|
||||
}
|
||||
}
|
||||
export class HostViewFactoryRef_ implements HostViewFactoryRef {
|
||||
constructor(private _hostViewFactory: HostViewFactory) {}
|
||||
|
||||
get internalHostViewFactory(): HostViewFactory { return this._hostViewFactory; }
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user