Compare commits
89 Commits
2.0.0-rc.7
...
2.0.1
Author | SHA1 | Date | |
---|---|---|---|
712d1a7c37 | |||
16601f9359 | |||
b81e2e7a31 | |||
98fac36706 | |||
3e780c032e | |||
e09882180e | |||
0e18c57a17 | |||
51e2b9c073 | |||
f218e240d3 | |||
af6b219f8e | |||
20addf5f9f | |||
2860418a3c | |||
39e251eea7 | |||
d7d716d5db | |||
a95d65241c | |||
fdb22bd185 | |||
51d73d3e4e | |||
bf81b06a28 | |||
0621f07a2c | |||
1225ecfb14 | |||
5509453e72 | |||
70488ed382 | |||
03aedbe54b | |||
8395aab25d | |||
0dc15eb64a | |||
cba885a1fb | |||
fa4723a208 | |||
5bf08b886f | |||
89802316b9 | |||
2300c23332 | |||
fa39965a37 | |||
115f0fa842 | |||
734b8b8c13 | |||
54b41f57be | |||
df4254ae89 | |||
14ee75924b | |||
bd4045b6e7 | |||
255099aa61 | |||
1c24096650 | |||
32aeb1052d | |||
838d4bbf6c | |||
c4114c2f66 | |||
37b8691c8c | |||
93054d4e3d | |||
cfc12c6539 | |||
c0bdd89b5d | |||
d5515473bf | |||
ffe5c49c3e | |||
ae1dd5bfd0 | |||
cb657c4b55 | |||
42f60ca303 | |||
e33037a2f1 | |||
9cee8bcc83 | |||
003294d5df | |||
785292f44f | |||
15c2912527 | |||
096ae7c404 | |||
5972fdc817 | |||
2c42a50fc3 | |||
caa1cd2470 | |||
5fad37df69 | |||
727c2b38a4 | |||
b6287ccc51 | |||
69e8ace884 | |||
85d9db6bc4 | |||
0a2132ef10 | |||
d299ce4bcf | |||
0b9425bbb4 | |||
1a035a0dc7 | |||
84b4338ab5 | |||
b847257b16 | |||
c65d139081 | |||
57f0269491 | |||
4e6c41b3a1 | |||
7105021c41 | |||
f7313db0be | |||
1d2e70e3a4 | |||
21516c32e6 | |||
00a24b63da | |||
e71558ba89 | |||
7ac47acc1c | |||
60e49a7e4b | |||
c71e35cbf5 | |||
1348c65b0c | |||
ff03d87cdd | |||
a2bf334e6e | |||
f8690caa98 | |||
aa713d1dd9 | |||
a2519c6164 |
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
@ -20,7 +20,7 @@
|
|||||||
**Please tell us about your environment:**
|
**Please tell us about your environment:**
|
||||||
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
<!-- Operating system, IDE, package manager, HTTP server, ... -->
|
||||||
|
|
||||||
* **Angular version:** 2.0.0-rc.X
|
* **Angular version:** 2.0.X
|
||||||
<!-- Check whether this is still an issue in the most recent Angular version -->
|
<!-- Check whether this is still an issue in the most recent Angular version -->
|
||||||
|
|
||||||
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
* **Browser:** [all | Chrome XX | Firefox XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView ]
|
||||||
|
148
.travis.yml
148
.travis.yml
@ -1,7 +1,7 @@
|
|||||||
language: node_js
|
language: node_js
|
||||||
sudo: false
|
sudo: false
|
||||||
node_js:
|
node_js:
|
||||||
- '5.4.1'
|
- '6.6.0'
|
||||||
|
|
||||||
addons:
|
addons:
|
||||||
# firefox: "38.0"
|
# firefox: "38.0"
|
||||||
@ -20,20 +20,9 @@ cache:
|
|||||||
directories:
|
directories:
|
||||||
- ./node_modules
|
- ./node_modules
|
||||||
- ./.chrome/chromium
|
- ./.chrome/chromium
|
||||||
# - $HOME/.pub-cache
|
|
||||||
|
|
||||||
|
|
||||||
#before_cache:
|
|
||||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
|
||||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
|
||||||
# - E2E_BROWSERS=ChromeOnTravis
|
|
||||||
# - LOGS_DIR=/tmp/angular-build/logs
|
|
||||||
# - ARCH=linux-x64
|
|
||||||
|
|
||||||
# GITHUB_TOKEN_ANGULAR
|
# GITHUB_TOKEN_ANGULAR
|
||||||
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
|
||||||
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
||||||
@ -52,146 +41,11 @@ matrix:
|
|||||||
- env: "CI_MODE=saucelabs_optional"
|
- env: "CI_MODE=saucelabs_optional"
|
||||||
- env: "CI_MODE=browserstack_optional"
|
- env: "CI_MODE=browserstack_optional"
|
||||||
|
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- ./scripts/ci-lite/install.sh
|
- ./scripts/ci-lite/install.sh
|
||||||
|
|
||||||
before_script:
|
|
||||||
|
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
|
||||||
|
|
||||||
after_script:
|
after_script:
|
||||||
- ./scripts/ci-lite/cleanup.sh
|
- ./scripts/ci-lite/cleanup.sh
|
||||||
|
|
||||||
|
|
||||||
#branches:
|
|
||||||
# except:
|
|
||||||
# - g3_v2_0
|
|
||||||
#
|
|
||||||
#cache:
|
|
||||||
# directories:
|
|
||||||
# - $HOME/.pub-cache
|
|
||||||
# - $HOME/.chrome/chromium
|
|
||||||
#
|
|
||||||
#before_cache:
|
|
||||||
# # Undo the pollution of the typescript_next build before the cache is primed for future use
|
|
||||||
# - if [[ "$MODE" == "typescript_next" ]]; then npm install typescript; fi
|
|
||||||
#
|
|
||||||
#env:
|
|
||||||
# global:
|
|
||||||
# # Use newer verison of GCC to that is required to compile native npm modules for Node v4+ on Ubuntu Precise
|
|
||||||
# # more info: https://docs.travis-ci.com/user/languages/javascript-with-nodejs#Node.js-v4-(or-io.js-v3)-compiler-requirements
|
|
||||||
# - CXX=g++-4.8
|
|
||||||
# - KARMA_DART_BROWSERS=DartiumWithWebPlatform
|
|
||||||
# # No sandbox mode is needed for Chromium in Travis, it crashes otherwise: https://sites.google.com/a/chromium.org/chromedriver/help/chrome-doesn-t-start
|
|
||||||
# - KARMA_JS_BROWSERS=ChromeNoSandbox
|
|
||||||
# - E2E_BROWSERS=ChromeOnTravis
|
|
||||||
# - LOGS_DIR=/tmp/angular-build/logs
|
|
||||||
# - SAUCE_USERNAME=angular-ci
|
|
||||||
# - SAUCE_ACCESS_KEY=9b988f434ff8-fbca-8aa4-4ae3-35442987
|
|
||||||
# - BROWSER_STACK_USERNAME=angularteam1
|
|
||||||
# - BROWSER_STACK_ACCESS_KEY=BWCd4SynLzdDcv8xtzsB
|
|
||||||
# - ARCH=linux-x64
|
|
||||||
# - DART_DEV_VERSION=latest
|
|
||||||
# - DART_STABLE_VERSION=latest
|
|
||||||
# - DART_CHANNEL=stable
|
|
||||||
# - DART_VERSION=$DART_STABLE_VERSION
|
|
||||||
# # Token for tsd to increase github rate limit
|
|
||||||
# # See https://github.com/DefinitelyTyped/tsd#tsdrc
|
|
||||||
# # This does not use http://docs.travis-ci.com/user/environment-variables/#Secure-Variables
|
|
||||||
# # because those are not visible for pull requests, and those should also be reliable.
|
|
||||||
# # This SSO token belongs to github account angular-github-ratelimit-token which has no access
|
|
||||||
# # (password is in Valentine)
|
|
||||||
# - TSDRC='{"token":"ef474500309daea53d5991b3079159a29520a40b"}'
|
|
||||||
# # GITHUB_TOKEN_ANGULAR
|
|
||||||
# - secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
|
|
||||||
# matrix:
|
|
||||||
# # Order: a slower build first, so that we don't occupy an idle travis worker waiting for others to complete.
|
|
||||||
# - MODE=dart
|
|
||||||
# - MODE=dart DART_CHANNEL=dev
|
|
||||||
# - MODE=saucelabs_required
|
|
||||||
# - MODE=browserstack_required
|
|
||||||
# - MODE=saucelabs_optional
|
|
||||||
# - MODE=browserstack_optional
|
|
||||||
# - MODE=dart_ddc
|
|
||||||
# - MODE=js
|
|
||||||
# - MODE=router
|
|
||||||
# - MODE=build_only
|
|
||||||
# - MODE=typescript_next
|
|
||||||
# - MODE=lint
|
|
||||||
#
|
|
||||||
#matrix:
|
|
||||||
# allow_failures:
|
|
||||||
# - env: "MODE=saucelabs_optional"
|
|
||||||
# - env: "MODE=browserstack_optional"
|
|
||||||
#
|
|
||||||
#addons:
|
|
||||||
# firefox: "38.0"
|
|
||||||
# apt:
|
|
||||||
# sources:
|
|
||||||
# - ubuntu-toolchain-r-test
|
|
||||||
# packages:
|
|
||||||
# - g++-4.8
|
|
||||||
#
|
|
||||||
#before_install:
|
|
||||||
# - node tools/analytics/build-analytics start ci job
|
|
||||||
# - node tools/analytics/build-analytics start ci before_install
|
|
||||||
# - echo ${TSDRC} > .tsdrc
|
|
||||||
# - export CHROME_BIN=$HOME/.chrome/chromium/chrome-linux/chrome
|
|
||||||
# - export DISPLAY=:99.0
|
|
||||||
# - export GIT_SHA=$(git rev-parse HEAD)
|
|
||||||
# - ./scripts/ci/init_android.sh
|
|
||||||
# - sh -e /etc/init.d/xvfb start
|
|
||||||
# # Use a separate SauseLabs account for upstream/master builds in order for Sauce to create a badge representing the status of just upstream/master
|
|
||||||
# - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && [ "${TRAVIS_BRANCH}" = "master" ] && SAUCE_USERNAME="angular2-ci" && SAUCE_ACCESS_KEY="693ebc16208a-0b5b-1614-8d66-a2662f4e" || true'
|
|
||||||
# - node tools/analytics/build-analytics success ci before_install
|
|
||||||
#
|
|
||||||
#install:
|
|
||||||
# - node tools/analytics/build-analytics start ci install
|
|
||||||
# # Install version of npm that we are locked against
|
|
||||||
# - npm install -g npm@3.5.3
|
|
||||||
# # Install version of Chromium that we are locked against
|
|
||||||
# - ./scripts/ci/install_chromium.sh
|
|
||||||
# # Install version of Dart based on the matrix build variables
|
|
||||||
# - ./scripts/ci/install_dart.sh ${DART_CHANNEL} ${DART_VERSION} ${ARCH}
|
|
||||||
# # Print the size of caches to ease debugging
|
|
||||||
# - du -sh ./node_modules || true
|
|
||||||
# # Install npm dependecies
|
|
||||||
# # 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:
|
|
||||||
# - node tools/analytics/build-analytics start ci before_script
|
|
||||||
# - mkdir -p $LOGS_DIR
|
|
||||||
# - ./scripts/ci/presubmit-queue-setup.sh
|
|
||||||
# - node tools/analytics/build-analytics success ci before_script
|
|
||||||
#
|
|
||||||
#script:
|
|
||||||
# - node tools/analytics/build-analytics start ci script
|
|
||||||
# - ./scripts/ci/build_and_test.sh ${MODE}
|
|
||||||
# - node tools/analytics/build-analytics success ci script
|
|
||||||
#
|
|
||||||
#after_script:
|
|
||||||
# - node tools/analytics/build-analytics start ci after_script
|
|
||||||
# - ./scripts/ci/print-logs.sh
|
|
||||||
# - ./scripts/ci/after-script.sh
|
|
||||||
# - ./scripts/publish/publish-build-artifacts.sh
|
|
||||||
# - node tools/analytics/build-analytics success ci after_script
|
|
||||||
# - tools/analytics/build-analytics $TRAVIS_TEST_RESULT ci job
|
|
||||||
#
|
|
||||||
#notifications:
|
|
||||||
# webhooks:
|
|
||||||
# urls:
|
|
||||||
# - https://webhooks.gitter.im/e/1ef62e23078036f9cee4
|
|
||||||
# # trigger Buildtime Trend Service to parse Travis CI log
|
|
||||||
# - https://buildtimetrend.herokuapp.com/travis
|
|
||||||
# - http://104.197.9.155:8484/hubot/travis/activity
|
|
||||||
# on_success: always # options: [always|never|change] default: always
|
|
||||||
# on_failure: always # options: [always|never|change] default: always
|
|
||||||
# on_start: never # default: never
|
|
||||||
# slack:
|
|
||||||
# secure: EP4MzZ8JMyNQJ4S3cd5LEPWSMjC7ZRdzt3veelDiOeorJ6GwZfCDHncR+4BahDzQAuqyE/yNpZqaLbwRWloDi15qIUsm09vgl/1IyNky1Sqc6lEknhzIXpWSalo4/T9ZP8w870EoDvM/UO+LCV99R3wS8Nm9o99eLoWVb2HIUu0=
|
|
||||||
|
30
CHANGELOG.md
30
CHANGELOG.md
@ -1,3 +1,33 @@
|
|||||||
|
<a name="2.0.1"></a>
|
||||||
|
## [2.0.1](https://github.com/angular/angular/compare/2.0.0...2.0.1) (2016-09-23)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **common:** fix ngOnChanges signature of NgTemplateOutlet directive ([14ee759](https://github.com/angular/angular/commit/14ee759))
|
||||||
|
* **compiler:** `[attribute~=value]` selector ([#11696](https://github.com/angular/angular/issues/11696)) ([734b8b8](https://github.com/angular/angular/commit/734b8b8)), closes [#9644](https://github.com/angular/angular/issues/9644)
|
||||||
|
* **compiler:** safe property access expressions work in event bindings ([#11724](https://github.com/angular/angular/issues/11724)) ([a95d652](https://github.com/angular/angular/commit/a95d652))
|
||||||
|
* **compiler:** throw when Component.moduleId is not a string ([bd4045b](https://github.com/angular/angular/commit/bd4045b)), closes [#11590](https://github.com/angular/angular/issues/11590)
|
||||||
|
* **compiler:** do not provide I18N values when they're not specified ([03aedbe](https://github.com/angular/angular/commit/03aedbe)), closes [#11643](https://github.com/angular/angular/issues/11643)
|
||||||
|
* **core:** ContentChild descendants should be queried by default ([0dc15eb](https://github.com/angular/angular/commit/0dc15eb)), closes [#1645](https://github.com/angular/angular/issues/1645)
|
||||||
|
* **forms:** disable all radios with disable() ([2860418](https://github.com/angular/angular/commit/2860418))
|
||||||
|
* **forms:** make setDisabledState optional for reactive form directives ([#11731](https://github.com/angular/angular/issues/11731)) ([51d73d3](https://github.com/angular/angular/commit/51d73d3)), closes [#11719](https://github.com/angular/angular/issues/11719)
|
||||||
|
* **forms:** support unbound disabled in ngModel ([#11736](https://github.com/angular/angular/issues/11736)) ([39e251e](https://github.com/angular/angular/commit/39e251e))
|
||||||
|
* **upgrade:** allow attribute selectors for components in ng2 which are not part of upgrade ([#11808](https://github.com/angular/angular/issues/11808)) ([b81e2e7](https://github.com/angular/angular/commit/b81e2e7)), closes [#11280](https://github.com/angular/angular/issues/11280)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<a name="2.0.0"></a>
|
||||||
|
# [2.0.0](https://github.com/angular/angular/compare/2.0.0-rc.7...2.0.0) (2016-09-14)
|
||||||
|
|
||||||
|
|
||||||
|
### Bug Fixes
|
||||||
|
|
||||||
|
* **platform-browser:** provide Title service as part of the module ([#11605](https://github.com/angular/angular/issues/11605)) ([85d9db6](https://github.com/angular/angular/commit/85d9db6)), closes [#11600](https://github.com/angular/angular/issues/11600)
|
||||||
|
* **upgrade:** correct the main entry path in package.json ([a2519c6](https://github.com/angular/angular/commit/a2519c6))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<a name="2.0.0-rc.7"></a>
|
<a name="2.0.0-rc.7"></a>
|
||||||
# [2.0.0-rc.7](https://github.com/angular/angular/compare/2.0.0-rc.6...2.0.0-rc.7) (2016-09-13)
|
# [2.0.0-rc.7](https://github.com/angular/angular/compare/2.0.0-rc.6...2.0.0-rc.7) (2016-09-13)
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ Help us keep Angular open and inclusive. Please read and follow our [Code of Con
|
|||||||
## <a name="question"></a> Got a Question or Problem?
|
## <a name="question"></a> Got a Question or Problem?
|
||||||
|
|
||||||
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
If you have questions about how to *use* Angular, please direct them to the [Google Group][angular-group]
|
||||||
discussion list or [StackOverflow][stackoverflow]. Please note that Angular 2 is still in early developer preview, and the core team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
discussion list or [StackOverflow][stackoverflow]. Please note that the Angular team's capacity to answer usage questions is limited. We are also available on [Gitter][gitter].
|
||||||
|
|
||||||
## <a name="issue"></a> Found an Issue?
|
## <a name="issue"></a> Found an Issue?
|
||||||
If you find a bug in the source code, you can help us by
|
If you find a bug in the source code, you can help us by
|
||||||
@ -28,8 +28,7 @@ If you find a bug in the source code, you can help us by
|
|||||||
## <a name="feature"></a> Want a Feature?
|
## <a name="feature"></a> Want a Feature?
|
||||||
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
You can *request* a new feature by [submitting an issue](#submit-issue) to our [GitHub
|
||||||
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
Repository][github]. If you would like to *implement* a new feature, please submit an issue with
|
||||||
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
|
a proposal for your work first, to be sure that we can use it.
|
||||||
and we are not ready to accept major contributions ahead of the full release.
|
|
||||||
Please consider what kind of change it is:
|
Please consider what kind of change it is:
|
||||||
|
|
||||||
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
* For a **Major Feature**, first open an issue and outline your proposal so that it can be
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
[](https://npmjs.org/package/angular2)
|
[](https://npmjs.org/package/angular2)
|
||||||
|
|
||||||
[](https://saucelabs.com/u/angular2-ci)
|
[](https://saucelabs.com/u/angular2-ci)
|
||||||
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][https://www.browserstack.com/].*
|
*Safari (7+), iOS (7+), Edge (14) and IE mobile (11) are tested on [BrowserStack][browserstack].*
|
||||||
|
|
||||||
Angular
|
Angular
|
||||||
=========
|
=========
|
||||||
@ -17,7 +17,6 @@ repository for [Angular 2][ng2] Typescript/JavaScript (JS).
|
|||||||
|
|
||||||
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
|
||||||
|
|
||||||
Angular 2 is currently in **Release Candidate**.
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
@ -29,10 +28,9 @@ Angular 2 is currently in **Release Candidate**.
|
|||||||
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
Want to file a bug, contribute some code, or improve documentation? Excellent! Read up on our
|
||||||
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
guidelines for [contributing][contributing] and then check out one of our issues in the [hotlist: community-help](https://github.com/angular/angular/labels/hotlist%3A%20community-help).
|
||||||
|
|
||||||
|
[browserstack]: https://www.browserstack.com/
|
||||||
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
|
||||||
[dart]: http://www.dartlang.org
|
[dart]: http://www.dartlang.org
|
||||||
[dartium]: http://www.dartlang.org/tools/dartium
|
|
||||||
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
|
||||||
[ng2]: http://angular.io
|
[ng2]: http://angular.io
|
||||||
[ngDart]: http://angulardart.org
|
[ngDart]: http://angulardart.org
|
||||||
|
@ -22,9 +22,11 @@ var CIconfiguration = {
|
|||||||
'Safari7': { unitTest: {target: 'BS', 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}},
|
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
|
'Safari10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'iOS7': { unitTest: {target: 'BS', required: true}, 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}},
|
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
'iOS9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
|
'iOS10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
|
||||||
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
'WindowsPhone': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -83,6 +85,12 @@ var customLaunchers = {
|
|||||||
platform: 'OS X 10.11',
|
platform: 'OS X 10.11',
|
||||||
version: '9.0'
|
version: '9.0'
|
||||||
},
|
},
|
||||||
|
'SL_SAFARI10': {
|
||||||
|
base: 'SauceLabs',
|
||||||
|
browserName: 'safari',
|
||||||
|
platform: 'OS X 10.12',
|
||||||
|
version: '10.0'
|
||||||
|
},
|
||||||
'SL_IOS7': {
|
'SL_IOS7': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'iphone',
|
browserName: 'iphone',
|
||||||
@ -101,6 +109,12 @@ var customLaunchers = {
|
|||||||
platform: 'OS X 10.10',
|
platform: 'OS X 10.10',
|
||||||
version: '9.3'
|
version: '9.3'
|
||||||
},
|
},
|
||||||
|
'SL_IOS10': {
|
||||||
|
base: 'SauceLabs',
|
||||||
|
browserName: 'iphone',
|
||||||
|
platform: 'OS X 10.10',
|
||||||
|
version: '10.0'
|
||||||
|
},
|
||||||
'SL_IE9': {
|
'SL_IE9': {
|
||||||
base: 'SauceLabs',
|
base: 'SauceLabs',
|
||||||
browserName: 'internet explorer',
|
browserName: 'internet explorer',
|
||||||
@ -186,6 +200,12 @@ var customLaunchers = {
|
|||||||
os: 'OS X',
|
os: 'OS X',
|
||||||
os_version: 'El Capitan'
|
os_version: 'El Capitan'
|
||||||
},
|
},
|
||||||
|
'BS_SAFARI10': {
|
||||||
|
base: 'BrowserStack',
|
||||||
|
browser: 'safari',
|
||||||
|
os: 'OS X',
|
||||||
|
os_version: 'Sierra'
|
||||||
|
},
|
||||||
'BS_IOS7': {
|
'BS_IOS7': {
|
||||||
base: 'BrowserStack',
|
base: 'BrowserStack',
|
||||||
device: 'iPhone 5S',
|
device: 'iPhone 5S',
|
||||||
@ -204,6 +224,12 @@ var customLaunchers = {
|
|||||||
os: 'ios',
|
os: 'ios',
|
||||||
os_version: '9.1'
|
os_version: '9.1'
|
||||||
},
|
},
|
||||||
|
'BS_IOS10': {
|
||||||
|
base: 'BrowserStack',
|
||||||
|
device: 'iPhone SE',
|
||||||
|
os: 'ios',
|
||||||
|
os_version: '10.0'
|
||||||
|
},
|
||||||
'BS_IE9': {
|
'BS_IE9': {
|
||||||
base: 'BrowserStack',
|
base: 'BrowserStack',
|
||||||
browser: 'ie',
|
browser: 'ie',
|
||||||
@ -271,12 +297,12 @@ var customLaunchers = {
|
|||||||
|
|
||||||
var sauceAliases = {
|
var sauceAliases = {
|
||||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
|
'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'],
|
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
|
||||||
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
|
||||||
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
|
||||||
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
|
||||||
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
|
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
|
||||||
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
|
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
|
||||||
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
|
||||||
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
|
||||||
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
|
||||||
@ -285,12 +311,12 @@ var sauceAliases = {
|
|||||||
|
|
||||||
var browserstackAliases = {
|
var browserstackAliases = {
|
||||||
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
|
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'BrowserStack';}),
|
||||||
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
'DESKTOP': ['BS_CHROME', 'BS_FIREFOX', 'BS_IE9', 'BS_IE10', 'BS_IE11', 'BS_EDGE', 'BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
|
||||||
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
|
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10', 'BS_WINDOWSPHONE'],
|
||||||
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
|
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
|
||||||
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
|
||||||
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
|
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
|
||||||
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
|
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
|
||||||
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
|
||||||
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
|
||||||
};
|
};
|
||||||
|
164
build.sh
164
build.sh
@ -4,74 +4,97 @@ set -e -o pipefail
|
|||||||
|
|
||||||
cd `dirname $0`
|
cd `dirname $0`
|
||||||
|
|
||||||
|
PACKAGES=(core
|
||||||
|
compiler
|
||||||
|
common
|
||||||
|
forms
|
||||||
|
platform-browser
|
||||||
|
platform-browser-dynamic
|
||||||
|
platform-server
|
||||||
|
platform-webworker
|
||||||
|
platform-webworker-dynamic
|
||||||
|
http
|
||||||
|
router
|
||||||
|
upgrade
|
||||||
|
compiler-cli
|
||||||
|
benchpress)
|
||||||
|
BUILD_ALL=true
|
||||||
|
BUNDLE=true
|
||||||
|
|
||||||
|
for ARG in "$@"; do
|
||||||
|
case "$ARG" in
|
||||||
|
--packages=*)
|
||||||
|
PACKAGES_STR=${ARG#--packages=}
|
||||||
|
PACKAGES=( ${PACKAGES_STR//,/ } )
|
||||||
|
BUILD_ALL=false
|
||||||
|
;;
|
||||||
|
--bundle=*)
|
||||||
|
BUNDLE=( "${ARG#--bundle=}" )
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Unknown option $ARG."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||||
|
|
||||||
|
|
||||||
rm -rf ./dist/all/
|
|
||||||
mkdir -p ./dist/all/
|
|
||||||
|
|
||||||
TSCONFIG=./tools/tsconfig.json
|
|
||||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
|
||||||
$(npm bin)/tsc -p ${TSCONFIG}
|
|
||||||
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
|
||||||
|
|
||||||
echo "====== Copying files needed for e2e tests ====="
|
|
||||||
cp -r ./modules/playground ./dist/all/
|
|
||||||
cp -r ./modules/playground/favicon.ico ./dist/
|
|
||||||
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
|
||||||
mkdir ./dist/all/playground/vendor
|
|
||||||
cd ./dist/all/playground/vendor
|
|
||||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
|
||||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
|
||||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
|
||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
|
||||||
ln -s ../../../../node_modules/rxjs .
|
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
|
||||||
cd -
|
|
||||||
|
|
||||||
echo "====== Copying files needed for benchmarks ====="
|
|
||||||
cp -r ./modules/benchmarks ./dist/all/
|
|
||||||
cp -r ./modules/benchmarks/favicon.ico ./dist/
|
|
||||||
mkdir ./dist/all/benchmarks/vendor
|
|
||||||
cd ./dist/all/benchmarks/vendor
|
|
||||||
ln -s ../../../../node_modules/core-js/client/core.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
|
||||||
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
|
||||||
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
|
||||||
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
|
||||||
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
|
||||||
ln -s ../../../../node_modules/rxjs .
|
|
||||||
ln -s ../../../../node_modules/angular/angular.js .
|
|
||||||
ln -s ../../../../bower_components/polymer .
|
|
||||||
ln -s ../../../../node_modules/incremental-dom/dist/incremental-dom-cjs.js
|
|
||||||
cd -
|
|
||||||
|
|
||||||
TSCONFIG=./modules/tsconfig.json
|
|
||||||
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
|
||||||
# compile ts code
|
|
||||||
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
TSC="node --max-old-space-size=3000 dist/tools/@angular/tsc-wrapped/src/main"
|
||||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||||
$TSC -p modules/tsconfig.json
|
TSCONFIG=./tools/tsconfig.json
|
||||||
|
echo "====== (tools)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||||
|
rm -rf ./dist/tools/
|
||||||
|
mkdir -p ./dist/tools/
|
||||||
|
$(npm bin)/tsc -p ${TSCONFIG}
|
||||||
|
|
||||||
rm -rf ./dist/packages-dist
|
cp ./tools/@angular/tsc-wrapped/package.json ./dist/tools/@angular/tsc-wrapped
|
||||||
|
|
||||||
for PACKAGE in \
|
if [[ ${BUILD_ALL} == true ]]; then
|
||||||
core \
|
rm -rf ./dist/all/
|
||||||
compiler \
|
mkdir -p ./dist/all/
|
||||||
common \
|
|
||||||
forms \
|
echo "====== Copying files needed for e2e tests ====="
|
||||||
platform-browser \
|
cp -r ./modules/playground ./dist/all/
|
||||||
platform-browser-dynamic \
|
cp -r ./modules/playground/favicon.ico ./dist/
|
||||||
platform-server \
|
#rsync -aP ./modules/playground/* ./dist/all/playground/
|
||||||
platform-webworker \
|
mkdir ./dist/all/playground/vendor
|
||||||
platform-webworker-dynamic \
|
cd ./dist/all/playground/vendor
|
||||||
http \
|
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||||
router \
|
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||||
upgrade \
|
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||||
compiler-cli \
|
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||||
benchpress
|
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||||
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
|
ln -s ../../../../node_modules/rxjs .
|
||||||
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
|
cd -
|
||||||
|
|
||||||
|
echo "====== Copying files needed for benchmarks ====="
|
||||||
|
cp -r ./modules/benchmarks ./dist/all/
|
||||||
|
cp -r ./modules/benchmarks/favicon.ico ./dist/
|
||||||
|
mkdir ./dist/all/benchmarks/vendor
|
||||||
|
cd ./dist/all/benchmarks/vendor
|
||||||
|
ln -s ../../../../node_modules/core-js/client/core.js .
|
||||||
|
ln -s ../../../../node_modules/zone.js/dist/zone.js .
|
||||||
|
ln -s ../../../../node_modules/zone.js/dist/long-stack-trace-zone.js .
|
||||||
|
ln -s ../../../../node_modules/systemjs/dist/system.src.js .
|
||||||
|
ln -s ../../../../node_modules/base64-js/lib/b64.js .
|
||||||
|
ln -s ../../../../node_modules/reflect-metadata/Reflect.js .
|
||||||
|
ln -s ../../../../node_modules/rxjs .
|
||||||
|
ln -s ../../../../node_modules/angular/angular.js .
|
||||||
|
ln -s ../../../../bower_components/polymer .
|
||||||
|
ln -s ../../../../node_modules/incremental-dom/dist/incremental-dom-cjs.js
|
||||||
|
cd -
|
||||||
|
|
||||||
|
TSCONFIG=./modules/tsconfig.json
|
||||||
|
echo "====== (all)COMPILING: \$(npm bin)/tsc -p ${TSCONFIG} ====="
|
||||||
|
# compile ts code
|
||||||
|
$TSC -p modules/tsconfig.json
|
||||||
|
|
||||||
|
rm -rf ./dist/packages-dist
|
||||||
|
fi
|
||||||
|
|
||||||
|
for PACKAGE in ${PACKAGES[@]}
|
||||||
do
|
do
|
||||||
PWD=`pwd`
|
PWD=`pwd`
|
||||||
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
SRCDIR=${PWD}/modules/@angular/${PACKAGE}
|
||||||
@ -81,10 +104,13 @@ do
|
|||||||
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
UMD_ES5_MIN_PATH=${DESTDIR}/bundles/${PACKAGE}.umd.min.js
|
||||||
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
LICENSE_BANNER=${PWD}/modules/@angular/license-banner.txt
|
||||||
|
|
||||||
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig.json ====="
|
rm -rf ${DESTDIR}
|
||||||
|
|
||||||
|
echo "====== COMPILING: ${TSC} -p ${SRCDIR}/tsconfig-build.json ====="
|
||||||
$TSC -p ${SRCDIR}/tsconfig-build.json
|
$TSC -p ${SRCDIR}/tsconfig-build.json
|
||||||
|
|
||||||
cp ${SRCDIR}/package.json ${DESTDIR}/
|
cp ${SRCDIR}/package.json ${DESTDIR}/
|
||||||
|
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
|
||||||
|
|
||||||
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
|
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
|
||||||
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"
|
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"
|
||||||
@ -103,7 +129,12 @@ do
|
|||||||
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
find ${DESTDIR} -type f -name '*.d.ts' -print0 | xargs -0 sed -i -E 's/^( +)abstract ([[:alnum:]]+\:)/\1\2/g'
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
|
if [[ ${PACKAGE} == benchpress ]]; then
|
||||||
|
cp ${SRCDIR}/*.md ${DESTDIR}
|
||||||
|
cp -r ${SRCDIR}/docs ${DESTDIR}
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${BUNDLE} == true && ${PACKAGE} != compiler-cli && ${PACKAGE} != benchpress ]]; then
|
||||||
|
|
||||||
echo "====== BUNDLING: ${SRCDIR} ====="
|
echo "====== BUNDLING: ${SRCDIR} ====="
|
||||||
mkdir ${DESTDIR}/bundles
|
mkdir ${DESTDIR}/bundles
|
||||||
@ -131,5 +162,4 @@ do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# TODO(i): uncomment once the protractor typings issue is resolved
|
./modules/@angular/examples/build.sh
|
||||||
#./modules/@angular/examples/build.sh
|
|
||||||
|
80
gulpfile.js
80
gulpfile.js
@ -2,18 +2,26 @@
|
|||||||
|
|
||||||
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
|
// 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.
|
// This is to ensure that we catch env issues before we error while requiring other dependencies.
|
||||||
require('./tools/check-environment')(
|
require('./tools/check-environment')({
|
||||||
{requiredNpmVersion: '>=3.5.3 <4.0.0', requiredNodeVersion: '>=5.4.1 <6.0.0'});
|
requiredNpmVersion: '>=3.5.3 <4.0.0',
|
||||||
|
requiredNodeVersion: '>=5.4.1 <7.0.0',
|
||||||
|
});
|
||||||
|
|
||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
|
||||||
const srcsToFmt =
|
// clang-format entry points
|
||||||
['tools/**/*.ts', 'modules/@angular/**/*.ts', '!tools/public_api_guard/**/*.d.ts',
|
const srcsToFmt = [
|
||||||
'modules/playground/**/*.ts', 'modules/benchmarks/**/*.ts', 'modules/e2e_util/**/*.ts'];
|
'modules/@angular/**/*.ts',
|
||||||
|
'modules/benchmarks/**/*.ts',
|
||||||
|
'modules/e2e_util/**/*.ts',
|
||||||
|
'modules/playground/**/*.ts',
|
||||||
|
'tools/**/*.ts',
|
||||||
|
'!tools/public_api_guard/**/*.d.ts',
|
||||||
|
];
|
||||||
|
|
||||||
|
// Check source code for formatting errors (clang-format)
|
||||||
gulp.task('format:enforce', () => {
|
gulp.task('format:enforce', () => {
|
||||||
const format = require('gulp-clang-format');
|
const format = require('gulp-clang-format');
|
||||||
const clangFormat = require('clang-format');
|
const clangFormat = require('clang-format');
|
||||||
@ -21,6 +29,7 @@ gulp.task('format:enforce', () => {
|
|||||||
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Format the source code with clang-format (see .clang-format)
|
||||||
gulp.task('format', () => {
|
gulp.task('format', () => {
|
||||||
const format = require('gulp-clang-format');
|
const format = require('gulp-clang-format');
|
||||||
const clangFormat = require('clang-format');
|
const clangFormat = require('clang-format');
|
||||||
@ -49,7 +58,7 @@ const entrypoints = [
|
|||||||
'dist/packages-dist/http/index.d.ts',
|
'dist/packages-dist/http/index.d.ts',
|
||||||
'dist/packages-dist/http/testing/index.d.ts',
|
'dist/packages-dist/http/testing/index.d.ts',
|
||||||
'dist/packages-dist/forms/index.d.ts',
|
'dist/packages-dist/forms/index.d.ts',
|
||||||
'dist/packages-dist/router/index.d.ts'
|
'dist/packages-dist/router/index.d.ts',
|
||||||
];
|
];
|
||||||
const publicApiDir = path.normalize('tools/public_api_guard');
|
const publicApiDir = path.normalize('tools/public_api_guard');
|
||||||
const publicApiArgs = [
|
const publicApiArgs = [
|
||||||
@ -58,22 +67,24 @@ const publicApiArgs = [
|
|||||||
'--allowModuleIdentifiers', 'jasmine',
|
'--allowModuleIdentifiers', 'jasmine',
|
||||||
'--allowModuleIdentifiers', 'protractor',
|
'--allowModuleIdentifiers', 'protractor',
|
||||||
'--allowModuleIdentifiers', 'angular',
|
'--allowModuleIdentifiers', 'angular',
|
||||||
'--onStabilityMissing', 'error'
|
'--onStabilityMissing', 'error',
|
||||||
].concat(entrypoints);
|
].concat(entrypoints);
|
||||||
|
|
||||||
|
// Build angular
|
||||||
gulp.task('build.sh', (done) => {
|
gulp.task('build.sh', (done) => {
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
childProcess.exec(path.join(__dirname, 'build.sh'), error => done(error));
|
childProcess.exec(path.join(__dirname, 'build.sh'), done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enforce that the public API matches the golden files
|
||||||
// Note that these two commands work on built d.ts files instead of the source
|
// Note that these two commands work on built d.ts files instead of the source
|
||||||
gulp.task('public-api:enforce', (done) => {
|
gulp.task('public-api:enforce', (done) => {
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
childProcess
|
childProcess
|
||||||
.spawn(
|
.spawn(
|
||||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||||
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
['--verifyDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||||
.on('close', (errorCode) => {
|
.on('close', (errorCode) => {
|
||||||
if (errorCode !== 0) {
|
if (errorCode !== 0) {
|
||||||
@ -85,17 +96,29 @@ gulp.task('public-api:enforce', (done) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Generate the public API golden files
|
||||||
gulp.task('public-api:update', ['build.sh'], (done) => {
|
gulp.task('public-api:update', ['build.sh'], (done) => {
|
||||||
const childProcess = require('child_process');
|
const childProcess = require('child_process');
|
||||||
|
|
||||||
childProcess
|
childProcess
|
||||||
.spawn(
|
.spawn(
|
||||||
path.join(__dirname, `/node_modules/.bin/ts-api-guardian${/^win/.test(os.platform()) ? '.cmd' : ''}`),
|
path.join(__dirname, platformScriptPath(`/node_modules/.bin/ts-api-guardian`)),
|
||||||
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
['--outDir', publicApiDir].concat(publicApiArgs), {stdio: 'inherit'})
|
||||||
.on('close', (errorCode) => done(errorCode));
|
.on('close', done);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
// Checks tests for presence of ddescribe, fdescribe, fit, iit and fails the build if one of the focused tests is found.
|
||||||
|
// Currently xdescribe and xit are _not_ reported as errors since there are a couple of excluded tests in our code base.
|
||||||
|
gulp.task('check-tests', function() {
|
||||||
|
const ddescribeIit = require('gulp-ddescribe-iit');
|
||||||
|
return gulp.src([
|
||||||
|
'modules/**/*.spec.ts',
|
||||||
|
'modules/**/*_spec.ts',
|
||||||
|
]).pipe(ddescribeIit({allowDisabledTests: true}));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check the coding standards and programming errors
|
||||||
|
gulp.task('lint', ['check-tests', 'format:enforce', 'tools:build'], () => {
|
||||||
const tslint = require('gulp-tslint');
|
const tslint = require('gulp-tslint');
|
||||||
// Built-in rules are at
|
// Built-in rules are at
|
||||||
// https://github.com/palantir/tslint#supported-rules
|
// https://github.com/palantir/tslint#supported-rules
|
||||||
@ -105,22 +128,23 @@ gulp.task('lint', ['format:enforce', 'tools:build'], () => {
|
|||||||
tslint: require('tslint').default,
|
tslint: require('tslint').default,
|
||||||
configuration: tslintConfig,
|
configuration: tslintConfig,
|
||||||
rulesDirectory: 'dist/tools/tslint',
|
rulesDirectory: 'dist/tools/tslint',
|
||||||
formatter: 'prose'
|
formatter: 'prose',
|
||||||
}))
|
}))
|
||||||
.pipe(tslint.report({emitError: true}));
|
.pipe(tslint.report({emitError: true}));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
gulp.task('tools:build', (done) => { tsc('tools/', done); });
|
||||||
|
|
||||||
|
// Check for circular dependency in the source code
|
||||||
gulp.task('check-cycle', (done) => {
|
gulp.task('check-cycle', (done) => {
|
||||||
const madge = require('madge');
|
const madge = require('madge');
|
||||||
|
|
||||||
var dependencyObject = madge(['dist/all/'], {
|
const dependencyObject = madge(['dist/all/'], {
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
extensions: ['.js'],
|
extensions: ['.js'],
|
||||||
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
|
||||||
});
|
});
|
||||||
var circularDependencies = dependencyObject.circular().getArray();
|
const circularDependencies = dependencyObject.circular().getArray();
|
||||||
if (circularDependencies.length > 0) {
|
if (circularDependencies.length > 0) {
|
||||||
console.log('Found circular dependencies!');
|
console.log('Found circular dependencies!');
|
||||||
console.log(circularDependencies);
|
console.log(circularDependencies);
|
||||||
@ -129,33 +153,36 @@ gulp.task('check-cycle', (done) => {
|
|||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Serve the built files
|
||||||
gulp.task('serve', () => {
|
gulp.task('serve', () => {
|
||||||
let connect = require('gulp-connect');
|
const connect = require('gulp-connect');
|
||||||
let cors = require('cors');
|
const cors = require('cors');
|
||||||
|
|
||||||
connect.server({
|
connect.server({
|
||||||
root: `${__dirname}/dist`,
|
root: `${__dirname}/dist`,
|
||||||
port: 8000,
|
port: 8000,
|
||||||
livereload: false,
|
livereload: false,
|
||||||
open: false,
|
open: false,
|
||||||
middleware: (connect, opt) => [cors()]
|
middleware: (connect, opt) => [cors()],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Serve the examples
|
||||||
gulp.task('serve-examples', () => {
|
gulp.task('serve-examples', () => {
|
||||||
let connect = require('gulp-connect');
|
const connect = require('gulp-connect');
|
||||||
let cors = require('cors');
|
const cors = require('cors');
|
||||||
|
|
||||||
connect.server({
|
connect.server({
|
||||||
root: `${__dirname}/dist/examples`,
|
root: `${__dirname}/dist/examples`,
|
||||||
port: 8001,
|
port: 8001,
|
||||||
livereload: false,
|
livereload: false,
|
||||||
open: false,
|
open: false,
|
||||||
middleware: (connect, opt) => [cors()]
|
middleware: (connect, opt) => [cors()],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Update the changelog with the latest changes
|
||||||
gulp.task('changelog', () => {
|
gulp.task('changelog', () => {
|
||||||
const conventionalChangelog = require('gulp-conventional-changelog');
|
const conventionalChangelog = require('gulp-conventional-changelog');
|
||||||
|
|
||||||
@ -177,8 +204,13 @@ function tsc(projectPath, done) {
|
|||||||
|
|
||||||
childProcess
|
childProcess
|
||||||
.spawn(
|
.spawn(
|
||||||
path.normalize(`${__dirname}/node_modules/.bin/tsc`) + (/^win/.test(os.platform()) ? '.cmd' : ''),
|
path.normalize(platformScriptPath(`${__dirname}/node_modules/.bin/tsc`)),
|
||||||
['-p', path.join(__dirname, projectPath)],
|
['-p', path.join(__dirname, projectPath)],
|
||||||
{stdio: 'inherit'})
|
{stdio: 'inherit'})
|
||||||
.on('close', (errorCode) => done(errorCode));
|
.on('close', done);
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the script path for the current platform
|
||||||
|
function platformScriptPath(path) {
|
||||||
|
return /^win/.test(os.platform()) ? `${path}.cmd` : path;
|
||||||
}
|
}
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
Angular2
|
|
||||||
=========
|
|
||||||
|
|
||||||
The sources for this package are in the main [Angular2](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo. This is the repository for the upcoming 2.0 version. If you're looking for the current official version of Angular you should go to [angular/angular.js](https://github.com/angular/angular.js)
|
|
||||||
|
|
||||||
This package contains different sources for different users:
|
|
||||||
|
|
||||||
1. The files located in the root folder can be consumed using CommonJS.
|
|
||||||
2. The files under `/es6` are es6 compatible files that can be transpiled to
|
|
||||||
es5 using any transpiler. This contains:
|
|
||||||
* `dev/`: a development version that includes runtime type assertions
|
|
||||||
* `prod/`: a production version that does not include runtime type assertions
|
|
||||||
3. The files under `/ts` are the TypeScript source files.
|
|
||||||
|
|
||||||
License: Apache MIT 2.0
|
|
6
modules/@angular/README.md
Normal file
6
modules/@angular/README.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
Angular
|
||||||
|
=======
|
||||||
|
|
||||||
|
The sources for this package are in the main [Angular](https://github.com/angular/angular) repo. Please file issues and pull requests against that repo.
|
||||||
|
|
||||||
|
License: MIT
|
@ -1,15 +1,13 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/benchpress",
|
"name": "@angular/benchpress",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.1.0",
|
||||||
"description": "Benchpress - a framework for e2e performance tests",
|
"description": "Benchpress - a framework for e2e performance tests",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/core": "0.0.0-PLACEHOLDER",
|
"@angular/core": "^2.0.0-rc.7",
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"rxjs": "5.0.0-beta.12"
|
"rxjs": "5.0.0-beta.12",
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"jpm": "1.1.4",
|
"jpm": "1.1.4",
|
||||||
"firefox-profile": "0.4.0",
|
"firefox-profile": "0.4.0",
|
||||||
"selenium-webdriver": "^2.53.3"
|
"selenium-webdriver": "^2.53.3"
|
||||||
|
16
modules/@angular/benchpress/publish.sh
Executable file
16
modules/@angular/benchpress/publish.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
cd $(dirname $0)/../../..
|
||||||
|
ROOTDIR=$(pwd)
|
||||||
|
SRCDIR=${ROOTDIR}/modules/@angular/benchpress
|
||||||
|
DESTDIR=${ROOTDIR}/dist/packages-dist/benchpress
|
||||||
|
|
||||||
|
rm -fr ${DESTDIR}
|
||||||
|
|
||||||
|
echo "====== BUILDING... ====="
|
||||||
|
./build.sh --packages=core,benchpress --bundle=false
|
||||||
|
|
||||||
|
echo "====== PUBLISHING: ${DESTDIR} ====="
|
||||||
|
npm publish ${DESTDIR} --access public
|
@ -42,11 +42,11 @@ class Profiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
addStartEvent(name: string, timeStarted: number) {
|
addStartEvent(name: string, timeStarted: number) {
|
||||||
this._markerEvents.push({ph: 'b', ts: timeStarted - this._profilerStartTime, name: name});
|
this._markerEvents.push({ph: 'B', ts: timeStarted - this._profilerStartTime, name: name});
|
||||||
}
|
}
|
||||||
|
|
||||||
addEndEvent(name: string, timeEnded: number) {
|
addEndEvent(name: string, timeEnded: number) {
|
||||||
this._markerEvents.push({ph: 'e', ts: timeEnded - this._profilerStartTime, name: name});
|
this._markerEvents.push({ph: 'E', ts: timeEnded - this._profilerStartTime, name: name});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,18 +6,15 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Map} from './facade/collection';
|
|
||||||
import {Date, DateWrapper} from './facade/lang';
|
|
||||||
|
|
||||||
export class MeasureValues {
|
export class MeasureValues {
|
||||||
constructor(
|
constructor(
|
||||||
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
|
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
|
||||||
|
|
||||||
toJson() {
|
toJson() {
|
||||||
return {
|
return {
|
||||||
'timeStamp': DateWrapper.toJson(this.timeStamp),
|
'timeStamp': this.timeStamp.toJSON(),
|
||||||
'runIndex': this.runIndex,
|
'runIndex': this.runIndex,
|
||||||
'values': this.values
|
'values': this.values,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
import {Inject, Injectable, OpaqueToken} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
|
||||||
import {Math, NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
|
|
||||||
import {Metric} from '../metric';
|
import {Metric} from '../metric';
|
||||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||||
|
|
||||||
@ -95,8 +93,9 @@ export class PerflogMetric extends Metric {
|
|||||||
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
res['frameTime.smooth'] = 'percentage of frames that hit 60fps';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StringMapWrapper.forEach(
|
for (let name in this._microMetrics) {
|
||||||
this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); });
|
res[name] = this._microMetrics[name];
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,8 +125,8 @@ export class PerflogMetric extends Metric {
|
|||||||
.then((_) => this._endMeasure(restartMeasure))
|
.then((_) => this._endMeasure(restartMeasure))
|
||||||
.then((forceGcMeasureValues) => {
|
.then((forceGcMeasureValues) => {
|
||||||
this._captureFrames = originalFrameCaptureValue;
|
this._captureFrames = originalFrameCaptureValue;
|
||||||
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
|
measureValues['forcedGcTime'] = forceGcMeasureValues['gcTime'];
|
||||||
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
|
measureValues['forcedGcAmount'] = forceGcMeasureValues['gcAmount'];
|
||||||
return measureValues;
|
return measureValues;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -152,7 +151,7 @@ export class PerflogMetric extends Metric {
|
|||||||
return this._driverExtension.readPerfLog().then((events) => {
|
return this._driverExtension.readPerfLog().then((events) => {
|
||||||
this._addEvents(events);
|
this._addEvents(events);
|
||||||
var result = this._aggregateEvents(this._remainingEvents, markName);
|
var result = this._aggregateEvents(this._remainingEvents, markName);
|
||||||
if (isPresent(result)) {
|
if (result) {
|
||||||
this._remainingEvents = events;
|
this._remainingEvents = events;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -166,14 +165,14 @@ export class PerflogMetric extends Metric {
|
|||||||
private _addEvents(events: PerfLogEvent[]) {
|
private _addEvents(events: PerfLogEvent[]) {
|
||||||
var needSort = false;
|
var needSort = false;
|
||||||
events.forEach(event => {
|
events.forEach(event => {
|
||||||
if (StringWrapper.equals(event['ph'], 'X')) {
|
if (event['ph'] === 'X') {
|
||||||
needSort = true;
|
needSort = true;
|
||||||
var startEvent: PerfLogEvent = {};
|
var startEvent: PerfLogEvent = {};
|
||||||
var endEvent: PerfLogEvent = {};
|
var endEvent: PerfLogEvent = {};
|
||||||
StringMapWrapper.forEach(event, (value, prop) => {
|
for (let prop in event) {
|
||||||
(<any>startEvent)[prop] = value;
|
startEvent[prop] = event[prop];
|
||||||
(<any>endEvent)[prop] = value;
|
endEvent[prop] = event[prop];
|
||||||
});
|
}
|
||||||
startEvent['ph'] = 'B';
|
startEvent['ph'] = 'B';
|
||||||
endEvent['ph'] = 'E';
|
endEvent['ph'] = 'E';
|
||||||
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
|
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
|
||||||
@ -185,7 +184,7 @@ export class PerflogMetric extends Metric {
|
|||||||
});
|
});
|
||||||
if (needSort) {
|
if (needSort) {
|
||||||
// Need to sort because of the ph==='X' events
|
// Need to sort because of the ph==='X' events
|
||||||
ListWrapper.sort(this._remainingEvents, (a, b) => {
|
this._remainingEvents.sort((a, b) => {
|
||||||
var diff = a['ts'] - b['ts'];
|
var diff = a['ts'] - b['ts'];
|
||||||
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
|
||||||
});
|
});
|
||||||
@ -208,7 +207,9 @@ export class PerflogMetric extends Metric {
|
|||||||
result['frameTime.worst'] = 0;
|
result['frameTime.worst'] = 0;
|
||||||
result['frameTime.smooth'] = 0;
|
result['frameTime.smooth'] = 0;
|
||||||
}
|
}
|
||||||
StringMapWrapper.forEach(this._microMetrics, (desc, name) => { result[name] = 0; });
|
for (let name in this._microMetrics) {
|
||||||
|
result[name] = 0;
|
||||||
|
}
|
||||||
if (this._receivedData) {
|
if (this._receivedData) {
|
||||||
result['receivedData'] = 0;
|
result['receivedData'] = 0;
|
||||||
}
|
}
|
||||||
@ -218,6 +219,24 @@ export class PerflogMetric extends Metric {
|
|||||||
|
|
||||||
var markStartEvent: PerfLogEvent = null;
|
var markStartEvent: PerfLogEvent = null;
|
||||||
var markEndEvent: PerfLogEvent = null;
|
var markEndEvent: PerfLogEvent = null;
|
||||||
|
events.forEach((event) => {
|
||||||
|
var ph = event['ph'];
|
||||||
|
var name = event['name'];
|
||||||
|
if (ph === 'B' && name === markName) {
|
||||||
|
markStartEvent = event;
|
||||||
|
} else if (ph === 'I' && name === 'navigationStart') {
|
||||||
|
// if a benchmark measures reload of a page, use the last
|
||||||
|
// navigationStart as begin event
|
||||||
|
markStartEvent = event;
|
||||||
|
} else if (ph === 'E' && name === markName) {
|
||||||
|
markEndEvent = event;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (!markStartEvent || !markEndEvent) {
|
||||||
|
// not all events have been received, no further processing for now
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var gcTimeInScript = 0;
|
var gcTimeInScript = 0;
|
||||||
var renderTimeInScript = 0;
|
var renderTimeInScript = 0;
|
||||||
|
|
||||||
@ -228,120 +247,99 @@ export class PerflogMetric extends Metric {
|
|||||||
|
|
||||||
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
var intervalStarts: {[key: string]: PerfLogEvent} = {};
|
||||||
var intervalStartCount: {[key: string]: number} = {};
|
var intervalStartCount: {[key: string]: number} = {};
|
||||||
|
|
||||||
|
var inMeasureRange = false;
|
||||||
events.forEach((event) => {
|
events.forEach((event) => {
|
||||||
var ph = event['ph'];
|
var ph = event['ph'];
|
||||||
var name = event['name'];
|
var name = event['name'];
|
||||||
var microIterations = 1;
|
var microIterations = 1;
|
||||||
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
|
||||||
if (isPresent(microIterationsMatch)) {
|
if (microIterationsMatch) {
|
||||||
name = microIterationsMatch[1];
|
name = microIterationsMatch[1];
|
||||||
microIterations = NumberWrapper.parseInt(microIterationsMatch[2], 10);
|
microIterations = parseInt(microIterationsMatch[2], 10);
|
||||||
|
}
|
||||||
|
if (event === markStartEvent) {
|
||||||
|
inMeasureRange = true;
|
||||||
|
} else if (event === markEndEvent) {
|
||||||
|
inMeasureRange = false;
|
||||||
|
}
|
||||||
|
if (!inMeasureRange || event['pid'] !== markStartEvent['pid']) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, markName)) {
|
if (this._requestCount && name === 'sendRequest') {
|
||||||
markStartEvent = event;
|
|
||||||
} else if (StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, markName)) {
|
|
||||||
markEndEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
let isInstant = StringWrapper.equals(ph, 'I') || StringWrapper.equals(ph, 'i');
|
|
||||||
if (this._requestCount && StringWrapper.equals(name, 'sendRequest')) {
|
|
||||||
result['requestCount'] += 1;
|
result['requestCount'] += 1;
|
||||||
} else if (this._receivedData && StringWrapper.equals(name, 'receivedData') && isInstant) {
|
} else if (this._receivedData && name === 'receivedData' && ph === 'I') {
|
||||||
result['receivedData'] += event['args']['encodedDataLength'];
|
result['receivedData'] += event['args']['encodedDataLength'];
|
||||||
} else if (StringWrapper.equals(name, 'navigationStart')) {
|
}
|
||||||
// We count data + requests since the last navigationStart
|
if (ph === 'B' && name === _MARK_NAME_FRAME_CAPUTRE) {
|
||||||
// (there might be chrome extensions loaded by selenium before our page, so there
|
if (frameCaptureStartEvent) {
|
||||||
// will likely be more than one navigationStart).
|
throw new Error('can capture frames only once per benchmark run');
|
||||||
if (this._receivedData) {
|
|
||||||
result['receivedData'] = 0;
|
|
||||||
}
|
}
|
||||||
if (this._requestCount) {
|
if (!this._captureFrames) {
|
||||||
result['requestCount'] = 0;
|
throw new Error(
|
||||||
|
'found start event for frame capture, but frame capture was not requested in benchpress');
|
||||||
|
}
|
||||||
|
frameCaptureStartEvent = event;
|
||||||
|
} else if (ph === 'E' && name === _MARK_NAME_FRAME_CAPUTRE) {
|
||||||
|
if (!frameCaptureStartEvent) {
|
||||||
|
throw new Error('missing start event for frame capture');
|
||||||
|
}
|
||||||
|
frameCaptureEndEvent = event;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ph === 'I' && frameCaptureStartEvent && !frameCaptureEndEvent && name === 'frame') {
|
||||||
|
frameTimestamps.push(event['ts']);
|
||||||
|
if (frameTimestamps.length >= 2) {
|
||||||
|
frameTimes.push(
|
||||||
|
frameTimestamps[frameTimestamps.length - 1] -
|
||||||
|
frameTimestamps[frameTimestamps.length - 2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (isPresent(markStartEvent) && isBlank(markEndEvent) &&
|
|
||||||
event['pid'] === markStartEvent['pid']) {
|
|
||||||
if (StringWrapper.equals(ph, 'b') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
|
|
||||||
if (isPresent(frameCaptureStartEvent)) {
|
|
||||||
throw new Error('can capture frames only once per benchmark run');
|
|
||||||
}
|
|
||||||
if (!this._captureFrames) {
|
|
||||||
throw new Error(
|
|
||||||
'found start event for frame capture, but frame capture was not requested in benchpress');
|
|
||||||
}
|
|
||||||
frameCaptureStartEvent = event;
|
|
||||||
} else if (
|
|
||||||
StringWrapper.equals(ph, 'e') && StringWrapper.equals(name, _MARK_NAME_FRAME_CAPUTRE)) {
|
|
||||||
if (isBlank(frameCaptureStartEvent)) {
|
|
||||||
throw new Error('missing start event for frame capture');
|
|
||||||
}
|
|
||||||
frameCaptureEndEvent = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isInstant) {
|
if (ph === 'B') {
|
||||||
if (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
|
if (!intervalStarts[name]) {
|
||||||
StringWrapper.equals(name, 'frame')) {
|
intervalStartCount[name] = 1;
|
||||||
frameTimestamps.push(event['ts']);
|
intervalStarts[name] = event;
|
||||||
if (frameTimestamps.length >= 2) {
|
} else {
|
||||||
frameTimes.push(
|
intervalStartCount[name]++;
|
||||||
frameTimestamps[frameTimestamps.length - 1] -
|
|
||||||
frameTimestamps[frameTimestamps.length - 2]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
} else if ((ph === 'E') && intervalStarts[name]) {
|
||||||
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
|
intervalStartCount[name]--;
|
||||||
if (isBlank(intervalStarts[name])) {
|
if (intervalStartCount[name] === 0) {
|
||||||
intervalStartCount[name] = 1;
|
var startEvent = intervalStarts[name];
|
||||||
intervalStarts[name] = event;
|
var duration = (event['ts'] - startEvent['ts']);
|
||||||
} else {
|
intervalStarts[name] = null;
|
||||||
intervalStartCount[name]++;
|
if (name === 'gc') {
|
||||||
}
|
result['gcTime'] += duration;
|
||||||
} else if (
|
var amount =
|
||||||
(StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
|
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
||||||
isPresent(intervalStarts[name])) {
|
result['gcAmount'] += amount;
|
||||||
intervalStartCount[name]--;
|
var majorGc = event['args']['majorGc'];
|
||||||
if (intervalStartCount[name] === 0) {
|
if (majorGc && majorGc) {
|
||||||
var startEvent = intervalStarts[name];
|
result['majorGcTime'] += duration;
|
||||||
var duration = (event['ts'] - startEvent['ts']);
|
|
||||||
intervalStarts[name] = null;
|
|
||||||
if (StringWrapper.equals(name, 'gc')) {
|
|
||||||
result['gcTime'] += duration;
|
|
||||||
var amount =
|
|
||||||
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
|
|
||||||
result['gcAmount'] += amount;
|
|
||||||
var majorGc = event['args']['majorGc'];
|
|
||||||
if (isPresent(majorGc) && majorGc) {
|
|
||||||
result['majorGcTime'] += duration;
|
|
||||||
}
|
|
||||||
if (isPresent(intervalStarts['script'])) {
|
|
||||||
gcTimeInScript += duration;
|
|
||||||
}
|
|
||||||
} else if (StringWrapper.equals(name, 'render')) {
|
|
||||||
result['renderTime'] += duration;
|
|
||||||
if (isPresent(intervalStarts['script'])) {
|
|
||||||
renderTimeInScript += duration;
|
|
||||||
}
|
|
||||||
} else if (StringWrapper.equals(name, 'script')) {
|
|
||||||
result['scriptTime'] += duration;
|
|
||||||
} else if (isPresent(this._microMetrics[name])) {
|
|
||||||
(<any>result)[name] += duration / microIterations;
|
|
||||||
}
|
}
|
||||||
|
if (intervalStarts['script']) {
|
||||||
|
gcTimeInScript += duration;
|
||||||
|
}
|
||||||
|
} else if (name === 'render') {
|
||||||
|
result['renderTime'] += duration;
|
||||||
|
if (intervalStarts['script']) {
|
||||||
|
renderTimeInScript += duration;
|
||||||
|
}
|
||||||
|
} else if (name === 'script') {
|
||||||
|
result['scriptTime'] += duration;
|
||||||
|
} else if (this._microMetrics[name]) {
|
||||||
|
(<any>result)[name] += duration / microIterations;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!isPresent(markStartEvent) || !isPresent(markEndEvent)) {
|
|
||||||
// not all events have been received, no further processing for now
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPresent(markEndEvent) && isPresent(frameCaptureStartEvent) &&
|
if (frameCaptureStartEvent && !frameCaptureEndEvent) {
|
||||||
isBlank(frameCaptureEndEvent)) {
|
|
||||||
throw new Error('missing end event for frame capture');
|
throw new Error('missing end event for frame capture');
|
||||||
}
|
}
|
||||||
if (this._captureFrames && isBlank(frameCaptureStartEvent)) {
|
if (this._captureFrames && !frameCaptureStartEvent) {
|
||||||
throw new Error('frame capture requested in benchpress, but no start event was found');
|
throw new Error('frame capture requested in benchpress, but no start event was found');
|
||||||
}
|
}
|
||||||
if (frameTimes.length > 0) {
|
if (frameTimes.length > 0) {
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken, Provider} from '@angular/core';
|
import {Inject, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
@ -48,9 +48,9 @@ export class UserMetric extends Metric {
|
|||||||
if (values.every(isNumber)) {
|
if (values.every(isNumber)) {
|
||||||
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
|
||||||
.then((_: any[]) => {
|
.then((_: any[]) => {
|
||||||
let map = StringMapWrapper.create();
|
let map: {[k: string]: any} = {};
|
||||||
for (let i = 0, n = names.length; i < n; i++) {
|
for (let i = 0, n = names.length; i < n; i++) {
|
||||||
StringMapWrapper.set(map, names[i], values[i]);
|
map[names[i]] = values[i];
|
||||||
}
|
}
|
||||||
resolve(map);
|
resolve(map);
|
||||||
}, reject);
|
}, reject);
|
||||||
|
@ -49,7 +49,7 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _iterate(lastState: SampleState): Promise<SampleState> {
|
private _iterate(lastState: SampleState): Promise<SampleState> {
|
||||||
var resultPromise: Promise<any>;
|
var resultPromise: Promise<SampleState>;
|
||||||
if (this._prepare !== Options.NO_PREPARE) {
|
if (this._prepare !== Options.NO_PREPARE) {
|
||||||
resultPromise = this._driver.waitFor(this._prepare);
|
resultPromise = this._driver.waitFor(this._prepare);
|
||||||
} else {
|
} else {
|
||||||
@ -77,5 +77,5 @@ export class Sampler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class SampleState {
|
export class SampleState {
|
||||||
constructor(public completeSample: any[], public validSample: any[]) {}
|
constructor(public completeSample: MeasureValues[], public validSample: MeasureValues[]) {}
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,20 @@ import {Options} from './common_options';
|
|||||||
import {isBlank, isPresent} from './facade/lang';
|
import {isBlank, isPresent} from './facade/lang';
|
||||||
|
|
||||||
export type PerfLogEvent = {
|
export type PerfLogEvent = {
|
||||||
cat?: string,
|
[key: string]: any
|
||||||
ph?: 'X' | 'B' | 'E' | 'b' | 'e',
|
} & {
|
||||||
|
ph?: 'X' | 'B' | 'E' | 'I',
|
||||||
ts?: number,
|
ts?: number,
|
||||||
dur?: number,
|
dur?: number,
|
||||||
name?: string,
|
name?: string,
|
||||||
pid?: string,
|
pid?: string,
|
||||||
args?: {encodedDataLength?: number, usedHeapSize?: number, majorGc?: number}
|
args?: {
|
||||||
|
encodedDataLength?: number,
|
||||||
|
usedHeapSize?: number,
|
||||||
|
majorGc?: boolean,
|
||||||
|
url?: string,
|
||||||
|
method?: string
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,8 +71,7 @@ export abstract class WebDriverExtension {
|
|||||||
* Format:
|
* Format:
|
||||||
* - cat: category of the event
|
* - cat: category of the event
|
||||||
* - name: event name: 'script', 'gc', 'render', ...
|
* - name: event name: 'script', 'gc', 'render', ...
|
||||||
* - ph: phase: 'B' (begin), 'E' (end), 'b' (nestable start), 'e' (nestable end), 'X' (Complete
|
* - ph: phase: 'B' (begin), 'E' (end), 'X' (Complete event), 'I' (Instant event)
|
||||||
*event)
|
|
||||||
* - ts: timestamp in ms, e.g. 12345
|
* - ts: timestamp in ms, e.g. 12345
|
||||||
* - pid: process id
|
* - pid: process id
|
||||||
* - args: arguments, e.g. {heapSize: 1234}
|
* - args: arguments, e.g. {heapSize: 1234}
|
||||||
|
@ -9,16 +9,12 @@
|
|||||||
import {Inject, Injectable} from '@angular/core';
|
import {Inject, Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {Options} from '../common_options';
|
import {Options} from '../common_options';
|
||||||
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
|
||||||
import {NumberWrapper, StringWrapper, isBlank, isPresent} from '../facade/lang';
|
|
||||||
import {WebDriverAdapter} from '../web_driver_adapter';
|
import {WebDriverAdapter} from '../web_driver_adapter';
|
||||||
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
import {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the following 'traceCategories' to collect metrics in Chrome:
|
* Set the following 'traceCategories' to collect metrics in Chrome:
|
||||||
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline'
|
* 'v8,blink.console,disabled-by-default-devtools.timeline,devtools.timeline,blink.user_timing'
|
||||||
*
|
*
|
||||||
* In order to collect the frame rate related metrics, add 'benchmark'
|
* In order to collect the frame rate related metrics, add 'benchmark'
|
||||||
* to the list above.
|
* to the list above.
|
||||||
@ -35,18 +31,18 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _parseChromeVersion(userAgent: string): number {
|
private _parseChromeVersion(userAgent: string): number {
|
||||||
if (isBlank(userAgent)) {
|
if (!userAgent) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
var v = StringWrapper.split(userAgent, /Chrom(e|ium)\//g)[2];
|
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
|
||||||
if (isBlank(v)) {
|
if (!v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
v = v.split('.')[0];
|
v = v.split('.')[0];
|
||||||
if (isBlank(v)) {
|
if (!v) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
return NumberWrapper.parseInt(v, 10);
|
return parseInt(v, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
gc() { return this._driver.executeScript('window.gc()'); }
|
gc() { return this._driver.executeScript('window.gc()'); }
|
||||||
@ -57,7 +53,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
timeEnd(name: string, restartName: string = null): Promise<any> {
|
timeEnd(name: string, restartName: string = null): Promise<any> {
|
||||||
var script = `console.timeEnd('${name}');`;
|
var script = `console.timeEnd('${name}');`;
|
||||||
if (isPresent(restartName)) {
|
if (restartName) {
|
||||||
script += `console.time('${restartName}');`;
|
script += `console.time('${restartName}');`;
|
||||||
}
|
}
|
||||||
return this._driver.executeScript(script);
|
return this._driver.executeScript(script);
|
||||||
@ -74,10 +70,10 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
var events: PerfLogEvent[] = [];
|
var events: PerfLogEvent[] = [];
|
||||||
entries.forEach(entry => {
|
entries.forEach(entry => {
|
||||||
var message = JSON.parse(entry['message'])['message'];
|
var message = JSON.parse(entry['message'])['message'];
|
||||||
if (StringWrapper.equals(message['method'], 'Tracing.dataCollected')) {
|
if (message['method'] === 'Tracing.dataCollected') {
|
||||||
events.push(message['params']);
|
events.push(message['params']);
|
||||||
}
|
}
|
||||||
if (StringWrapper.equals(message['method'], 'Tracing.bufferUsage')) {
|
if (message['method'] === 'Tracing.bufferUsage') {
|
||||||
throw new Error('The DevTools trace buffer filled during the test!');
|
throw new Error('The DevTools trace buffer filled during the test!');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -87,107 +83,64 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
|
|
||||||
private _convertPerfRecordsToEvents(
|
private _convertPerfRecordsToEvents(
|
||||||
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
|
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
|
||||||
if (isBlank(normalizedEvents)) {
|
if (!normalizedEvents) {
|
||||||
normalizedEvents = [];
|
normalizedEvents = [];
|
||||||
}
|
}
|
||||||
var majorGCPids = {};
|
|
||||||
chromeEvents.forEach((event) => {
|
chromeEvents.forEach((event) => {
|
||||||
var categories = this._parseCategories(event['cat']);
|
const categories = this._parseCategories(event['cat']);
|
||||||
var name = event['name'];
|
const normalizedEvent = this._convertEvent(event, categories);
|
||||||
if (this._isEvent(categories, name, ['blink.console'])) {
|
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
||||||
normalizedEvents.push(normalizeEvent(event, {'name': name}));
|
|
||||||
} else if (this._isEvent(
|
|
||||||
categories, name, ['benchmark'],
|
|
||||||
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
|
|
||||||
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
|
|
||||||
// following events should be used (if available) for more accurate measurments:
|
|
||||||
// 1st choice: vsync_before - ground truth on Android
|
|
||||||
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
|
|
||||||
// new surfaces framework (not broadly enabled yet)
|
|
||||||
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
|
||||||
// always available if something is rendered
|
|
||||||
var frameCount = event['args']['data']['frame_count'];
|
|
||||||
if (frameCount > 1) {
|
|
||||||
throw new Error('multi-frame render stats not supported');
|
|
||||||
}
|
|
||||||
if (frameCount == 1) {
|
|
||||||
normalizedEvents.push(normalizeEvent(event, {'name': 'frame'}));
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
|
|
||||||
this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
|
||||||
normalizedEvents.push(normalizeEvent(event, {'name': 'render'}));
|
|
||||||
} else if (this._majorChromeVersion < 45) {
|
|
||||||
var normalizedEvent = this._processAsPreChrome45Event(event, categories, majorGCPids);
|
|
||||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
|
||||||
} else {
|
|
||||||
var normalizedEvent = this._processAsPostChrome44Event(event, categories);
|
|
||||||
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
return normalizedEvents;
|
return normalizedEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processAsPreChrome45Event(
|
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
|
||||||
event: {[key: string]: any}, categories: string[], majorGCPids: {[key: string]: any}) {
|
|
||||||
var name = event['name'];
|
var name = event['name'];
|
||||||
var args = event['args'];
|
var args = event['args'];
|
||||||
var pid = event['pid'];
|
if (this._isEvent(categories, name, ['blink.console'])) {
|
||||||
var ph = event['ph'];
|
return normalizeEvent(event, {'name': name});
|
||||||
if (this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'FunctionCall') &&
|
|
||||||
(isBlank(args) || isBlank(args['data']) ||
|
|
||||||
!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript'))) {
|
|
||||||
return normalizeEvent(event, {'name': 'script'});
|
|
||||||
} else if (
|
|
||||||
this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'RecalculateStyles') ||
|
|
||||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Layout') ||
|
|
||||||
this._isEvent(
|
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'UpdateLayerTree') ||
|
|
||||||
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Paint')) {
|
|
||||||
return normalizeEvent(event, {'name': 'render'});
|
|
||||||
} else if (this._isEvent(
|
} else if (this._isEvent(
|
||||||
categories, name, ['disabled-by-default-devtools.timeline'], 'GCEvent')) {
|
categories, name, ['benchmark'],
|
||||||
var normArgs: {[key: string]: any} = {
|
'BenchmarkInstrumentation::ImplThreadRenderingStats')) {
|
||||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
// TODO(goderbauer): Instead of BenchmarkInstrumentation::ImplThreadRenderingStats the
|
||||||
args['usedHeapSizeBefore']
|
// following events should be used (if available) for more accurate measurments:
|
||||||
};
|
// 1st choice: vsync_before - ground truth on Android
|
||||||
if (StringWrapper.equals(ph, 'E')) {
|
// 2nd choice: BenchmarkInstrumentation::DisplayRenderingStats - available on systems with
|
||||||
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
|
// new surfaces framework (not broadly enabled yet)
|
||||||
|
// 3rd choice: BenchmarkInstrumentation::ImplThreadRenderingStats - fallback event that is
|
||||||
|
// always available if something is rendered
|
||||||
|
var frameCount = event['args']['data']['frame_count'];
|
||||||
|
if (frameCount > 1) {
|
||||||
|
throw new Error('multi-frame render stats not supported');
|
||||||
|
}
|
||||||
|
if (frameCount == 1) {
|
||||||
|
return normalizeEvent(event, {'name': 'frame'});
|
||||||
}
|
}
|
||||||
majorGCPids[pid] = false;
|
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
|
||||||
} else if (
|
} else if (
|
||||||
this._isEvent(categories, name, ['v8'], 'majorGC') && StringWrapper.equals(ph, 'B')) {
|
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
|
||||||
majorGCPids[pid] = true;
|
this._isEvent(
|
||||||
}
|
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
|
||||||
return null; // nothing useful in this event
|
return normalizeEvent(event, {'name': 'render'});
|
||||||
}
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
||||||
|
|
||||||
private _processAsPostChrome44Event(event: {[key: string]: any}, categories: string[]) {
|
|
||||||
var name = event['name'];
|
|
||||||
var args = event['args'];
|
|
||||||
if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
|
|
||||||
var normArgs = {
|
var normArgs = {
|
||||||
'majorGc': true,
|
'majorGc': true,
|
||||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
};
|
};
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
|
||||||
var normArgs = {
|
var normArgs = {
|
||||||
'majorGc': false,
|
'majorGc': false,
|
||||||
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
|
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
|
||||||
args['usedHeapSizeBefore']
|
args['usedHeapSizeBefore']
|
||||||
};
|
};
|
||||||
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
|
||||||
} else if (
|
} else if (
|
||||||
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
|
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
|
||||||
(isBlank(args) || isBlank(args['data']) ||
|
(!args || !args['data'] ||
|
||||||
(!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript') &&
|
(args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
|
||||||
!StringWrapper.equals(args['data']['scriptName'], '')))) {
|
return normalizeEvent(event, {'name': 'script'});
|
||||||
|
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'EvaluateScript')) {
|
||||||
return normalizeEvent(event, {'name': 'script'});
|
return normalizeEvent(event, {'name': 'script'});
|
||||||
} else if (this._isEvent(
|
} else if (this._isEvent(
|
||||||
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
|
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
|
||||||
@ -205,7 +158,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
|
||||||
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
|
||||||
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
} else if (this._isEvent(categories, name, ['blink.user_timing'], 'navigationStart')) {
|
||||||
return normalizeEvent(event, {'name': name});
|
return normalizeEvent(event, {'name': 'navigationStart'});
|
||||||
}
|
}
|
||||||
return null; // nothing useful in this event
|
return null; // nothing useful in this event
|
||||||
}
|
}
|
||||||
@ -216,9 +169,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
eventCategories: string[], eventName: string, expectedCategories: string[],
|
eventCategories: string[], eventName: string, expectedCategories: string[],
|
||||||
expectedName: string = null): boolean {
|
expectedName: string = null): boolean {
|
||||||
var hasCategories = expectedCategories.reduce(
|
var hasCategories = expectedCategories.reduce(
|
||||||
(value, cat) => { return value && ListWrapper.contains(eventCategories, cat); }, true);
|
(value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true);
|
||||||
return isBlank(expectedName) ? hasCategories :
|
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
|
||||||
hasCategories && StringWrapper.equals(eventName, expectedName);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
perfLogFeatures(): PerfLogFeatures {
|
perfLogFeatures(): PerfLogFeatures {
|
||||||
@ -226,28 +178,31 @@ export class ChromeDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
supports(capabilities: {[key: string]: any}): boolean {
|
supports(capabilities: {[key: string]: any}): boolean {
|
||||||
return this._majorChromeVersion != -1 &&
|
return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
|
||||||
StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeEvent(
|
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
|
||||||
chromeEvent: {[key: string]: any}, data: {[key: string]: any}): PerfLogEvent {
|
var ph = chromeEvent['ph'].toUpperCase();
|
||||||
var ph = chromeEvent['ph'];
|
if (ph === 'S') {
|
||||||
if (StringWrapper.equals(ph, 'S')) {
|
ph = 'B';
|
||||||
ph = 'b';
|
} else if (ph === 'F') {
|
||||||
} else if (StringWrapper.equals(ph, 'F')) {
|
ph = 'E';
|
||||||
ph = 'e';
|
} else if (ph === 'R') {
|
||||||
|
// mark events from navigation timing
|
||||||
|
ph = 'I';
|
||||||
}
|
}
|
||||||
var result: {[key: string]: any} =
|
var result: {[key: string]: any} =
|
||||||
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
|
||||||
if (chromeEvent['ph'] === 'X') {
|
if (ph === 'X') {
|
||||||
var dur = chromeEvent['dur'];
|
var dur = chromeEvent['dur'];
|
||||||
if (isBlank(dur)) {
|
if (dur === undefined) {
|
||||||
dur = chromeEvent['tdur'];
|
dur = chromeEvent['tdur'];
|
||||||
}
|
}
|
||||||
result['dur'] = isBlank(dur) ? 0.0 : dur / 1000;
|
result['dur'] = !dur ? 0.0 : dur / 1000;
|
||||||
|
}
|
||||||
|
for (let prop in data) {
|
||||||
|
result[prop] = data[prop];
|
||||||
}
|
}
|
||||||
StringMapWrapper.forEach(data, (value, prop) => { result[prop] = value; });
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ export class IOsDriverExtension extends WebDriverExtension {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createEvent(
|
function createEvent(
|
||||||
ph: 'X' | 'B' | 'E' | 'b' | 'e', name: string, time: number, args: any = null) {
|
ph: 'X' | 'B' | 'E' | 'B' | 'E', name: string, time: number, args: any = null) {
|
||||||
var result: PerfLogEvent = {
|
var result: PerfLogEvent = {
|
||||||
'cat': 'timeline',
|
'cat': 'timeline',
|
||||||
'name': name,
|
'name': name,
|
||||||
@ -122,9 +122,9 @@ function createEndEvent(name: string, time: number, args: any = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createMarkStartEvent(name: string, time: number) {
|
function createMarkStartEvent(name: string, time: number) {
|
||||||
return createEvent('b', name, time);
|
return createEvent('B', name, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMarkEndEvent(name: string, time: number) {
|
function createMarkEndEvent(name: string, time: number) {
|
||||||
return createEvent('e', name, time);
|
return createEvent('E', name, time);
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ export function main() {
|
|||||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||||
}
|
}
|
||||||
if (isBlank(microMetrics)) {
|
if (isBlank(microMetrics)) {
|
||||||
microMetrics = StringMapWrapper.create();
|
microMetrics = {};
|
||||||
}
|
}
|
||||||
var providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
Options.DEFAULT_PROVIDERS, PerflogMetric.PROVIDERS,
|
||||||
@ -171,6 +171,22 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should mark and aggregate events since navigationStart',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
var events = [[
|
||||||
|
eventFactory.markStart('benchpress0', 0), eventFactory.start('script', 4),
|
||||||
|
eventFactory.end('script', 6), eventFactory.instant('navigationStart', 7),
|
||||||
|
eventFactory.start('script', 8), eventFactory.end('script', 9),
|
||||||
|
eventFactory.markEnd('benchpress0', 10)
|
||||||
|
]];
|
||||||
|
var metric = createMetric(events, null);
|
||||||
|
metric.beginMeasure().then((_) => metric.endMeasure(false)).then((data) => {
|
||||||
|
expect(data['scriptTime']).toBe(1);
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should restart timing', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var events = [
|
var events = [
|
||||||
[
|
[
|
||||||
|
@ -24,7 +24,7 @@ export function main() {
|
|||||||
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
|
||||||
}
|
}
|
||||||
if (isBlank(userMetrics)) {
|
if (isBlank(userMetrics)) {
|
||||||
userMetrics = StringMapWrapper.create();
|
userMetrics = {};
|
||||||
}
|
}
|
||||||
wdAdapter = new MockDriverAdapter();
|
wdAdapter = new MockDriverAdapter();
|
||||||
var providers: Provider[] = [
|
var providers: Provider[] = [
|
||||||
|
@ -21,16 +21,16 @@ export class TraceEventFactory {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
markStart(name: string, time: number) { return this.create('b', name, time); }
|
markStart(name: string, time: number) { return this.create('B', name, time); }
|
||||||
|
|
||||||
markEnd(name: string, time: number) { return this.create('e', name, time); }
|
markEnd(name: string, time: number) { return this.create('E', name, time); }
|
||||||
|
|
||||||
start(name: string, time: number, args: any = null) { return this.create('B', name, time, args); }
|
start(name: string, time: number, args: any = null) { return this.create('B', name, time, args); }
|
||||||
|
|
||||||
end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); }
|
end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); }
|
||||||
|
|
||||||
instant(name: string, time: number, args: any = null) {
|
instant(name: string, time: number, args: any = null) {
|
||||||
return this.create('i', name, time, args);
|
return this.create('I', name, time, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
complete(name: string, time: number, duration: number, args: any = null) {
|
complete(name: string, time: number, duration: number, args: any = null) {
|
||||||
|
@ -14,8 +14,6 @@ import {TraceEventFactory} from '../trace_event_factory';
|
|||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('chrome driver extension', () => {
|
describe('chrome driver extension', () => {
|
||||||
var CHROME44_USER_AGENT =
|
|
||||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.0 Safari/537.36"';
|
|
||||||
var CHROME45_USER_AGENT =
|
var CHROME45_USER_AGENT =
|
||||||
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
'"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2499.0 Safari/537.36"';
|
||||||
|
|
||||||
@ -41,7 +39,7 @@ export function main() {
|
|||||||
perfRecords = [];
|
perfRecords = [];
|
||||||
}
|
}
|
||||||
if (isBlank(userAgent)) {
|
if (isBlank(userAgent)) {
|
||||||
userAgent = CHROME44_USER_AGENT;
|
userAgent = CHROME45_USER_AGENT;
|
||||||
}
|
}
|
||||||
log = [];
|
log = [];
|
||||||
extension = ReflectiveInjector
|
extension = ReflectiveInjector
|
||||||
@ -89,228 +87,95 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('readPerfLog Chrome44', () => {
|
it('should normalize times to ms and forward ph and pid event properties',
|
||||||
it('should normalize times to ms and forward ph and pid event properties',
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)])
|
||||||
createExtension([chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)])
|
.readPerfLog()
|
||||||
.readPerfLog()
|
.then((events) => {
|
||||||
.then((events) => {
|
expect(events).toEqual([
|
||||||
expect(events).toEqual([
|
normEvents.complete('script', 1.1, 5.5, null),
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
]);
|
||||||
]);
|
async.done();
|
||||||
async.done();
|
});
|
||||||
});
|
}));
|
||||||
}));
|
|
||||||
|
|
||||||
it('should normalize "tdur" to "dur"',
|
it('should normalize "tdur" to "dur"',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
var event: any = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null);
|
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
||||||
event['tdur'] = 5500;
|
event['tdur'] = 5500;
|
||||||
createExtension([event]).readPerfLog().then((events) => {
|
createExtension([event]).readPerfLog().then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
normEvents.complete('script', 1.1, 5.5, null),
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should report FunctionCall events as "script"',
|
it('should report FunctionCall events as "script"',
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
createExtension([chromeTimelineEvents.start('FunctionCall', 0)])
|
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)])
|
||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
normEvents.start('script', 0),
|
normEvents.start('script', 0),
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should report gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should report EvaluateScript events as "script"',
|
||||||
createExtension([
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
createExtension([chromeTimelineV8Events.start('EvaluateScript', 0)])
|
||||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
.readPerfLog()
|
||||||
])
|
.then((events) => {
|
||||||
.readPerfLog()
|
expect(events).toEqual([
|
||||||
.then((events) => {
|
normEvents.start('script', 0),
|
||||||
expect(events).toEqual([
|
]);
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
async.done();
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
|
});
|
||||||
]);
|
}));
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should ignore major gc from different processes',
|
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
createExtension([
|
||||||
createExtension([
|
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||||
v8EventsOtherProcess.start('majorGC', 1100, null),
|
])
|
||||||
v8EventsOtherProcess.end('majorGC', 1200, null),
|
.readPerfLog()
|
||||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
.then((events) => {
|
||||||
])
|
expect(events.length).toEqual(2);
|
||||||
.readPerfLog()
|
expect(events[0]).toEqual(
|
||||||
.then((events) => {
|
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
|
||||||
expect(events).toEqual([
|
expect(events[1]).toEqual(
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
|
async.done();
|
||||||
]);
|
});
|
||||||
async.done();
|
}));
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
createExtension([
|
createExtension(
|
||||||
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
|
[
|
||||||
v8Events.start('majorGC', 1100, null),
|
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
||||||
v8Events.end('majorGC', 1200, null),
|
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
|
||||||
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
|
], )
|
||||||
])
|
.readPerfLog()
|
||||||
.readPerfLog()
|
.then((events) => {
|
||||||
.then((events) => {
|
expect(events.length).toEqual(2);
|
||||||
expect(events).toEqual([
|
expect(events[0]).toEqual(
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
|
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}),
|
expect(events[1]).toEqual(
|
||||||
]);
|
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
||||||
it(`should report ${recordType} as "render"`,
|
it(`should report ${recordType} as "render"`,
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([
|
|
||||||
chromeTimelineEvents.start(recordType, 1234),
|
|
||||||
chromeTimelineEvents.end(recordType, 2345)
|
|
||||||
])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.start('render', 1.234),
|
|
||||||
normEvents.end('render', 2.345),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should ignore FunctionCalls from webdriver',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([chromeTimelineEvents.start(
|
|
||||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('readPerfLog Chrome45', () => {
|
|
||||||
it('should normalize times to ms and forward ph and pid event properties',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should normalize "tdur" to "dur"',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
var event: any = chromeTimelineV8Events.create('X', 'FunctionCall', 1100, null);
|
|
||||||
event['tdur'] = 5500;
|
|
||||||
createExtension([event], CHROME45_USER_AGENT).readPerfLog().then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.complete('script', 1.1, 5.5, null),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report FunctionCall events as "script"',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)], CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.start('script', 0),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[
|
|
||||||
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
|
||||||
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
|
|
||||||
],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events.length).toEqual(2);
|
|
||||||
expect(events[0]).toEqual(
|
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': false}));
|
|
||||||
expect(events[1]).toEqual(
|
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[
|
|
||||||
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
|
|
||||||
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
|
|
||||||
],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events.length).toEqual(2);
|
|
||||||
expect(events[0]).toEqual(
|
|
||||||
normEvents.start('gc', 1.0, {'usedHeapSize': 1000, 'majorGc': true}));
|
|
||||||
expect(events[1]).toEqual(
|
|
||||||
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}));
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
|
|
||||||
it(`should report ${recordType} as "render"`,
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[
|
|
||||||
chrome45TimelineEvents.start(recordType, 1234),
|
|
||||||
chrome45TimelineEvents.end(recordType, 2345)
|
|
||||||
],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([
|
|
||||||
normEvents.start('render', 1.234),
|
|
||||||
normEvents.end('render', 2.345),
|
|
||||||
]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should report UpdateLayoutTree as "render"`,
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
createExtension(
|
createExtension(
|
||||||
[
|
[
|
||||||
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
|
chrome45TimelineEvents.start(recordType, 1234),
|
||||||
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
|
chrome45TimelineEvents.end(recordType, 2345)
|
||||||
],
|
], )
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
@ -320,70 +185,80 @@ export function main() {
|
|||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
it('should ignore FunctionCalls from webdriver',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension([chromeTimelineV8Events.start(
|
|
||||||
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should ignore FunctionCalls with empty scriptName',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report navigationStart',
|
|
||||||
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chromeBlinkUserTimingEvents.start('navigationStart', 1234)], CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([normEvents.start('navigationStart', 1.234)]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chrome45TimelineEvents.instant(
|
|
||||||
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual(
|
|
||||||
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
|
||||||
createExtension(
|
|
||||||
[chrome45TimelineEvents.instant(
|
|
||||||
'ResourceSendRequest', 1234,
|
|
||||||
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})],
|
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
|
||||||
.then((events) => {
|
|
||||||
expect(events).toEqual([normEvents.instant(
|
|
||||||
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it(`should report UpdateLayoutTree as "render"`,
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension(
|
||||||
|
[
|
||||||
|
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
|
||||||
|
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
|
||||||
|
], )
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([
|
||||||
|
normEvents.start('render', 1.234),
|
||||||
|
normEvents.end('render', 2.345),
|
||||||
|
]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should ignore FunctionCalls from webdriver',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chromeTimelineV8Events.start(
|
||||||
|
'FunctionCall', 0, {'data': {'scriptName': 'InjectedScript'}})])
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should ignore FunctionCalls with empty scriptName',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension(
|
||||||
|
[chromeTimelineV8Events.start('FunctionCall', 0, {'data': {'scriptName': ''}})])
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report navigationStart',
|
||||||
|
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chromeBlinkUserTimingEvents.instant('navigationStart', 1234)])
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([normEvents.instant('navigationStart', 1.234)]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report receivedData', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chrome45TimelineEvents.instant(
|
||||||
|
'ResourceReceivedData', 1234, {'data': {'encodedDataLength': 987}})], )
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual(
|
||||||
|
[normEvents.instant('receivedData', 1.234, {'encodedDataLength': 987})]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should report sendRequest', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
|
||||||
|
createExtension([chrome45TimelineEvents.instant(
|
||||||
|
'ResourceSendRequest', 1234,
|
||||||
|
{'data': {'url': 'http://here', 'requestMethod': 'GET'}})], )
|
||||||
|
.readPerfLog()
|
||||||
|
.then((events) => {
|
||||||
|
expect(events).toEqual([normEvents.instant(
|
||||||
|
'sendRequest', 1.234, {'url': 'http://here', 'method': 'GET'})]);
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
describe('readPerfLog (common)', () => {
|
describe('readPerfLog (common)', () => {
|
||||||
|
|
||||||
it('should execute a dummy script before reading them',
|
it('should execute a dummy script before reading them',
|
||||||
@ -404,8 +279,7 @@ export function main() {
|
|||||||
[
|
[
|
||||||
chromeTimelineEvents.start(recordType, 1234),
|
chromeTimelineEvents.start(recordType, 1234),
|
||||||
chromeTimelineEvents.end(recordType, 2345)
|
chromeTimelineEvents.end(recordType, 2345)
|
||||||
],
|
], )
|
||||||
CHROME45_USER_AGENT)
|
|
||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
@ -426,7 +300,7 @@ export function main() {
|
|||||||
.readPerfLog()
|
.readPerfLog()
|
||||||
.then((events) => {
|
.then((events) => {
|
||||||
expect(events).toEqual([
|
expect(events).toEqual([
|
||||||
normEvents.create('i', 'frame', 1.1),
|
normEvents.instant('frame', 1.1),
|
||||||
]);
|
]);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/common",
|
"name": "@angular/common",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.0.0-PLACEHOLDER",
|
||||||
"description": "",
|
"description": "Angular - commonly needed directives and services",
|
||||||
"main": "bundles/common.umd.js",
|
"main": "bundles/common.umd.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
|
@ -11,65 +11,32 @@ import {CollectionChangeRecord, Directive, DoCheck, ElementRef, Input, IterableD
|
|||||||
import {isListLikeIterable} from '../facade/collection';
|
import {isListLikeIterable} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `NgClass` directive conditionally adds and removes CSS classes on an HTML element based on
|
* @ngModule CommonModule
|
||||||
* an expression's evaluation result.
|
|
||||||
*
|
*
|
||||||
* The result of an expression evaluation is interpreted differently depending on type of
|
* @whatItDoes Adds and removes CSS classes on an HTML element.
|
||||||
* the expression evaluation result:
|
|
||||||
* - `string` - all the CSS classes listed in a string (space delimited) are added
|
|
||||||
* - `Array` - all the CSS classes (Array elements) are added
|
|
||||||
* - `Object` - each key corresponds to a CSS class name while values are interpreted as expressions
|
|
||||||
* evaluating to `Boolean`. If a given expression evaluates to `true` a corresponding CSS class
|
|
||||||
* is added - otherwise it is removed.
|
|
||||||
*
|
|
||||||
* While the `NgClass` directive can interpret expressions evaluating to `string`, `Array`
|
|
||||||
* or `Object`, the `Object`-based version is the most often used and has an advantage of keeping
|
|
||||||
* all the CSS class names in a template.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/a4YdtmWywhJ33uqfpPPn?p=preview)):
|
|
||||||
*
|
*
|
||||||
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* import {Component} from '@angular/core';
|
* <some-element [ngClass]="'first second'">...</some-element>
|
||||||
* import {NgClass} from '@angular/common';
|
|
||||||
*
|
*
|
||||||
* @Component({
|
* <some-element [ngClass]="['first', 'second']">...</some-element>
|
||||||
* selector: 'toggle-button',
|
|
||||||
* inputs: ['isDisabled'],
|
|
||||||
* template: `
|
|
||||||
* <div class="button" [ngClass]="{active: isOn, disabled: isDisabled}"
|
|
||||||
* (click)="toggle(!isOn)">
|
|
||||||
* Click me!
|
|
||||||
* </div>`,
|
|
||||||
* styles: [`
|
|
||||||
* .button {
|
|
||||||
* width: 120px;
|
|
||||||
* border: medium solid black;
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* .active {
|
* <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
|
||||||
* background-color: red;
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* .disabled {
|
* <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
|
||||||
* color: gray;
|
|
||||||
* border: medium solid gray;
|
|
||||||
* }
|
|
||||||
* `],
|
|
||||||
* directives: [NgClass]
|
|
||||||
* })
|
|
||||||
* class ToggleButton {
|
|
||||||
* isOn = false;
|
|
||||||
* isDisabled = false;
|
|
||||||
*
|
|
||||||
* toggle(newState) {
|
|
||||||
* if (!this.isDisabled) {
|
|
||||||
* this.isOn = newState;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* The CSS classes are updated as follow depending on the type of the expression evaluation:
|
||||||
|
* - `string` - the CSS classes listed in a string (space delimited) are added,
|
||||||
|
* - `Array` - the CSS classes (Array elements) are added,
|
||||||
|
* - `Object` - keys are CSS class names that get added when the expression given in the value
|
||||||
|
* evaluates to a truthy value, otherwise class are removed.
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngClass]'})
|
@Directive({selector: '[ngClass]'})
|
||||||
|
@ -14,48 +14,35 @@ import {SwitchView} from './ng_switch';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `ngPlural` is an i18n directive that displays DOM sub-trees that match the switch expression
|
* @ngModule CommonModule
|
||||||
* value, or failing that, DOM sub-trees that match the switch expression's pluralization category.
|
*
|
||||||
|
* @whatItDoes Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <some-element [ngPlural]="value">
|
||||||
|
* <ng-container *ngPluralCase="'=0'">there is nothing</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'=1'">there is one</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'few'">there are a few</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'other'">there are exactly #</ng-container>
|
||||||
|
* </some-element>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees
|
||||||
|
* that match the switch expression's pluralization category.
|
||||||
*
|
*
|
||||||
* To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
* To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
||||||
* to a
|
* to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their
|
||||||
* switch expression.
|
* expression:
|
||||||
* - Inner elements defined with an `[ngPluralCase]` attribute will display based on their
|
* - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
||||||
* expression.
|
* matches the switch expression exactly,
|
||||||
* - If `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
* - otherwise, the view will be treated as a "category match", and will only display if exact
|
||||||
* matches the switch expression exactly.
|
* value matches aren't found and the value maps to its category for the defined locale.
|
||||||
* - Otherwise, the view will be treated as a "category match", and will only display if exact
|
|
||||||
* value matches aren't found and the value maps to its category for the defined locale.
|
|
||||||
*
|
*
|
||||||
* ```typescript
|
* See http://cldr.unicode.org/index/cldr-spec/plural-rules
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* // best practice is to define the locale at the application level
|
|
||||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
|
||||||
* })
|
|
||||||
* @View({
|
|
||||||
* template: `
|
|
||||||
* <p>Value = {{value}}</p>
|
|
||||||
* <button (click)="inc()">Increment</button>
|
|
||||||
*
|
*
|
||||||
* <div [ngPlural]="value">
|
|
||||||
* <template ngPluralCase="=0">there is nothing</template>
|
|
||||||
* <template ngPluralCase="=1">there is one</template>
|
|
||||||
* <template ngPluralCase="few">there are a few</template>
|
|
||||||
* <template ngPluralCase="other">there is some number</template>
|
|
||||||
* </div>
|
|
||||||
* `,
|
|
||||||
* directives: [NgPlural, NgPluralCase]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* value = 'init';
|
|
||||||
*
|
|
||||||
* inc() {
|
|
||||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngPlural]'})
|
@Directive({selector: '[ngPlural]'})
|
||||||
@ -74,8 +61,7 @@ export class NgPlural {
|
|||||||
|
|
||||||
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
|
||||||
|
|
||||||
/** @internal */
|
private _updateView(): void {
|
||||||
_updateView(): void {
|
|
||||||
this._clearViews();
|
this._clearViews();
|
||||||
|
|
||||||
const cases = Object.keys(this._caseViews);
|
const cases = Object.keys(this._caseViews);
|
||||||
@ -83,13 +69,11 @@ export class NgPlural {
|
|||||||
this._activateView(this._caseViews[key]);
|
this._activateView(this._caseViews[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _clearViews() {
|
||||||
_clearViews() {
|
|
||||||
if (this._activeView) this._activeView.destroy();
|
if (this._activeView) this._activeView.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _activateView(view: SwitchView) {
|
||||||
_activateView(view: SwitchView) {
|
|
||||||
if (view) {
|
if (view) {
|
||||||
this._activeView = view;
|
this._activeView = view;
|
||||||
this._activeView.create();
|
this._activeView.create();
|
||||||
@ -98,6 +82,21 @@ export class NgPlural {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
*
|
||||||
|
* @whatItDoes Creates a view that will be added/removed from the parent {@link NgPlural} when the
|
||||||
|
* given expression matches the plural expression according to CLDR rules.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <some-element [ngPlural]="value">
|
||||||
|
* <ng-container *ngPluralCase="'=0'">...</ng-container>
|
||||||
|
* <ng-container *ngPluralCase="'other'">...</ng-container>
|
||||||
|
* </some-element>
|
||||||
|
*```
|
||||||
|
*
|
||||||
|
* See {@link NgPlural} for more details and example.
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngPluralCase]'})
|
@Directive({selector: '[ngPluralCase]'})
|
||||||
|
@ -9,65 +9,31 @@
|
|||||||
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDiffer, KeyValueDiffers, Renderer} from '@angular/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The `NgStyle` directive changes styles based on a result of expression evaluation.
|
* @ngModule CommonModule
|
||||||
*
|
*
|
||||||
* An expression assigned to the `ngStyle` property must evaluate to an object and the
|
* @whatItDoes Update an HTML element styles.
|
||||||
* corresponding element styles are updated based on changes to this object. Style names to update
|
|
||||||
* are taken from the object's keys, and values - from the corresponding object's values.
|
|
||||||
*
|
|
||||||
* ### Syntax
|
|
||||||
*
|
|
||||||
* - `<div [ngStyle]="{'font-style': styleExp}"></div>`
|
|
||||||
* - `<div [ngStyle]="{'max-width.px': widthExp}"></div>`
|
|
||||||
* - `<div [ngStyle]="styleExp"></div>` - here the `styleExp` must evaluate to an object
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/YamGS6GkUh9GqWNQhCyM?p=preview)):
|
|
||||||
*
|
*
|
||||||
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* import {Component} from '@angular/core';
|
* <some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
|
||||||
* import {NgStyle} from '@angular/common';
|
|
||||||
*
|
*
|
||||||
* @Component({
|
* <some-element [ngStyle]="{'max-width.px': widthExp}">...</some-element>
|
||||||
* selector: 'ngStyle-example',
|
|
||||||
* template: `
|
|
||||||
* <h1 [ngStyle]="{'font-style': style, 'font-size': size, 'font-weight': weight}">
|
|
||||||
* Change style of this text!
|
|
||||||
* </h1>
|
|
||||||
*
|
*
|
||||||
* <hr>
|
* <some-element [ngStyle]="objExp">...</some-element>
|
||||||
*
|
|
||||||
* <label>Italic: <input type="checkbox" (change)="changeStyle($event)"></label>
|
|
||||||
* <label>Bold: <input type="checkbox" (change)="changeWeight($event)"></label>
|
|
||||||
* <label>Size: <input type="text" [value]="size" (change)="size = $event.target.value"></label>
|
|
||||||
* `,
|
|
||||||
* directives: [NgStyle]
|
|
||||||
* })
|
|
||||||
* export class NgStyleExample {
|
|
||||||
* style = 'normal';
|
|
||||||
* weight = 'normal';
|
|
||||||
* size = '20px';
|
|
||||||
*
|
|
||||||
* changeStyle($event: any) {
|
|
||||||
* this.style = $event.target.checked ? 'italic' : 'normal';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* changeWeight($event: any) {
|
|
||||||
* this.weight = $event.target.checked ? 'bold' : 'normal';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* In this example the `font-style`, `font-size` and `font-weight` styles will be updated
|
* @description
|
||||||
* based on the `style` property's value changes.
|
*
|
||||||
|
* The styles are updated according to the value of the expression evaluation:
|
||||||
|
* - keys are style names with an option `.<unit>` suffix (ie 'top.px', 'font-style.em'),
|
||||||
|
* - values are the values assigned to those properties (expressed in the given unit).
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngStyle]'})
|
@Directive({selector: '[ngStyle]'})
|
||||||
export class NgStyle implements DoCheck {
|
export class NgStyle implements DoCheck {
|
||||||
/** @internal */
|
private _ngStyle: {[key: string]: string};
|
||||||
_ngStyle: {[key: string]: string};
|
private _differ: KeyValueDiffer;
|
||||||
/** @internal */
|
|
||||||
_differ: KeyValueDiffer;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}
|
||||||
|
@ -22,58 +22,44 @@ export class SwitchView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds or removes DOM sub-trees when their match expressions match the switch expression.
|
* @ngModule CommonModule
|
||||||
*
|
*
|
||||||
* Elements within `NgSwitch` but without `NgSwitchCase` or `NgSwitchDefault` directives will be
|
* @whatItDoes Adds / removes DOM sub-trees when the nest match expressions matches the switch
|
||||||
* preserved at the location as specified in the template.
|
* expression.
|
||||||
*
|
*
|
||||||
* `NgSwitch` simply inserts nested elements based on which match expression matches the value
|
* @howToUse
|
||||||
* obtained from the evaluated switch expression. In other words, you define a container element
|
|
||||||
* (where you place the directive with a switch expression on the
|
|
||||||
* `[ngSwitch]="..."` attribute), define any inner elements inside of the directive and
|
|
||||||
* place a `[ngSwitchCase]` attribute per element.
|
|
||||||
*
|
|
||||||
* The `ngSwitchCase` property is used to inform `NgSwitch` which element to display when the
|
|
||||||
* expression is evaluated. If a matching expression is not found via a `ngSwitchCase` property
|
|
||||||
* then an element with the `ngSwitchDefault` attribute is displayed.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/DQMTII95CbuqWrl3lYAs?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <p>Value = {{value}}</p>
|
|
||||||
* <button (click)="inc()">Increment</button>
|
|
||||||
*
|
|
||||||
* <div [ngSwitch]="value">
|
|
||||||
* <p *ngSwitchCase="'init'">increment to start</p>
|
|
||||||
* <p *ngSwitchCase="0">0, increment again</p>
|
|
||||||
* <p *ngSwitchCase="1">1, increment again</p>
|
|
||||||
* <p *ngSwitchCase="2">2, stop incrementing</p>
|
|
||||||
* <p *ngSwitchDefault>> 2, STOP!</p>
|
|
||||||
* </div>
|
|
||||||
*
|
|
||||||
* <!-- alternate syntax -->
|
|
||||||
*
|
|
||||||
* <p [ngSwitch]="value">
|
|
||||||
* <template ngSwitchCase="init">increment to start</template>
|
|
||||||
* <template [ngSwitchCase]="0">0, increment again</template>
|
|
||||||
* <template [ngSwitchCase]="1">1, increment again</template>
|
|
||||||
* <template [ngSwitchCase]="2">2, stop incrementing</template>
|
|
||||||
* <template ngSwitchDefault>> 2, STOP!</template>
|
|
||||||
* </p>
|
|
||||||
* `,
|
|
||||||
* directives: [NgSwitch, NgSwitchCase, NgSwitchDefault]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* value = 'init';
|
|
||||||
*
|
|
||||||
* inc() {
|
|
||||||
* this.value = this.value === 'init' ? 0 : this.value + 1;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
* ```
|
||||||
|
* <container-element [ngSwitch]="switch_expression">
|
||||||
|
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||||
|
* <some-element *ngSwitchCase="match_expression_2">...</some-element>
|
||||||
|
* <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
|
||||||
|
* <ng-container *ngSwitchCase="match_expression_3">
|
||||||
|
* <!-- use a ng-container to group multiple root nodes -->
|
||||||
|
* <inner-element></inner-element>
|
||||||
|
* <inner-other-element></inner-other-element>
|
||||||
|
* </ng-container>
|
||||||
|
* <some-element *ngSwitchDefault>...</p>
|
||||||
|
* </container-element>
|
||||||
|
* ```
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* `NgSwitch` stamps out nested views when their match expression value matches the value of the
|
||||||
|
* switch expression.
|
||||||
|
*
|
||||||
|
* In other words:
|
||||||
|
* - you define a container element (where you place the directive with a switch expression on the
|
||||||
|
* `[ngSwitch]="..."` attribute)
|
||||||
|
* - you define inner views inside the `NgSwitch` and place a `*ngSwitchCase` attribute on the view
|
||||||
|
* root elements.
|
||||||
|
*
|
||||||
|
* Elements within `NgSwitch` but outside of a `NgSwitchCase` or `NgSwitchDefault` directives will
|
||||||
|
* be
|
||||||
|
* preserved at the location.
|
||||||
|
*
|
||||||
|
* The `ngSwitchCase` directive informs the parent `NgSwitch` of which view to display when the
|
||||||
|
* expression is evaluated.
|
||||||
|
* When no matching expression is found on a `ngSwitchCase` view, the `ngSwitchDefault` view is
|
||||||
|
* stamped out.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -125,8 +111,7 @@ export class NgSwitch {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _emptyAllActiveViews(): void {
|
||||||
_emptyAllActiveViews(): void {
|
|
||||||
const activeContainers = this._activeViews;
|
const activeContainers = this._activeViews;
|
||||||
for (var i = 0; i < activeContainers.length; i++) {
|
for (var i = 0; i < activeContainers.length; i++) {
|
||||||
activeContainers[i].destroy();
|
activeContainers[i].destroy();
|
||||||
@ -134,9 +119,7 @@ export class NgSwitch {
|
|||||||
this._activeViews = [];
|
this._activeViews = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _activateViews(views: SwitchView[]): void {
|
||||||
_activateViews(views: SwitchView[]): void {
|
|
||||||
// TODO(vicb): assert(this._activeViews.length === 0);
|
|
||||||
if (views) {
|
if (views) {
|
||||||
for (var i = 0; i < views.length; i++) {
|
for (var i = 0; i < views.length; i++) {
|
||||||
views[i].create();
|
views[i].create();
|
||||||
@ -155,8 +138,7 @@ export class NgSwitch {
|
|||||||
views.push(view);
|
views.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _deregisterView(value: any, view: SwitchView): void {
|
||||||
_deregisterView(value: any, view: SwitchView): void {
|
|
||||||
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
// `_CASE_DEFAULT` is used a marker for non-registered cases
|
||||||
if (value === _CASE_DEFAULT) return;
|
if (value === _CASE_DEFAULT) return;
|
||||||
const views = this._valueViews.get(value);
|
const views = this._valueViews.get(value);
|
||||||
@ -169,10 +151,24 @@ export class NgSwitch {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Insert the sub-tree when the `ngSwitchCase` expression evaluates to the same value as the
|
* @ngModule CommonModule
|
||||||
* enclosing switch expression.
|
|
||||||
*
|
*
|
||||||
* If multiple match expression match the switch expression value, all of them are displayed.
|
* @whatItDoes Creates a view that will be added/removed from the parent {@link NgSwitch} when the
|
||||||
|
* given expression evaluate to respectively the same/different value as the switch
|
||||||
|
* expression.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <container-element [ngSwitch]="switch_expression">
|
||||||
|
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||||
|
* </container-element>
|
||||||
|
*```
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Insert the sub-tree when the expression evaluates to the same value as the enclosing switch
|
||||||
|
* expression.
|
||||||
|
*
|
||||||
|
* If multiple match expressions match the switch expression value, all of them are displayed.
|
||||||
*
|
*
|
||||||
* See {@link NgSwitch} for more details and example.
|
* See {@link NgSwitch} for more details and example.
|
||||||
*
|
*
|
||||||
@ -181,10 +177,8 @@ export class NgSwitch {
|
|||||||
@Directive({selector: '[ngSwitchCase]'})
|
@Directive({selector: '[ngSwitchCase]'})
|
||||||
export class NgSwitchCase {
|
export class NgSwitchCase {
|
||||||
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
|
||||||
/** @internal */
|
private _value: any = _CASE_DEFAULT;
|
||||||
_value: any = _CASE_DEFAULT;
|
private _view: SwitchView;
|
||||||
/** @internal */
|
|
||||||
_view: SwitchView;
|
|
||||||
private _switch: NgSwitch;
|
private _switch: NgSwitch;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ -202,8 +196,23 @@ export class NgSwitchCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default case statements are displayed when no match expression matches the switch expression
|
* @ngModule CommonModule
|
||||||
* value.
|
* @whatItDoes Creates a view that is added to the parent {@link NgSwitch} when no case expressions
|
||||||
|
* match the
|
||||||
|
* switch expression.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* <container-element [ngSwitch]="switch_expression">
|
||||||
|
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
||||||
|
* <some-other-element *ngSwitchDefault>...</some-other-element>
|
||||||
|
* </container-element>
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Insert the sub-tree when no case expressions evaluate to the same value as the enclosing switch
|
||||||
|
* expression.
|
||||||
*
|
*
|
||||||
* See {@link NgSwitch} for more details and example.
|
* See {@link NgSwitch} for more details and example.
|
||||||
*
|
*
|
||||||
|
@ -6,24 +6,28 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Directive, EmbeddedViewRef, Input, OnChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
import {Directive, EmbeddedViewRef, Input, OnChanges, SimpleChanges, TemplateRef, ViewContainerRef} from '@angular/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates and inserts an embedded view based on a prepared `TemplateRef`.
|
* @ngModule CommonModule
|
||||||
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
|
||||||
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
|
||||||
* available within the `TemplateRef`.
|
|
||||||
*
|
*
|
||||||
* Note: using the key `$implicit` in the context object will set it's value as default.
|
* @whatItDoes Inserts an embedded view from a prepared `TemplateRef`
|
||||||
*
|
|
||||||
* ### Syntax
|
|
||||||
*
|
*
|
||||||
|
* @howToUse
|
||||||
* ```
|
* ```
|
||||||
* <template [ngTemplateOutlet]="templateRefExpression"
|
* <template [ngTemplateOutlet]="templateRefExpression"
|
||||||
* [ngOutletContext]="objectExpression">
|
* [ngOutletContext]="objectExpression">
|
||||||
* </template>
|
* </template>
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* You can attach a context object to the `EmbeddedViewRef` by setting `[ngOutletContext]`.
|
||||||
|
* `[ngOutletContext]` should be an object, the object's keys will be the local template variables
|
||||||
|
* available within the `TemplateRef`.
|
||||||
|
*
|
||||||
|
* Note: using the key `$implicit` in the context object will set it's value as default.
|
||||||
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@Directive({selector: '[ngTemplateOutlet]'})
|
@Directive({selector: '[ngTemplateOutlet]'})
|
||||||
@ -40,7 +44,7 @@ export class NgTemplateOutlet implements OnChanges {
|
|||||||
@Input()
|
@Input()
|
||||||
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges(changes: SimpleChanges) {
|
||||||
if (this._viewRef) {
|
if (this._viewRef) {
|
||||||
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
|
||||||
}
|
}
|
||||||
|
@ -67,7 +67,7 @@ export enum Plural {
|
|||||||
Two,
|
Two,
|
||||||
Few,
|
Few,
|
||||||
Many,
|
Many,
|
||||||
Other
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,16 +49,20 @@ export class Location {
|
|||||||
_subject: EventEmitter<any> = new EventEmitter();
|
_subject: EventEmitter<any> = new EventEmitter();
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_baseHref: string;
|
_baseHref: string;
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_platformStrategy: LocationStrategy;
|
_platformStrategy: LocationStrategy;
|
||||||
|
|
||||||
constructor(platformStrategy: LocationStrategy) {
|
constructor(platformStrategy: LocationStrategy) {
|
||||||
this._platformStrategy = platformStrategy;
|
this._platformStrategy = platformStrategy;
|
||||||
var browserBaseHref = this._platformStrategy.getBaseHref();
|
const browserBaseHref = this._platformStrategy.getBaseHref();
|
||||||
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
||||||
this._platformStrategy.onPopState(
|
this._platformStrategy.onPopState((ev) => {
|
||||||
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
|
this._subject.emit({
|
||||||
|
'url': this.path(true),
|
||||||
|
'pop': true,
|
||||||
|
'type': ev.type,
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
|
||||||
import {EventEmitter, Observable} from '../facade/async';
|
import {EventEmitter, Observable} from '../facade/async';
|
||||||
import {isBlank, isPresent, isPromise} from '../facade/lang';
|
import {isPromise} from '../private_import_core';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
interface SubscriptionStrategy {
|
interface SubscriptionStrategy {
|
||||||
@ -37,63 +37,54 @@ class PromiseStrategy implements SubscriptionStrategy {
|
|||||||
onDestroy(subscription: any): void {}
|
onDestroy(subscription: any): void {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _promiseStrategy = new PromiseStrategy();
|
const _promiseStrategy = new PromiseStrategy();
|
||||||
var _observableStrategy = new ObservableStrategy();
|
const _observableStrategy = new ObservableStrategy();
|
||||||
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Unwraps a value from an asynchronous primitive.
|
||||||
|
* @howToUse `observable_or_promise_expression | async`
|
||||||
|
* @description
|
||||||
* The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has
|
* The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has
|
||||||
* emitted.
|
* emitted. When a new value is emitted, the `async` pipe marks the component to be checked for
|
||||||
* When a new value is emitted, the `async` pipe marks the component to be checked for changes.
|
* changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
||||||
* When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
|
||||||
* potential memory leaks.
|
* potential memory leaks.
|
||||||
*
|
*
|
||||||
* ## Usage
|
|
||||||
*
|
|
||||||
* object | async
|
|
||||||
*
|
|
||||||
* where `object` is of type `Observable` or of type `Promise`.
|
|
||||||
*
|
*
|
||||||
* ## Examples
|
* ## Examples
|
||||||
*
|
*
|
||||||
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
|
* This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
|
||||||
* promise.
|
* promise.
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipePromise'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
||||||
*
|
*
|
||||||
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
* It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
||||||
* to the view. Every 500ms, the `time` Observable updates the view with the current time.
|
* to the view. The Observable continuesly updates the view with the current time.
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/async_pipe/async_pipe_example.ts region='AsyncPipeObservable'}
|
* {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Pipe({name: 'async', pure: false})
|
@Pipe({name: 'async', pure: false})
|
||||||
export class AsyncPipe implements OnDestroy {
|
export class AsyncPipe implements OnDestroy {
|
||||||
/** @internal */
|
private _latestValue: Object = null;
|
||||||
_latestValue: Object = null;
|
private _latestReturnedValue: Object = null;
|
||||||
/** @internal */
|
|
||||||
_latestReturnedValue: Object = null;
|
|
||||||
|
|
||||||
/** @internal */
|
private _subscription: Object = null;
|
||||||
_subscription: Object = null;
|
private _obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
|
||||||
/** @internal */
|
|
||||||
_obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
|
|
||||||
/** @internal */
|
|
||||||
_ref: ChangeDetectorRef;
|
|
||||||
private _strategy: SubscriptionStrategy = null;
|
private _strategy: SubscriptionStrategy = null;
|
||||||
|
|
||||||
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
|
constructor(private _ref: ChangeDetectorRef) {}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (isPresent(this._subscription)) {
|
if (this._subscription) {
|
||||||
this._dispose();
|
this._dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||||
if (isBlank(this._obj)) {
|
if (!this._obj) {
|
||||||
if (isPresent(obj)) {
|
if (obj) {
|
||||||
this._subscribe(obj);
|
this._subscribe(obj);
|
||||||
}
|
}
|
||||||
this._latestReturnedValue = this._latestValue;
|
this._latestReturnedValue = this._latestValue;
|
||||||
@ -107,33 +98,32 @@ export class AsyncPipe implements OnDestroy {
|
|||||||
|
|
||||||
if (this._latestValue === this._latestReturnedValue) {
|
if (this._latestValue === this._latestReturnedValue) {
|
||||||
return this._latestReturnedValue;
|
return this._latestReturnedValue;
|
||||||
} else {
|
|
||||||
this._latestReturnedValue = this._latestValue;
|
|
||||||
return WrappedValue.wrap(this._latestValue);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._latestReturnedValue = this._latestValue;
|
||||||
|
return WrappedValue.wrap(this._latestValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
|
||||||
_subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
|
|
||||||
this._obj = obj;
|
this._obj = obj;
|
||||||
this._strategy = this._selectStrategy(obj);
|
this._strategy = this._selectStrategy(obj);
|
||||||
this._subscription = this._strategy.createSubscription(
|
this._subscription = this._strategy.createSubscription(
|
||||||
obj, (value: Object) => this._updateLatestValue(obj, value));
|
obj, (value: Object) => this._updateLatestValue(obj, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
||||||
_selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
|
|
||||||
if (isPromise(obj)) {
|
if (isPromise(obj)) {
|
||||||
return _promiseStrategy;
|
return _promiseStrategy;
|
||||||
} else if ((<any>obj).subscribe) {
|
|
||||||
return _observableStrategy;
|
|
||||||
} else {
|
|
||||||
throw new InvalidPipeArgumentError(AsyncPipe, obj);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((<any>obj).subscribe) {
|
||||||
|
return _observableStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidPipeArgumentError(AsyncPipe, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _dispose(): void {
|
||||||
_dispose(): void {
|
|
||||||
this._strategy.dispose(this._subscription);
|
this._strategy.dispose(this._subscription);
|
||||||
this._latestValue = null;
|
this._latestValue = null;
|
||||||
this._latestReturnedValue = null;
|
this._latestReturnedValue = null;
|
||||||
@ -141,8 +131,7 @@ export class AsyncPipe implements OnDestroy {
|
|||||||
this._obj = null;
|
this._obj = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
private _updateLatestValue(async: any, value: Object) {
|
||||||
_updateLatestValue(async: any, value: Object) {
|
|
||||||
if (async === this._obj) {
|
if (async === this._obj) {
|
||||||
this._latestValue = value;
|
this._latestValue = value;
|
||||||
this._ref.markForCheck();
|
this._ref.markForCheck();
|
||||||
|
@ -7,31 +7,31 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
|
||||||
|
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
|
||||||
import {DateFormatter} from '../facade/intl';
|
import {DateFormatter} from '../facade/intl';
|
||||||
import {DateWrapper, NumberWrapper, isBlank, isDate, isString} from '../facade/lang';
|
import {NumberWrapper, isBlank, isDate} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Formats a date value to a string based on the requested format.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Formats a date according to locale rules.
|
||||||
|
* @howToUse `date_expression | date[:format]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* WARNINGS:
|
* Where:
|
||||||
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||||
* Instead users should treat the date as an immutable object and change the reference when the
|
* (https://www.w3.org/TR/NOTE-datetime).
|
||||||
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
* - `format` indicates which date/time components to include. The format can be predifined as
|
||||||
* which would be an expensive operation).
|
* shown below or custom as shown in the table.
|
||||||
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||||
* browsers.
|
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||||
|
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. `Friday, September 3, 2010` for `en-US`)
|
||||||
|
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. `September 3, 2010` for `en-US`)
|
||||||
|
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. `Sep 3, 2010` for `en-US`)
|
||||||
|
* - `'shortDate'`: equivalent to `'yMd'` (e.g. `9/3/2010` for `en-US`)
|
||||||
|
* - `'mediumTime'`: equivalent to `'jms'` (e.g. `12:05:08 PM` for `en-US`)
|
||||||
|
* - `'shortTime'`: equivalent to `'jm'` (e.g. `12:05 PM` for `en-US`)
|
||||||
*
|
*
|
||||||
* ## Usage
|
|
||||||
*
|
|
||||||
* expression | date[:format]
|
|
||||||
*
|
|
||||||
* where `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
|
||||||
* (https://www.w3.org/TR/NOTE-datetime) and `format` indicates which date/time components to
|
|
||||||
* include:
|
|
||||||
*
|
*
|
||||||
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
* | Component | Symbol | Short Form | Long Form | Numeric | 2-digit |
|
||||||
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
* |-----------|:------:|--------------|-------------------|-----------|-----------|
|
||||||
@ -52,19 +52,16 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* In javascript, only the components specified will be respected (not the ordering,
|
* In javascript, only the components specified will be respected (not the ordering,
|
||||||
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
* punctuations, ...) and details of the formatting will be dependent on the locale.
|
||||||
*
|
*
|
||||||
* `format` can also be one of the following predefined formats:
|
|
||||||
*
|
|
||||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. Sep 3, 2010, 12:05:08 PM for en-US)
|
|
||||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. 9/3/2010, 12:05 PM for en-US)
|
|
||||||
* - `'fullDate'`: equivalent to `'yMMMMEEEEd'` (e.g. Friday, September 3, 2010 for en-US)
|
|
||||||
* - `'longDate'`: equivalent to `'yMMMMd'` (e.g. September 3, 2010 for en-US)
|
|
||||||
* - `'mediumDate'`: equivalent to `'yMMMd'` (e.g. Sep 3, 2010 for en-US)
|
|
||||||
* - `'shortDate'`: equivalent to `'yMd'` (e.g. 9/3/2010 for en-US)
|
|
||||||
* - `'mediumTime'`: equivalent to `'jms'` (e.g. 12:05:08 PM for en-US)
|
|
||||||
* - `'shortTime'`: equivalent to `'jm'` (e.g. 12:05 PM for en-US)
|
|
||||||
*
|
|
||||||
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
* Timezone of the formatted text will be the local system timezone of the end-user's machine.
|
||||||
*
|
*
|
||||||
|
* WARNINGS:
|
||||||
|
* - this pipe is marked as pure hence it will not be re-evaluated when the input is mutated.
|
||||||
|
* Instead users should treat the date as an immutable object and change the reference when the
|
||||||
|
* pipe needs to re-run (this is to avoid reformatting the date on every change detection run
|
||||||
|
* which would be an expensive operation).
|
||||||
|
* - this pipe uses the Internationalization API. Therefore it is only reliable in Chrome and Opera
|
||||||
|
* browsers.
|
||||||
|
*
|
||||||
* ### Examples
|
* ### Examples
|
||||||
*
|
*
|
||||||
* Assuming `dateObj` is (year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11)
|
* Assuming `dateObj` is (year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11)
|
||||||
@ -77,14 +74,14 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
* {{ dateObj | date:'mmss' }} // output is '43:11'
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/date_pipe/date_pipe_example.ts region='DatePipe'}
|
* {@example common/pipes/ts/date_pipe.ts region='DatePipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@Pipe({name: 'date', pure: true})
|
@Pipe({name: 'date', pure: true})
|
||||||
export class DatePipe implements PipeTransform {
|
export class DatePipe implements PipeTransform {
|
||||||
/** @internal */
|
/** @internal */
|
||||||
static _ALIASES: {[key: string]: String} = {
|
static _ALIASES: {[key: string]: string} = {
|
||||||
'medium': 'yMMMdjms',
|
'medium': 'yMMMdjms',
|
||||||
'short': 'yMdjm',
|
'short': 'yMdjm',
|
||||||
'fullDate': 'yMMMMEEEEd',
|
'fullDate': 'yMMMMEEEEd',
|
||||||
@ -105,23 +102,15 @@ export class DatePipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NumberWrapper.isNumeric(value)) {
|
if (NumberWrapper.isNumeric(value)) {
|
||||||
value = DateWrapper.fromMillis(parseFloat(value));
|
value = parseFloat(value);
|
||||||
} else if (isString(value)) {
|
|
||||||
value = DateWrapper.fromISOString(value);
|
|
||||||
}
|
}
|
||||||
if (StringMapWrapper.contains(DatePipe._ALIASES, pattern)) {
|
|
||||||
pattern = <string>StringMapWrapper.get(DatePipe._ALIASES, pattern);
|
return DateFormatter.format(
|
||||||
}
|
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
|
||||||
return DateFormatter.format(value, this._locale, pattern);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private supports(obj: any): boolean {
|
private supports(obj: any): boolean {
|
||||||
if (isDate(obj) || NumberWrapper.isNumeric(obj)) {
|
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
|
||||||
return true;
|
(typeof obj === 'string' && isDate(new Date(obj)));
|
||||||
}
|
|
||||||
if (isString(obj) && isDate(DateWrapper.fromISOString(obj))) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,46 +7,26 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {StringWrapper, isBlank, isStringMap} from '../facade/lang';
|
import {isBlank, isStringMap} from '../facade/lang';
|
||||||
import {NgLocalization, getPluralCategory} from '../localization';
|
import {NgLocalization, getPluralCategory} from '../localization';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
const _INTERPOLATION_REGEXP: RegExp = /#/g;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maps a value to a string that pluralizes the value properly.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Maps a value to a string that pluralizes the value according to locale rules.
|
||||||
|
* @howToUse `expression | i18nPlural:mapping`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* ## Usage
|
* Where:
|
||||||
*
|
* - `expression` is a number.
|
||||||
* expression | i18nPlural:mapping
|
* - `mapping` is an object that mimics the ICU format, see
|
||||||
*
|
* http://userguide.icu-project.org/formatparse/messages
|
||||||
* where `expression` is a number and `mapping` is an object that mimics the ICU format,
|
|
||||||
* see http://userguide.icu-project.org/formatparse/messages
|
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
* ```
|
* {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'}
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <div>
|
|
||||||
* {{ messages.length | i18nPlural: messageMapping }}
|
|
||||||
* </div>
|
|
||||||
* `,
|
|
||||||
* // best practice is to define the locale at the application level
|
|
||||||
* providers: [{provide: LOCALE_ID, useValue: 'en_US'}]
|
|
||||||
* })
|
|
||||||
*
|
|
||||||
* class MyApp {
|
|
||||||
* messages: any[];
|
|
||||||
* messageMapping: {[k:string]: string} = {
|
|
||||||
* '=0': 'No messages.',
|
|
||||||
* '=1': 'One message.',
|
|
||||||
* 'other': '# messages.'
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
@ -63,6 +43,6 @@ export class I18nPluralPipe implements PipeTransform {
|
|||||||
|
|
||||||
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
const key = getPluralCategory(value, Object.keys(pluralMap), this._localization);
|
||||||
|
|
||||||
return StringWrapper.replaceAll(pluralMap[key], _INTERPOLATION_REGEXP, value.toString());
|
return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,33 +11,18 @@ import {isBlank, isStringMap} from '../facade/lang';
|
|||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Generic selector that displays the string that matches the current value.
|
||||||
|
* @howToUse `expression | i18nSelect:mapping`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* Generic selector that displays the string that matches the current value.
|
* Where:
|
||||||
*
|
* - `mapping`: is an object that indicates the text that should be displayed
|
||||||
* ## Usage
|
|
||||||
*
|
|
||||||
* expression | i18nSelect:mapping
|
|
||||||
*
|
|
||||||
* where `mapping` is an object that indicates the text that should be displayed
|
|
||||||
* for different values of the provided `expression`.
|
* for different values of the provided `expression`.
|
||||||
*
|
*
|
||||||
* ## Example
|
* ## Example
|
||||||
*
|
*
|
||||||
* ```
|
* {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'}
|
||||||
* <div>
|
|
||||||
* {{ gender | i18nSelect: inviteMap }}
|
|
||||||
* </div>
|
|
||||||
*
|
|
||||||
* class MyApp {
|
|
||||||
* gender: string = 'male';
|
|
||||||
* inviteMap: any = {
|
|
||||||
* 'male': 'Invite him.',
|
|
||||||
* 'female': 'Invite her.',
|
|
||||||
* 'other': 'Invite them.'
|
|
||||||
* }
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
|
@ -13,10 +13,15 @@ import {Json} from '../facade/lang';
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms any input value using `JSON.stringify`. Useful for debugging.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Converts value into JSON string.
|
||||||
|
* @howToUse `expression | json`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into string using `JSON.stringify`. Useful for debugging.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* {@example core/pipes/ts/json_pipe/json_pipe_example.ts region='JsonPipe'}
|
* {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
@ -7,16 +7,21 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank, isString} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transforms text to lowercase.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Transforms string to lowercase.
|
||||||
|
* @howToUse `expression | lowercase`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into lowercase string using `String.prototype.toLowerCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -24,7 +29,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
export class LowerCasePipe implements PipeTransform {
|
export class LowerCasePipe implements PipeTransform {
|
||||||
transform(value: string): string {
|
transform(value: string): string {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
if (!isString(value)) {
|
if (typeof value !== 'string') {
|
||||||
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
throw new InvalidPipeArgumentError(LowerCasePipe, value);
|
||||||
}
|
}
|
||||||
return value.toLowerCase();
|
return value.toLowerCase();
|
||||||
|
@ -9,21 +9,23 @@
|
|||||||
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
|
||||||
|
|
||||||
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
import {NumberFormatStyle, NumberFormatter} from '../facade/intl';
|
||||||
import {NumberWrapper, isBlank, isNumber, isPresent, isString} from '../facade/lang';
|
import {NumberWrapper, isBlank, isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
|
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
||||||
|
|
||||||
function formatNumber(
|
function formatNumber(
|
||||||
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
|
||||||
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
|
||||||
if (isBlank(value)) return null;
|
if (isBlank(value)) return null;
|
||||||
|
|
||||||
// Convert strings to numbers
|
// Convert strings to numbers
|
||||||
value = isString(value) && NumberWrapper.isNumeric(value) ? +value : value;
|
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
|
||||||
if (!isNumber(value)) {
|
if (typeof value !== 'number') {
|
||||||
throw new InvalidPipeArgumentError(pipe, value);
|
throw new InvalidPipeArgumentError(pipe, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
let minInt: number;
|
let minInt: number;
|
||||||
let minFraction: number;
|
let minFraction: number;
|
||||||
let maxFraction: number;
|
let maxFraction: number;
|
||||||
@ -34,8 +36,8 @@ function formatNumber(
|
|||||||
maxFraction = 3;
|
maxFraction = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(digits)) {
|
if (digits) {
|
||||||
var parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
|
||||||
if (parts === null) {
|
if (parts === null) {
|
||||||
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
throw new Error(`${digits} is not a valid digit info for number pipes`);
|
||||||
}
|
}
|
||||||
@ -49,41 +51,40 @@ function formatNumber(
|
|||||||
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NumberFormatter.format(value as number, locale, style, {
|
return NumberFormatter.format(value as number, locale, style, {
|
||||||
minimumIntegerDigits: minInt,
|
minimumIntegerDigits: minInt,
|
||||||
minimumFractionDigits: minFraction,
|
minimumFractionDigits: minFraction,
|
||||||
maximumFractionDigits: maxFraction,
|
maximumFractionDigits: maxFraction,
|
||||||
currency: currency,
|
currency: currency,
|
||||||
currencyAsSymbol: currencyAsSymbol
|
currencyAsSymbol: currencyAsSymbol,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: this pipe uses the Internationalization API.
|
* @ngModule CommonModule
|
||||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use a
|
* @whatItDoes Formats a number according to locale rules.
|
||||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
* @howToUse `number_expression | number[:digitInfo]`
|
||||||
*
|
*
|
||||||
* Formats a number as local text. i.e. group sizing and separator and other locale-specific
|
* Formats a number as text. Group sizing and separator and other locale-specific
|
||||||
* configurations are based on the active locale.
|
* configurations are based on the active locale.
|
||||||
*
|
*
|
||||||
* ### Usage
|
* where `expression` is a number:
|
||||||
*
|
* - `digitInfo` is a `string` which has a following format: <br>
|
||||||
* expression | number[:digitInfo]
|
* <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>
|
||||||
*
|
* - `minIntegerDigits` is the minimum number of integer digits to use. Defaults to `1`.
|
||||||
* where `expression` is a number and `digitInfo` has the following format:
|
* - `minFractionDigits` is the minimum number of digits after fraction. Defaults to `0`.
|
||||||
*
|
* - `maxFractionDigits` is the maximum number of digits after fraction. Defaults to `3`.
|
||||||
* {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
|
|
||||||
*
|
|
||||||
* - minIntegerDigits is the minimum number of integer digits to use. Defaults to 1.
|
|
||||||
* - minFractionDigits is the minimum number of digits after fraction. Defaults to 0.
|
|
||||||
* - maxFractionDigits is the maximum number of digits after fraction. Defaults to 3.
|
|
||||||
*
|
*
|
||||||
* For more information on the acceptable range for each of these numbers and other
|
* For more information on the acceptable range for each of these numbers and other
|
||||||
* details see your native internationalization library.
|
* details see your native internationalization library.
|
||||||
*
|
*
|
||||||
|
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||||
|
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||||
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='NumberPipe'}
|
* {@example common/pipes/ts/number_pipe.ts region='NumberPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -97,21 +98,22 @@ export class DecimalPipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: this pipe uses the Internationalization API.
|
* @ngModule CommonModule
|
||||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use a
|
* @whatItDoes Formats a number as a percentage according to locale rules.
|
||||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
* @howToUse `number_expression | percent[:digitInfo]`
|
||||||
*
|
*
|
||||||
* Formats a number as local percent.
|
* @description
|
||||||
*
|
*
|
||||||
* ### Usage
|
* Formats a number as percentage.
|
||||||
*
|
*
|
||||||
* expression | percent[:digitInfo]
|
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||||
*
|
*
|
||||||
* For more information about `digitInfo` see {@link DecimalPipe}
|
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||||
|
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='PercentPipe'}
|
* {@example common/pipes/ts/number_pipe.ts region='PercentPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -125,26 +127,26 @@ export class PercentPipe implements PipeTransform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WARNING: this pipe uses the Internationalization API.
|
* @ngModule CommonModule
|
||||||
* Therefore it is only reliable in Chrome and Opera browsers. For other browsers please use a
|
* @whatItDoes Formats a number as currency using locale rules.
|
||||||
* polyfill, for example: [https://github.com/andyearnshaw/Intl.js/].
|
* @howToUse `number_expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
|
* Use `currency` to format a number as currency.
|
||||||
*
|
*
|
||||||
* Formats a number as local currency.
|
* - `currencyCode` is the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code, such
|
||||||
|
* as `USD` for the US dollar and `EUR` for the euro.
|
||||||
|
* - `symbolDisplay` is a boolean indicating whether to use the currency symbol or code.
|
||||||
|
* - `true`: use symbol (e.g. `$`).
|
||||||
|
* - `false`(default): use code (e.g. `USD`).
|
||||||
|
* - `digitInfo` See {@link DecimalPipe} for detailed description.
|
||||||
*
|
*
|
||||||
* ### Usage
|
* WARNING: this pipe uses the Internationalization API which is not yet available in all browsers
|
||||||
*
|
* and may require a polyfill. See {@linkDocs guide/browser-support} for details.
|
||||||
* expression | currency[:currencyCode[:symbolDisplay[:digitInfo]]]
|
|
||||||
*
|
|
||||||
* where `currencyCode` is the ISO 4217 currency code, such as "USD" for the US dollar and
|
|
||||||
* "EUR" for the euro. `symbolDisplay` is a boolean indicating whether to use the currency
|
|
||||||
* symbol (e.g. $) or the currency code (e.g. USD) in the output. The default for this value
|
|
||||||
* is `false`.
|
|
||||||
* For more information about `digitInfo` see {@link DecimalPipe}.
|
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/number_pipe/number_pipe_example.ts region='CurrencyPipe'}
|
* {@example common/pipes/ts/number_pipe.ts region='CurrencyPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
@ -7,53 +7,41 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {ListWrapper} from '../facade/collection';
|
import {isBlank} from '../facade/lang';
|
||||||
import {StringWrapper, isArray, isBlank, isString} from '../facade/lang';
|
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new List or String containing only a subset (slice) of the
|
* @ngModule CommonModule
|
||||||
* elements.
|
* @whatItDoes Creates a new List or String containing a subset (slice) of the elements.
|
||||||
|
* @howToUse `array_or_string_expression | slice:start[:end]`
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* The starting index of the subset to return is specified by the `start` parameter.
|
* Where the input expression is a `List` or `String`, and:
|
||||||
|
* - `start`: The starting index of the subset to return.
|
||||||
|
* - **a positive integer**: return the item at `start` index and all items after
|
||||||
|
* in the list or string expression.
|
||||||
|
* - **a negative integer**: return the item at `start` index from the end and all items after
|
||||||
|
* in the list or string expression.
|
||||||
|
* - **if positive and greater than the size of the expression**: return an empty list or string.
|
||||||
|
* - **if negative and greater than the size of the expression**: return entire list or string.
|
||||||
|
* - `end`: The ending index of the subset to return.
|
||||||
|
* - **omitted**: return all items until the end.
|
||||||
|
* - **if positive**: return all items before `end` index of the list or string.
|
||||||
|
* - **if negative**: return all items before `end` index from the end of the list or string.
|
||||||
*
|
*
|
||||||
* The ending index of the subset to return is specified by the optional `end` parameter.
|
* All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()`
|
||||||
*
|
* and `String.prototype.slice()`.
|
||||||
* ### Usage
|
|
||||||
*
|
|
||||||
* expression | slice:start[:end]
|
|
||||||
*
|
|
||||||
* All behavior is based on the expected behavior of the JavaScript API
|
|
||||||
* Array.prototype.slice() and String.prototype.slice()
|
|
||||||
*
|
|
||||||
* Where the input expression is a [List] or [String], and `start` is:
|
|
||||||
*
|
|
||||||
* - **a positive integer**: return the item at _start_ index and all items after
|
|
||||||
* in the list or string expression.
|
|
||||||
* - **a negative integer**: return the item at _start_ index from the end and all items after
|
|
||||||
* in the list or string expression.
|
|
||||||
* - **`|start|` greater than the size of the expression**: return an empty list or string.
|
|
||||||
* - **`|start|` negative greater than the size of the expression**: return entire list or
|
|
||||||
* string expression.
|
|
||||||
*
|
|
||||||
* and where `end` is:
|
|
||||||
*
|
|
||||||
* - **omitted**: return all items until the end of the input
|
|
||||||
* - **a positive integer**: return all items before _end_ index of the list or string
|
|
||||||
* expression.
|
|
||||||
* - **a negative integer**: return all items before _end_ index from the end of the list
|
|
||||||
* or string expression.
|
|
||||||
*
|
*
|
||||||
* When operating on a [List], the returned list is always a copy even when all
|
* When operating on a [List], the returned list is always a copy even when all
|
||||||
* the elements are being returned.
|
* the elements are being returned.
|
||||||
*
|
*
|
||||||
* When operating on a blank value, returns it.
|
* When operating on a blank value, the pipe returns the blank value.
|
||||||
*
|
*
|
||||||
* ## List Example
|
* ## List Example
|
||||||
*
|
*
|
||||||
* This `ngFor` example:
|
* This `ngFor` example:
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_list'}
|
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'}
|
||||||
*
|
*
|
||||||
* produces the following:
|
* produces the following:
|
||||||
*
|
*
|
||||||
@ -62,23 +50,22 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
*
|
*
|
||||||
* ## String Examples
|
* ## String Examples
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/slice_pipe/slice_pipe_example.ts region='SlicePipe_string'}
|
* {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Pipe({name: 'slice', pure: false})
|
@Pipe({name: 'slice', pure: false})
|
||||||
export class SlicePipe implements PipeTransform {
|
export class SlicePipe implements PipeTransform {
|
||||||
transform(value: any, start: number, end: number = null): any {
|
transform(value: any, start: number, end?: number): any {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
|
|
||||||
if (!this.supports(value)) {
|
if (!this.supports(value)) {
|
||||||
throw new InvalidPipeArgumentError(SlicePipe, value);
|
throw new InvalidPipeArgumentError(SlicePipe, value);
|
||||||
}
|
}
|
||||||
if (isString(value)) {
|
|
||||||
return StringWrapper.slice(value, start, end);
|
return value.slice(start, end);
|
||||||
}
|
|
||||||
return ListWrapper.slice(value, start, end);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private supports(obj: any): boolean { return isString(obj) || isArray(obj); }
|
private supports(obj: any): boolean { return typeof obj === 'string' || Array.isArray(obj); }
|
||||||
}
|
}
|
||||||
|
@ -7,15 +7,20 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Pipe, PipeTransform} from '@angular/core';
|
import {Pipe, PipeTransform} from '@angular/core';
|
||||||
import {isBlank, isString} from '../facade/lang';
|
import {isBlank} from '../facade/lang';
|
||||||
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements uppercase transforms to text.
|
* @ngModule CommonModule
|
||||||
|
* @whatItDoes Transforms string to uppercase.
|
||||||
|
* @howToUse `expression | uppercase`
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* Converts value into lowercase string using `String.prototype.toUpperCase()`.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/pipes/ts/lowerupper_pipe/lowerupper_pipe_example.ts region='LowerUpperPipe'}
|
* {@example common/pipes/ts/lowerupper_pipe.ts region='LowerUpperPipe'}
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -23,7 +28,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
|||||||
export class UpperCasePipe implements PipeTransform {
|
export class UpperCasePipe implements PipeTransform {
|
||||||
transform(value: string): string {
|
transform(value: string): string {
|
||||||
if (isBlank(value)) return value;
|
if (isBlank(value)) return value;
|
||||||
if (!isString(value)) {
|
if (typeof value !== 'string') {
|
||||||
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
throw new InvalidPipeArgumentError(UpperCasePipe, value);
|
||||||
}
|
}
|
||||||
return value.toUpperCase();
|
return value.toUpperCase();
|
||||||
|
11
modules/@angular/common/src/private_import_core.ts
Normal file
11
modules/@angular/common/src/private_import_core.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {__core_private__ as r} from '@angular/core';
|
||||||
|
|
||||||
|
export const isPromise: typeof r.isPromise = r.isPromise;
|
@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class SpyLocation implements Location {
|
export class SpyLocation implements Location {
|
||||||
urlChanges: string[] = [];
|
urlChanges: string[] = [];
|
||||||
/** @internal */
|
|
||||||
private _history: LocationState[] = [new LocationState('', '')];
|
private _history: LocationState[] = [new LocationState('', '')];
|
||||||
/** @internal */
|
|
||||||
private _historyIndex: number = 0;
|
private _historyIndex: number = 0;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_subject: EventEmitter<any> = new EventEmitter();
|
_subject: EventEmitter<any> = new EventEmitter();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/compiler-cli",
|
"name": "@angular/compiler-cli",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.0.0-PLACEHOLDER",
|
||||||
"description": "Execute angular2 template compiler in nodejs.",
|
"description": "Angular - the compiler CLI for Node.js",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
"bin": {
|
"bin": {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/compiler",
|
"name": "@angular/compiler",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.0.0-PLACEHOLDER",
|
||||||
"description": "Angular2 - compiler",
|
"description": "Angular - the compiler library",
|
||||||
"main": "bundles/compiler.umd.js",
|
"main": "bundles/compiler.umd.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
|
@ -115,27 +115,27 @@ export class DirectiveNormalizer {
|
|||||||
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
|
||||||
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
{styles: visitor.styles, styleUrls: visitor.styleUrls, moduleUrl: templateAbsUrl}));
|
||||||
|
|
||||||
const allStyles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
|
||||||
const allStyleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
|
||||||
|
|
||||||
let encapsulation = templateMeta.encapsulation;
|
let encapsulation = templateMeta.encapsulation;
|
||||||
if (isBlank(encapsulation)) {
|
if (isBlank(encapsulation)) {
|
||||||
encapsulation = this._config.defaultEncapsulation;
|
encapsulation = this._config.defaultEncapsulation;
|
||||||
}
|
}
|
||||||
if (encapsulation === ViewEncapsulation.Emulated && allStyles.length === 0 &&
|
|
||||||
allStyleUrls.length === 0) {
|
const styles = templateMetadataStyles.styles.concat(templateStyles.styles);
|
||||||
|
const styleUrls = templateMetadataStyles.styleUrls.concat(templateStyles.styleUrls);
|
||||||
|
|
||||||
|
if (encapsulation === ViewEncapsulation.Emulated && styles.length === 0 &&
|
||||||
|
styleUrls.length === 0) {
|
||||||
encapsulation = ViewEncapsulation.None;
|
encapsulation = ViewEncapsulation.None;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CompileTemplateMetadata({
|
return new CompileTemplateMetadata({
|
||||||
encapsulation,
|
encapsulation,
|
||||||
template: template,
|
template,
|
||||||
templateUrl: templateAbsUrl,
|
templateUrl: templateAbsUrl, styles, styleUrls,
|
||||||
styles: allStyles,
|
|
||||||
styleUrls: allStyleUrls,
|
|
||||||
externalStylesheets: templateMeta.externalStylesheets,
|
externalStylesheets: templateMeta.externalStylesheets,
|
||||||
ngContentSelectors: visitor.ngContentSelectors,
|
ngContentSelectors: visitor.ngContentSelectors,
|
||||||
animations: templateMeta.animations,
|
animations: templateMeta.animations,
|
||||||
interpolation: templateMeta.interpolation
|
interpolation: templateMeta.interpolation,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate(
|
|||||||
viewProviders: directive.viewProviders,
|
viewProviders: directive.viewProviders,
|
||||||
queries: directive.queries,
|
queries: directive.queries,
|
||||||
viewQueries: directive.viewQueries,
|
viewQueries: directive.viewQueries,
|
||||||
entryComponents: directive.entryComponents,
|
entryComponents: directive.entryComponents, template,
|
||||||
template: template
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
visitAll(asts: any[]): any[] {
|
visitAll(asts: any[]): any[] {
|
||||||
var res = ListWrapper.createFixedSize(asts.length);
|
var res = new Array(asts.length);
|
||||||
for (var i = 0; i < asts.length; ++i) {
|
for (var i = 0; i < asts.length; ++i) {
|
||||||
res[i] = asts[i].visit(this);
|
res[i] = asts[i].visit(this);
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,10 @@
|
|||||||
|
|
||||||
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
import {AnimationAnimateMetadata, AnimationEntryMetadata, AnimationGroupMetadata, AnimationKeyframesSequenceMetadata, AnimationMetadata, AnimationStateDeclarationMetadata, AnimationStateMetadata, AnimationStateTransitionMetadata, AnimationStyleMetadata, AnimationWithStepsMetadata, Attribute, ChangeDetectionStrategy, Component, Host, Inject, Injectable, ModuleWithProviders, Optional, Provider, Query, SchemaMetadata, Self, SkipSelf, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {StringMapWrapper} from '../src/facade/collection';
|
|
||||||
|
|
||||||
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
|
||||||
import * as cpl from './compile_metadata';
|
import * as cpl from './compile_metadata';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
import {StringWrapper, isArray, isBlank, isPresent, isString, stringify} from './facade/lang';
|
import {isBlank, isPresent, isString, stringify} from './facade/lang';
|
||||||
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifierToken} from './identifiers';
|
||||||
import {hasLifecycleHook} from './lifecycle_reflector';
|
import {hasLifecycleHook} from './lifecycle_reflector';
|
||||||
import {NgModuleResolver} from './ng_module_resolver';
|
import {NgModuleResolver} from './ng_module_resolver';
|
||||||
@ -42,7 +40,7 @@ export class CompileMetadataResolver {
|
|||||||
if (identifier.indexOf('(') >= 0) {
|
if (identifier.indexOf('(') >= 0) {
|
||||||
// case: anonymous functions!
|
// case: anonymous functions!
|
||||||
let found = this._anonymousTypes.get(token);
|
let found = this._anonymousTypes.get(token);
|
||||||
if (isBlank(found)) {
|
if (!found) {
|
||||||
this._anonymousTypes.set(token, this._anonymousTypeIndex++);
|
this._anonymousTypes.set(token, this._anonymousTypeIndex++);
|
||||||
found = this._anonymousTypes.get(token);
|
found = this._anonymousTypes.get(token);
|
||||||
}
|
}
|
||||||
@ -67,18 +65,21 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
|
||||||
var defs = entry.definitions.map(def => this.getAnimationStateMetadata(def));
|
const defs = entry.definitions.map(def => this.getAnimationStateMetadata(def));
|
||||||
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
return new cpl.CompileAnimationEntryMetadata(entry.name, defs);
|
||||||
}
|
}
|
||||||
|
|
||||||
getAnimationStateMetadata(value: AnimationStateMetadata): cpl.CompileAnimationStateMetadata {
|
getAnimationStateMetadata(value: AnimationStateMetadata): cpl.CompileAnimationStateMetadata {
|
||||||
if (value instanceof AnimationStateDeclarationMetadata) {
|
if (value instanceof AnimationStateDeclarationMetadata) {
|
||||||
var styles = this.getAnimationStyleMetadata(value.styles);
|
const styles = this.getAnimationStyleMetadata(value.styles);
|
||||||
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
|
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
|
||||||
} else if (value instanceof AnimationStateTransitionMetadata) {
|
}
|
||||||
|
|
||||||
|
if (value instanceof AnimationStateTransitionMetadata) {
|
||||||
return new cpl.CompileAnimationStateTransitionMetadata(
|
return new cpl.CompileAnimationStateTransitionMetadata(
|
||||||
value.stateChangeExpr, this.getAnimationMetadata(value.steps));
|
value.stateChangeExpr, this.getAnimationMetadata(value.steps));
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,21 +90,28 @@ export class CompileMetadataResolver {
|
|||||||
getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata {
|
getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata {
|
||||||
if (value instanceof AnimationStyleMetadata) {
|
if (value instanceof AnimationStyleMetadata) {
|
||||||
return this.getAnimationStyleMetadata(value);
|
return this.getAnimationStyleMetadata(value);
|
||||||
} else if (value instanceof AnimationKeyframesSequenceMetadata) {
|
}
|
||||||
|
|
||||||
|
if (value instanceof AnimationKeyframesSequenceMetadata) {
|
||||||
return new cpl.CompileAnimationKeyframesSequenceMetadata(
|
return new cpl.CompileAnimationKeyframesSequenceMetadata(
|
||||||
value.steps.map(entry => this.getAnimationStyleMetadata(entry)));
|
value.steps.map(entry => this.getAnimationStyleMetadata(entry)));
|
||||||
} else if (value instanceof AnimationAnimateMetadata) {
|
}
|
||||||
let animateData =
|
|
||||||
|
if (value instanceof AnimationAnimateMetadata) {
|
||||||
|
const animateData =
|
||||||
<cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this
|
<cpl.CompileAnimationStyleMetadata|cpl.CompileAnimationKeyframesSequenceMetadata>this
|
||||||
.getAnimationMetadata(value.styles);
|
.getAnimationMetadata(value.styles);
|
||||||
return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData);
|
return new cpl.CompileAnimationAnimateMetadata(value.timings, animateData);
|
||||||
} else if (value instanceof AnimationWithStepsMetadata) {
|
}
|
||||||
var steps = value.steps.map(step => this.getAnimationMetadata(step));
|
|
||||||
|
if (value instanceof AnimationWithStepsMetadata) {
|
||||||
|
const steps = value.steps.map(step => this.getAnimationMetadata(step));
|
||||||
|
|
||||||
if (value instanceof AnimationGroupMetadata) {
|
if (value instanceof AnimationGroupMetadata) {
|
||||||
return new cpl.CompileAnimationGroupMetadata(steps);
|
return new cpl.CompileAnimationGroupMetadata(steps);
|
||||||
} else {
|
|
||||||
return new cpl.CompileAnimationSequenceMetadata(steps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return new cpl.CompileAnimationSequenceMetadata(steps);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -111,47 +119,49 @@ export class CompileMetadataResolver {
|
|||||||
getDirectiveMetadata(directiveType: Type<any>, throwIfNotFound = true):
|
getDirectiveMetadata(directiveType: Type<any>, throwIfNotFound = true):
|
||||||
cpl.CompileDirectiveMetadata {
|
cpl.CompileDirectiveMetadata {
|
||||||
directiveType = resolveForwardRef(directiveType);
|
directiveType = resolveForwardRef(directiveType);
|
||||||
var meta = this._directiveCache.get(directiveType);
|
let meta = this._directiveCache.get(directiveType);
|
||||||
if (isBlank(meta)) {
|
if (!meta) {
|
||||||
var dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound);
|
const dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound);
|
||||||
if (!dirMeta) {
|
if (!dirMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
var templateMeta: cpl.CompileTemplateMetadata = null;
|
let templateMeta: cpl.CompileTemplateMetadata = null;
|
||||||
var changeDetectionStrategy: ChangeDetectionStrategy = null;
|
let changeDetectionStrategy: ChangeDetectionStrategy = null;
|
||||||
var viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||||
var moduleUrl = staticTypeModuleUrl(directiveType);
|
let moduleUrl = staticTypeModuleUrl(directiveType);
|
||||||
var entryComponentMetadata: cpl.CompileTypeMetadata[] = [];
|
let entryComponentMetadata: cpl.CompileTypeMetadata[] = [];
|
||||||
let selector = dirMeta.selector;
|
let selector = dirMeta.selector;
|
||||||
|
|
||||||
if (dirMeta instanceof Component) {
|
if (dirMeta instanceof Component) {
|
||||||
var cmpMeta = <Component>dirMeta;
|
// Component
|
||||||
assertArrayOfStrings('styles', cmpMeta.styles);
|
assertArrayOfStrings('styles', dirMeta.styles);
|
||||||
assertInterpolationSymbols('interpolation', cmpMeta.interpolation);
|
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
|
||||||
var animations = isPresent(cmpMeta.animations) ?
|
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
|
||||||
cmpMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
|
||||||
|
const animations = dirMeta.animations ?
|
||||||
|
dirMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
|
||||||
null;
|
null;
|
||||||
assertArrayOfStrings('styles', cmpMeta.styles);
|
|
||||||
assertArrayOfStrings('styleUrls', cmpMeta.styleUrls);
|
|
||||||
|
|
||||||
templateMeta = new cpl.CompileTemplateMetadata({
|
templateMeta = new cpl.CompileTemplateMetadata({
|
||||||
encapsulation: cmpMeta.encapsulation,
|
encapsulation: dirMeta.encapsulation,
|
||||||
template: cmpMeta.template,
|
template: dirMeta.template,
|
||||||
templateUrl: cmpMeta.templateUrl,
|
templateUrl: dirMeta.templateUrl,
|
||||||
styles: cmpMeta.styles,
|
styles: dirMeta.styles,
|
||||||
styleUrls: cmpMeta.styleUrls,
|
styleUrls: dirMeta.styleUrls,
|
||||||
animations: animations,
|
animations: animations,
|
||||||
interpolation: cmpMeta.interpolation
|
interpolation: dirMeta.interpolation
|
||||||
});
|
});
|
||||||
changeDetectionStrategy = cmpMeta.changeDetection;
|
|
||||||
if (isPresent(dirMeta.viewProviders)) {
|
changeDetectionStrategy = dirMeta.changeDetection;
|
||||||
|
if (dirMeta.viewProviders) {
|
||||||
viewProviders = this.getProvidersMetadata(
|
viewProviders = this.getProvidersMetadata(
|
||||||
dirMeta.viewProviders, entryComponentMetadata,
|
dirMeta.viewProviders, entryComponentMetadata,
|
||||||
`viewProviders for "${stringify(directiveType)}"`);
|
`viewProviders for "${stringify(directiveType)}"`);
|
||||||
}
|
}
|
||||||
moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta);
|
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
|
||||||
if (cmpMeta.entryComponents) {
|
if (dirMeta.entryComponents) {
|
||||||
entryComponentMetadata =
|
entryComponentMetadata =
|
||||||
flattenArray(cmpMeta.entryComponents)
|
flattenArray(dirMeta.entryComponents)
|
||||||
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
|
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
|
||||||
.concat(entryComponentMetadata);
|
.concat(entryComponentMetadata);
|
||||||
}
|
}
|
||||||
@ -159,27 +169,29 @@ export class CompileMetadataResolver {
|
|||||||
selector = this._schemaRegistry.getDefaultComponentElementName();
|
selector = this._schemaRegistry.getDefaultComponentElementName();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
// Directive
|
||||||
if (!selector) {
|
if (!selector) {
|
||||||
throw new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`);
|
throw new Error(`Directive ${stringify(directiveType)} has no selector, please add it!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
let providers: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
|
||||||
if (isPresent(dirMeta.providers)) {
|
if (isPresent(dirMeta.providers)) {
|
||||||
providers = this.getProvidersMetadata(
|
providers = this.getProvidersMetadata(
|
||||||
dirMeta.providers, entryComponentMetadata,
|
dirMeta.providers, entryComponentMetadata,
|
||||||
`providers for "${stringify(directiveType)}"`);
|
`providers for "${stringify(directiveType)}"`);
|
||||||
}
|
}
|
||||||
var queries: cpl.CompileQueryMetadata[] = [];
|
let queries: cpl.CompileQueryMetadata[] = [];
|
||||||
var viewQueries: cpl.CompileQueryMetadata[] = [];
|
let viewQueries: cpl.CompileQueryMetadata[] = [];
|
||||||
if (isPresent(dirMeta.queries)) {
|
if (isPresent(dirMeta.queries)) {
|
||||||
queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType);
|
queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType);
|
||||||
viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType);
|
viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType);
|
||||||
}
|
}
|
||||||
|
|
||||||
meta = cpl.CompileDirectiveMetadata.create({
|
meta = cpl.CompileDirectiveMetadata.create({
|
||||||
selector: selector,
|
selector: selector,
|
||||||
exportAs: dirMeta.exportAs,
|
exportAs: dirMeta.exportAs,
|
||||||
isComponent: isPresent(templateMeta),
|
isComponent: !!templateMeta,
|
||||||
type: this.getTypeMetadata(directiveType, moduleUrl),
|
type: this.getTypeMetadata(directiveType, moduleUrl),
|
||||||
template: templateMeta,
|
template: templateMeta,
|
||||||
changeDetection: changeDetectionStrategy,
|
changeDetection: changeDetectionStrategy,
|
||||||
@ -199,7 +211,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
|
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
|
||||||
moduleType = resolveForwardRef(moduleType);
|
moduleType = resolveForwardRef(moduleType);
|
||||||
var compileMeta = this._ngModuleCache.get(moduleType);
|
let compileMeta = this._ngModuleCache.get(moduleType);
|
||||||
if (!compileMeta) {
|
if (!compileMeta) {
|
||||||
const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
|
const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
|
||||||
if (!meta) {
|
if (!meta) {
|
||||||
@ -230,8 +242,9 @@ export class CompileMetadataResolver {
|
|||||||
`provider for the NgModule '${stringify(importedModuleType)}'`));
|
`provider for the NgModule '${stringify(importedModuleType)}'`));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (importedModuleType) {
|
if (importedModuleType) {
|
||||||
let importedMeta = this.getNgModuleMetadata(importedModuleType, false);
|
const importedMeta = this.getNgModuleMetadata(importedModuleType, false);
|
||||||
if (importedMeta === null) {
|
if (importedMeta === null) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
|
||||||
@ -298,11 +311,13 @@ export class CompileMetadataResolver {
|
|||||||
meta.providers, entryComponents,
|
meta.providers, entryComponents,
|
||||||
`provider for the NgModule '${stringify(moduleType)}'`));
|
`provider for the NgModule '${stringify(moduleType)}'`));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.entryComponents) {
|
if (meta.entryComponents) {
|
||||||
entryComponents.push(
|
entryComponents.push(
|
||||||
...flattenArray(meta.entryComponents)
|
...flattenArray(meta.entryComponents)
|
||||||
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
|
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.bootstrap) {
|
if (meta.bootstrap) {
|
||||||
const typeMetadata = flattenArray(meta.bootstrap).map(type => {
|
const typeMetadata = flattenArray(meta.bootstrap).map(type => {
|
||||||
if (!isValidType(type)) {
|
if (!isValidType(type)) {
|
||||||
@ -313,7 +328,9 @@ export class CompileMetadataResolver {
|
|||||||
});
|
});
|
||||||
bootstrapComponents.push(...typeMetadata);
|
bootstrapComponents.push(...typeMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
entryComponents.push(...bootstrapComponents);
|
entryComponents.push(...bootstrapComponents);
|
||||||
|
|
||||||
if (meta.schemas) {
|
if (meta.schemas) {
|
||||||
schemas.push(...flattenArray(meta.schemas));
|
schemas.push(...flattenArray(meta.schemas));
|
||||||
}
|
}
|
||||||
@ -323,19 +340,20 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
compileMeta = new cpl.CompileNgModuleMetadata({
|
compileMeta = new cpl.CompileNgModuleMetadata({
|
||||||
type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
|
type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
|
||||||
providers: providers,
|
providers,
|
||||||
entryComponents: entryComponents,
|
entryComponents,
|
||||||
bootstrapComponents: bootstrapComponents,
|
bootstrapComponents,
|
||||||
schemas: schemas,
|
schemas,
|
||||||
declaredDirectives: declaredDirectives,
|
declaredDirectives,
|
||||||
exportedDirectives: exportedDirectives,
|
exportedDirectives,
|
||||||
declaredPipes: declaredPipes,
|
declaredPipes,
|
||||||
exportedPipes: exportedPipes,
|
exportedPipes,
|
||||||
importedModules: importedModules,
|
importedModules,
|
||||||
exportedModules: exportedModules,
|
exportedModules,
|
||||||
transitiveModule: transitiveModule,
|
transitiveModule,
|
||||||
id: meta.id,
|
id: meta.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
transitiveModule.modules.push(compileMeta);
|
transitiveModule.modules.push(compileMeta);
|
||||||
this._verifyModule(compileMeta);
|
this._verifyModule(compileMeta);
|
||||||
this._ngModuleCache.set(moduleType, compileMeta);
|
this._ngModuleCache.set(moduleType, compileMeta);
|
||||||
@ -351,6 +369,7 @@ export class CompileMetadataResolver {
|
|||||||
`Can't export directive ${stringify(dirMeta.type.reference)} from ${stringify(moduleMeta.type.reference)} as it was neither declared nor imported!`);
|
`Can't export directive ${stringify(dirMeta.type.reference)} from ${stringify(moduleMeta.type.reference)} as it was neither declared nor imported!`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
moduleMeta.exportedPipes.forEach((pipeMeta) => {
|
moduleMeta.exportedPipes.forEach((pipeMeta) => {
|
||||||
if (!moduleMeta.transitiveModule.pipesSet.has(pipeMeta.type.reference)) {
|
if (!moduleMeta.transitiveModule.pipesSet.has(pipeMeta.type.reference)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
@ -362,15 +381,21 @@ export class CompileMetadataResolver {
|
|||||||
private _getTypeDescriptor(type: Type<any>): string {
|
private _getTypeDescriptor(type: Type<any>): string {
|
||||||
if (this._directiveResolver.resolve(type, false) !== null) {
|
if (this._directiveResolver.resolve(type, false) !== null) {
|
||||||
return 'directive';
|
return 'directive';
|
||||||
} else if (this._pipeResolver.resolve(type, false) !== null) {
|
|
||||||
return 'pipe';
|
|
||||||
} else if (this._ngModuleResolver.resolve(type, false) !== null) {
|
|
||||||
return 'module';
|
|
||||||
} else if ((type as any).provide) {
|
|
||||||
return 'provider';
|
|
||||||
} else {
|
|
||||||
return 'value';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._pipeResolver.resolve(type, false) !== null) {
|
||||||
|
return 'pipe';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._ngModuleResolver.resolve(type, false) !== null) {
|
||||||
|
return 'module';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((type as any).provide) {
|
||||||
|
return 'provider';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'value';
|
||||||
}
|
}
|
||||||
|
|
||||||
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
|
private _addTypeToModule(type: Type<any>, moduleType: Type<any>) {
|
||||||
@ -434,7 +459,7 @@ export class CompileMetadataResolver {
|
|||||||
type = resolveForwardRef(type);
|
type = resolveForwardRef(type);
|
||||||
return new cpl.CompileTypeMetadata({
|
return new cpl.CompileTypeMetadata({
|
||||||
name: this.sanitizeTokenName(type),
|
name: this.sanitizeTokenName(type),
|
||||||
moduleUrl: moduleUrl,
|
moduleUrl,
|
||||||
reference: type,
|
reference: type,
|
||||||
diDeps: this.getDependenciesMetadata(type, dependencies),
|
diDeps: this.getDependenciesMetadata(type, dependencies),
|
||||||
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, type)),
|
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, type)),
|
||||||
@ -446,7 +471,7 @@ export class CompileMetadataResolver {
|
|||||||
factory = resolveForwardRef(factory);
|
factory = resolveForwardRef(factory);
|
||||||
return new cpl.CompileFactoryMetadata({
|
return new cpl.CompileFactoryMetadata({
|
||||||
name: this.sanitizeTokenName(factory),
|
name: this.sanitizeTokenName(factory),
|
||||||
moduleUrl: moduleUrl,
|
moduleUrl,
|
||||||
reference: factory,
|
reference: factory,
|
||||||
diDeps: this.getDependenciesMetadata(factory, dependencies)
|
diDeps: this.getDependenciesMetadata(factory, dependencies)
|
||||||
});
|
});
|
||||||
@ -454,12 +479,13 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
getPipeMetadata(pipeType: Type<any>, throwIfNotFound = true): cpl.CompilePipeMetadata {
|
getPipeMetadata(pipeType: Type<any>, throwIfNotFound = true): cpl.CompilePipeMetadata {
|
||||||
pipeType = resolveForwardRef(pipeType);
|
pipeType = resolveForwardRef(pipeType);
|
||||||
var meta = this._pipeCache.get(pipeType);
|
let meta = this._pipeCache.get(pipeType);
|
||||||
if (isBlank(meta)) {
|
if (!meta) {
|
||||||
var pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound);
|
const pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound);
|
||||||
if (!pipeMeta) {
|
if (!pipeMeta) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta = new cpl.CompilePipeMetadata({
|
meta = new cpl.CompilePipeMetadata({
|
||||||
type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
|
type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
|
||||||
name: pipeMeta.name,
|
name: pipeMeta.name,
|
||||||
@ -473,10 +499,8 @@ export class CompileMetadataResolver {
|
|||||||
getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]):
|
getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]):
|
||||||
cpl.CompileDiDependencyMetadata[] {
|
cpl.CompileDiDependencyMetadata[] {
|
||||||
let hasUnknownDeps = false;
|
let hasUnknownDeps = false;
|
||||||
let params = isPresent(dependencies) ? dependencies : this._reflector.parameters(typeOrFunc);
|
let params = dependencies || this._reflector.parameters(typeOrFunc) || [];
|
||||||
if (isBlank(params)) {
|
|
||||||
params = [];
|
|
||||||
}
|
|
||||||
let dependenciesMetadata: cpl.CompileDiDependencyMetadata[] = params.map((param) => {
|
let dependenciesMetadata: cpl.CompileDiDependencyMetadata[] = params.map((param) => {
|
||||||
let isAttribute = false;
|
let isAttribute = false;
|
||||||
let isHost = false;
|
let isHost = false;
|
||||||
@ -486,8 +510,8 @@ export class CompileMetadataResolver {
|
|||||||
let query: Query = null;
|
let query: Query = null;
|
||||||
let viewQuery: Query = null;
|
let viewQuery: Query = null;
|
||||||
var token: any = null;
|
var token: any = null;
|
||||||
if (isArray(param)) {
|
if (Array.isArray(param)) {
|
||||||
(<any[]>param).forEach((paramEntry) => {
|
param.forEach((paramEntry) => {
|
||||||
if (paramEntry instanceof Host) {
|
if (paramEntry instanceof Host) {
|
||||||
isHost = true;
|
isHost = true;
|
||||||
} else if (paramEntry instanceof Self) {
|
} else if (paramEntry instanceof Self) {
|
||||||
@ -518,14 +542,15 @@ export class CompileMetadataResolver {
|
|||||||
hasUnknownDeps = true;
|
hasUnknownDeps = true;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileDiDependencyMetadata({
|
return new cpl.CompileDiDependencyMetadata({
|
||||||
isAttribute: isAttribute,
|
isAttribute,
|
||||||
isHost: isHost,
|
isHost,
|
||||||
isSelf: isSelf,
|
isSelf,
|
||||||
isSkipSelf: isSkipSelf,
|
isSkipSelf,
|
||||||
isOptional: isOptional,
|
isOptional,
|
||||||
query: isPresent(query) ? this.getQueryMetadata(query, null, typeOrFunc) : null,
|
query: query ? this.getQueryMetadata(query, null, typeOrFunc) : null,
|
||||||
viewQuery: isPresent(viewQuery) ? this.getQueryMetadata(viewQuery, null, typeOrFunc) : null,
|
viewQuery: viewQuery ? this.getQueryMetadata(viewQuery, null, typeOrFunc) : null,
|
||||||
token: this.getTokenMetadata(token)
|
token: this.getTokenMetadata(token)
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -533,8 +558,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
if (hasUnknownDeps) {
|
if (hasUnknownDeps) {
|
||||||
let depsTokens =
|
let depsTokens =
|
||||||
dependenciesMetadata.map((dep) => { return dep ? stringify(dep.token) : '?'; })
|
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
|
||||||
.join(', ');
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`);
|
`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`);
|
||||||
}
|
}
|
||||||
@ -544,7 +568,7 @@ export class CompileMetadataResolver {
|
|||||||
|
|
||||||
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
|
||||||
token = resolveForwardRef(token);
|
token = resolveForwardRef(token);
|
||||||
var compileToken: any /** TODO #9100 */;
|
let compileToken: cpl.CompileTokenMetadata;
|
||||||
if (isString(token)) {
|
if (isString(token)) {
|
||||||
compileToken = new cpl.CompileTokenMetadata({value: token});
|
compileToken = new cpl.CompileTokenMetadata({value: token});
|
||||||
} else {
|
} else {
|
||||||
@ -569,7 +593,7 @@ export class CompileMetadataResolver {
|
|||||||
provider = new cpl.ProviderMeta(provider.provide, provider);
|
provider = new cpl.ProviderMeta(provider.provide, provider);
|
||||||
}
|
}
|
||||||
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
|
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
|
||||||
if (isArray(provider)) {
|
if (Array.isArray(provider)) {
|
||||||
compileProvider = this.getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
compileProvider = this.getProvidersMetadata(provider, targetEntryComponents, debugInfo);
|
||||||
} else if (provider instanceof cpl.ProviderMeta) {
|
} else if (provider instanceof cpl.ProviderMeta) {
|
||||||
let tokenMeta = this.getTokenMetadata(provider.token);
|
let tokenMeta = this.getTokenMetadata(provider.token);
|
||||||
@ -607,17 +631,20 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): cpl.CompileTypeMetadata[] {
|
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): cpl.CompileTypeMetadata[] {
|
||||||
let components: cpl.CompileTypeMetadata[] = [];
|
const components: cpl.CompileTypeMetadata[] = [];
|
||||||
let collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
|
||||||
|
|
||||||
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
if (provider.useFactory || provider.useExisting || provider.useClass) {
|
||||||
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`);
|
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!provider.multi) {
|
if (!provider.multi) {
|
||||||
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
|
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
convertToCompileValue(provider.useValue, collectedIdentifiers);
|
convertToCompileValue(provider.useValue, collectedIdentifiers);
|
||||||
collectedIdentifiers.forEach((identifier) => {
|
collectedIdentifiers.forEach((identifier) => {
|
||||||
let dirMeta = this.getDirectiveMetadata(identifier.reference, false);
|
const dirMeta = this.getDirectiveMetadata(identifier.reference, false);
|
||||||
if (dirMeta) {
|
if (dirMeta) {
|
||||||
components.push(dirMeta.type);
|
components.push(dirMeta.type);
|
||||||
}
|
}
|
||||||
@ -626,15 +653,15 @@ export class CompileMetadataResolver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
|
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
|
||||||
var compileDeps: cpl.CompileDiDependencyMetadata[];
|
let compileDeps: cpl.CompileDiDependencyMetadata[];
|
||||||
var compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
|
||||||
var compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
|
||||||
|
|
||||||
if (isPresent(provider.useClass)) {
|
if (provider.useClass) {
|
||||||
compileTypeMetadata = this.getTypeMetadata(
|
compileTypeMetadata = this.getTypeMetadata(
|
||||||
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
|
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
|
||||||
compileDeps = compileTypeMetadata.diDeps;
|
compileDeps = compileTypeMetadata.diDeps;
|
||||||
} else if (isPresent(provider.useFactory)) {
|
} else if (provider.useFactory) {
|
||||||
compileFactoryMetadata = this.getFactoryMetadata(
|
compileFactoryMetadata = this.getFactoryMetadata(
|
||||||
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
|
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
|
||||||
compileDeps = compileFactoryMetadata.diDeps;
|
compileDeps = compileFactoryMetadata.diDeps;
|
||||||
@ -645,8 +672,7 @@ export class CompileMetadataResolver {
|
|||||||
useClass: compileTypeMetadata,
|
useClass: compileTypeMetadata,
|
||||||
useValue: convertToCompileValue(provider.useValue, []),
|
useValue: convertToCompileValue(provider.useValue, []),
|
||||||
useFactory: compileFactoryMetadata,
|
useFactory: compileFactoryMetadata,
|
||||||
useExisting: isPresent(provider.useExisting) ? this.getTokenMetadata(provider.useExisting) :
|
useExisting: provider.useExisting ? this.getTokenMetadata(provider.useExisting) : null,
|
||||||
null,
|
|
||||||
deps: compileDeps,
|
deps: compileDeps,
|
||||||
multi: provider.multi
|
multi: provider.multi
|
||||||
});
|
});
|
||||||
@ -655,38 +681,38 @@ export class CompileMetadataResolver {
|
|||||||
getQueriesMetadata(
|
getQueriesMetadata(
|
||||||
queries: {[key: string]: Query}, isViewQuery: boolean,
|
queries: {[key: string]: Query}, isViewQuery: boolean,
|
||||||
directiveType: Type<any>): cpl.CompileQueryMetadata[] {
|
directiveType: Type<any>): cpl.CompileQueryMetadata[] {
|
||||||
var res: cpl.CompileQueryMetadata[] = [];
|
const res: cpl.CompileQueryMetadata[] = [];
|
||||||
StringMapWrapper.forEach(queries, (query: Query, propertyName: string) => {
|
|
||||||
|
Object.keys(queries).forEach((propertyName: string) => {
|
||||||
|
const query = queries[propertyName];
|
||||||
if (query.isViewQuery === isViewQuery) {
|
if (query.isViewQuery === isViewQuery) {
|
||||||
res.push(this.getQueryMetadata(query, propertyName, directiveType));
|
res.push(this.getQueryMetadata(query, propertyName, directiveType));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _queryVarBindings(selector: any): string[] {
|
private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); }
|
||||||
return StringWrapper.split(selector, /\s*,\s*/g);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function):
|
getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function):
|
||||||
cpl.CompileQueryMetadata {
|
cpl.CompileQueryMetadata {
|
||||||
var selectors: cpl.CompileTokenMetadata[];
|
var selectors: cpl.CompileTokenMetadata[];
|
||||||
if (isString(q.selector)) {
|
if (typeof q.selector === 'string') {
|
||||||
selectors = this._queryVarBindings(q.selector).map(varName => this.getTokenMetadata(varName));
|
selectors = this._queryVarBindings(q.selector).map(varName => this.getTokenMetadata(varName));
|
||||||
} else {
|
} else {
|
||||||
if (!isPresent(q.selector)) {
|
if (!q.selector) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
|
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
|
||||||
}
|
}
|
||||||
selectors = [this.getTokenMetadata(q.selector)];
|
selectors = [this.getTokenMetadata(q.selector)];
|
||||||
}
|
}
|
||||||
|
|
||||||
return new cpl.CompileQueryMetadata({
|
return new cpl.CompileQueryMetadata({
|
||||||
selectors: selectors,
|
selectors,
|
||||||
first: q.first,
|
first: q.first,
|
||||||
descendants: q.descendants,
|
descendants: q.descendants, propertyName,
|
||||||
propertyName: propertyName,
|
read: q.read ? this.getTokenMetadata(q.read) : null
|
||||||
read: isPresent(q.read) ? this.getTokenMetadata(q.read) : null
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -713,9 +739,9 @@ function getTransitiveModules(
|
|||||||
|
|
||||||
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
|
||||||
if (tree) {
|
if (tree) {
|
||||||
for (var i = 0; i < tree.length; i++) {
|
for (let i = 0; i < tree.length; i++) {
|
||||||
var item = resolveForwardRef(tree[i]);
|
const item = resolveForwardRef(tree[i]);
|
||||||
if (isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
flattenArray(item, out);
|
flattenArray(item, out);
|
||||||
} else {
|
} else {
|
||||||
out.push(item);
|
out.push(item);
|
||||||
@ -733,16 +759,21 @@ function staticTypeModuleUrl(value: any): string {
|
|||||||
return cpl.isStaticSymbol(value) ? value.filePath : null;
|
return cpl.isStaticSymbol(value) ? value.filePath : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function componentModuleUrl(reflector: ReflectorReader, type: any, cmpMetadata: Component): string {
|
function componentModuleUrl(
|
||||||
|
reflector: ReflectorReader, type: Type<any>, cmpMetadata: Component): string {
|
||||||
if (cpl.isStaticSymbol(type)) {
|
if (cpl.isStaticSymbol(type)) {
|
||||||
return staticTypeModuleUrl(type);
|
return staticTypeModuleUrl(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isPresent(cmpMetadata.moduleId)) {
|
const moduleId = cmpMetadata.moduleId;
|
||||||
var moduleId = cmpMetadata.moduleId;
|
|
||||||
var scheme = getUrlScheme(moduleId);
|
if (typeof moduleId === 'string') {
|
||||||
return isPresent(scheme) && scheme.length > 0 ? moduleId :
|
const scheme = getUrlScheme(moduleId);
|
||||||
`package:${moduleId}${MODULE_SUFFIX}`;
|
return scheme ? moduleId : `package:${moduleId}${MODULE_SUFFIX}`;
|
||||||
|
} else if (moduleId !== null && moduleId !== void 0) {
|
||||||
|
throw new Error(
|
||||||
|
`moduleId should be a string in "${stringify(type)}". See https://goo.gl/wIDDiL for more information.\n` +
|
||||||
|
`If you're using Webpack you should inline the template and the styles, see https://goo.gl/X2J8zc.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflector.importUri(type);
|
return reflector.importUri(type);
|
||||||
|
@ -10,7 +10,6 @@ import {SchemaMetadata} from '@angular/core';
|
|||||||
|
|
||||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTokenMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTokenMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {ListWrapper} from './facade/collection';
|
|
||||||
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
|
||||||
import {CompileMetadataResolver} from './metadata_resolver';
|
import {CompileMetadataResolver} from './metadata_resolver';
|
||||||
import {NgModuleCompiler} from './ng_module_compiler';
|
import {NgModuleCompiler} from './ng_module_compiler';
|
||||||
@ -58,10 +57,10 @@ export class OfflineCompiler {
|
|||||||
compile(
|
compile(
|
||||||
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
|
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
|
||||||
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
|
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
|
||||||
let fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
|
const fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
|
||||||
let statements: o.Statement[] = [];
|
const statements: o.Statement[] = [];
|
||||||
let exportedVars: string[] = [];
|
const exportedVars: string[] = [];
|
||||||
let outputSourceModules: SourceModule[] = [];
|
const outputSourceModules: SourceModule[] = [];
|
||||||
|
|
||||||
// compile all ng modules
|
// compile all ng modules
|
||||||
exportedVars.push(
|
exportedVars.push(
|
||||||
@ -75,12 +74,12 @@ export class OfflineCompiler {
|
|||||||
if (!ngModule) {
|
if (!ngModule) {
|
||||||
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
|
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise
|
return Promise
|
||||||
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
.all([compMeta, ...ngModule.transitiveModule.directives].map(
|
||||||
dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
|
||||||
.then((normalizedCompWithDirectives) => {
|
.then((normalizedCompWithDirectives) => {
|
||||||
const compMeta = normalizedCompWithDirectives[0];
|
const [compMeta, ...dirMetas] = normalizedCompWithDirectives;
|
||||||
const dirMetas = normalizedCompWithDirectives.slice(1);
|
|
||||||
_assertComponent(compMeta);
|
_assertComponent(compMeta);
|
||||||
|
|
||||||
// compile styles
|
// compile styles
|
||||||
@ -90,10 +89,11 @@ export class OfflineCompiler {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// compile components
|
// compile components
|
||||||
exportedVars.push(this._compileComponentFactory(compMeta, fileSuffix, statements));
|
exportedVars.push(
|
||||||
exportedVars.push(this._compileComponent(
|
this._compileComponentFactory(compMeta, fileSuffix, statements),
|
||||||
compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas,
|
this._compileComponent(
|
||||||
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas,
|
||||||
|
stylesCompileResults.componentStylesheet, fileSuffix, statements));
|
||||||
});
|
});
|
||||||
}))
|
}))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@ -106,19 +106,30 @@ export class OfflineCompiler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
|
||||||
const ngModule = this._metadataResolver.getNgModuleMetadata(<any>ngModuleType);
|
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
|
||||||
let appCompileResult = this._ngModuleCompiler.compile(ngModule, [
|
const providers: CompileProviderMetadata[] = [];
|
||||||
new CompileProviderMetadata(
|
|
||||||
{token: resolveIdentifierToken(Identifiers.LOCALE_ID), useValue: this._localeId}),
|
if (this._localeId) {
|
||||||
new CompileProviderMetadata({
|
providers.push(new CompileProviderMetadata({
|
||||||
|
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
|
||||||
|
useValue: this._localeId,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._translationFormat) {
|
||||||
|
providers.push(new CompileProviderMetadata({
|
||||||
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
|
||||||
useValue: this._translationFormat
|
useValue: this._translationFormat
|
||||||
})
|
}));
|
||||||
]);
|
}
|
||||||
|
|
||||||
|
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
|
||||||
|
|
||||||
appCompileResult.dependencies.forEach((dep) => {
|
appCompileResult.dependencies.forEach((dep) => {
|
||||||
dep.placeholder.name = _componentFactoryName(dep.comp);
|
dep.placeholder.name = _componentFactoryName(dep.comp);
|
||||||
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
|
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
|
||||||
});
|
});
|
||||||
|
|
||||||
targetStatements.push(...appCompileResult.statements);
|
targetStatements.push(...appCompileResult.statements);
|
||||||
return appCompileResult.ngModuleFactoryVar;
|
return appCompileResult.ngModuleFactoryVar;
|
||||||
}
|
}
|
||||||
@ -126,18 +137,19 @@ export class OfflineCompiler {
|
|||||||
private _compileComponentFactory(
|
private _compileComponentFactory(
|
||||||
compMeta: CompileDirectiveMetadata, fileSuffix: string,
|
compMeta: CompileDirectiveMetadata, fileSuffix: string,
|
||||||
targetStatements: o.Statement[]): string {
|
targetStatements: o.Statement[]): string {
|
||||||
var hostMeta = createHostComponentMeta(compMeta);
|
const hostMeta = createHostComponentMeta(compMeta);
|
||||||
var hostViewFactoryVar =
|
const hostViewFactoryVar =
|
||||||
this._compileComponent(hostMeta, [compMeta], [], [], null, fileSuffix, targetStatements);
|
this._compileComponent(hostMeta, [compMeta], [], [], null, fileSuffix, targetStatements);
|
||||||
var compFactoryVar = _componentFactoryName(compMeta.type);
|
const compFactoryVar = _componentFactoryName(compMeta.type);
|
||||||
targetStatements.push(
|
targetStatements.push(
|
||||||
o.variable(compFactoryVar)
|
o.variable(compFactoryVar)
|
||||||
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
|
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
|
||||||
compMeta.type)])
|
compMeta.type)])
|
||||||
.instantiate(
|
.instantiate(
|
||||||
[
|
[
|
||||||
o.literal(compMeta.selector), o.variable(hostViewFactoryVar),
|
o.literal(compMeta.selector),
|
||||||
o.importExpr(compMeta.type)
|
o.variable(hostViewFactoryVar),
|
||||||
|
o.importExpr(compMeta.type),
|
||||||
],
|
],
|
||||||
o.importType(
|
o.importType(
|
||||||
resolveIdentifier(Identifiers.ComponentFactory),
|
resolveIdentifier(Identifiers.ComponentFactory),
|
||||||
@ -150,15 +162,15 @@ export class OfflineCompiler {
|
|||||||
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
|
||||||
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
|
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
|
||||||
fileSuffix: string, targetStatements: o.Statement[]): string {
|
fileSuffix: string, targetStatements: o.Statement[]): string {
|
||||||
var parsedTemplate = this._templateParser.parse(
|
const parsedTemplate = this._templateParser.parse(
|
||||||
compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name);
|
compMeta, compMeta.template.template, directives, pipes, schemas, compMeta.type.name);
|
||||||
var stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
|
||||||
var viewResult =
|
const viewResult =
|
||||||
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes);
|
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes);
|
||||||
if (componentStyles) {
|
if (componentStyles) {
|
||||||
ListWrapper.addAll(targetStatements, _resolveStyleStatements(componentStyles, fileSuffix));
|
targetStatements.push(..._resolveStyleStatements(componentStyles, fileSuffix));
|
||||||
}
|
}
|
||||||
ListWrapper.addAll(targetStatements, _resolveViewStatements(viewResult));
|
targetStatements.push(..._resolveViewStatements(viewResult));
|
||||||
return viewResult.viewFactoryVar;
|
return viewResult.viewFactoryVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,10 +192,10 @@ export class OfflineCompiler {
|
|||||||
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
|
||||||
compileResult.dependencies.forEach((dep) => {
|
compileResult.dependencies.forEach((dep) => {
|
||||||
if (dep instanceof ViewFactoryDependency) {
|
if (dep instanceof ViewFactoryDependency) {
|
||||||
let vfd = <ViewFactoryDependency>dep;
|
const vfd = <ViewFactoryDependency>dep;
|
||||||
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
|
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
|
||||||
} else if (dep instanceof ComponentFactoryDependency) {
|
} else if (dep instanceof ComponentFactoryDependency) {
|
||||||
let cfd = <ComponentFactoryDependency>dep;
|
const cfd = <ComponentFactoryDependency>dep;
|
||||||
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
cfd.placeholder.name = _componentFactoryName(cfd.comp);
|
||||||
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
|
||||||
}
|
}
|
||||||
@ -201,7 +213,7 @@ function _resolveStyleStatements(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _ngfactoryModuleUrl(compUrl: string): string {
|
function _ngfactoryModuleUrl(compUrl: string): string {
|
||||||
var urlWithSuffix = _splitTypescriptSuffix(compUrl);
|
const urlWithSuffix = _splitTypescriptSuffix(compUrl);
|
||||||
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,13 +232,15 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _splitTypescriptSuffix(path: string): string[] {
|
function _splitTypescriptSuffix(path: string): string[] {
|
||||||
if (/\.d\.ts$/.test(path)) {
|
if (path.endsWith('.d.ts')) {
|
||||||
return [path.substring(0, path.length - 5), '.ts'];
|
return [path.slice(0, -5), '.ts'];
|
||||||
}
|
}
|
||||||
let lastDot = path.lastIndexOf('.');
|
|
||||||
|
const lastDot = path.lastIndexOf('.');
|
||||||
|
|
||||||
if (lastDot !== -1) {
|
if (lastDot !== -1) {
|
||||||
return [path.substring(0, lastDot), path.substring(lastDot)];
|
return [path.substring(0, lastDot), path.substring(lastDot)];
|
||||||
} else {
|
|
||||||
return [path, ''];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [path, ''];
|
||||||
}
|
}
|
||||||
|
@ -426,7 +426,7 @@ export class ShadowCss {
|
|||||||
return scopedP;
|
return scopedP;
|
||||||
};
|
};
|
||||||
|
|
||||||
const sep = /( |>|\+|~)\s*/g;
|
const sep = /( |>|\+|~(?!=))\s*/g;
|
||||||
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
|
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
|
||||||
|
|
||||||
let scoped = '';
|
let scoped = '';
|
||||||
@ -483,7 +483,7 @@ function stripComments(input: string): string {
|
|||||||
return StringWrapper.replaceAllMapped(input, _commentRe, (_: any /** TODO #9100 */) => '');
|
return StringWrapper.replaceAllMapped(input, _commentRe, (_: any /** TODO #9100 */) => '');
|
||||||
}
|
}
|
||||||
|
|
||||||
// all comments except inline source mapping ("/* #sourceMappingURL= ... */")
|
// all comments except inline source mapping
|
||||||
const _sourceMappingUrlRe = /\/\*\s*#\s*sourceMappingURL=[\s\S]+?\*\//;
|
const _sourceMappingUrlRe = /\/\*\s*#\s*sourceMappingURL=[\s\S]+?\*\//;
|
||||||
|
|
||||||
function extractSourceMappingUrl(input: string): string {
|
function extractSourceMappingUrl(input: string): string {
|
||||||
|
@ -854,14 +854,14 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
||||||
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
||||||
bindingType = PropertyBindingType.Property;
|
bindingType = PropertyBindingType.Property;
|
||||||
this._assertNoEventBinding(boundPropertyName, sourceSpan);
|
this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
|
||||||
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
|
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
|
||||||
let errorMsg =
|
let errorMsg =
|
||||||
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
|
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
|
||||||
if (elementName.indexOf('-') > -1) {
|
if (elementName.indexOf('-') > -1) {
|
||||||
errorMsg +=
|
errorMsg +=
|
||||||
`\n1. If '${elementName}' is an Angular component and it has '${boundPropertyName}' input, then verify that it is part of this module.` +
|
`\n1. If '${elementName}' is an Angular component and it has '${boundPropertyName}' input, then verify that it is part of this module.` +
|
||||||
`\n2. If '${elementName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message.\n`;
|
`\n2. If '${elementName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.\n`;
|
||||||
}
|
}
|
||||||
this._reportError(errorMsg, sourceSpan);
|
this._reportError(errorMsg, sourceSpan);
|
||||||
}
|
}
|
||||||
@ -869,7 +869,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
} else {
|
} else {
|
||||||
if (parts[0] == ATTRIBUTE_PREFIX) {
|
if (parts[0] == ATTRIBUTE_PREFIX) {
|
||||||
boundPropertyName = parts[1];
|
boundPropertyName = parts[1];
|
||||||
this._assertNoEventBinding(boundPropertyName, sourceSpan);
|
this._assertNoEventBinding(boundPropertyName, sourceSpan, true);
|
||||||
// NB: For security purposes, use the mapped property name, not the attribute name.
|
// NB: For security purposes, use the mapped property name, not the attribute name.
|
||||||
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
|
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
|
||||||
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
|
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
|
||||||
@ -902,12 +902,23 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
|
boundPropertyName, bindingType, securityContext, ast, unit, sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan): void {
|
/**
|
||||||
|
* @param propName the name of the property / attribute
|
||||||
|
* @param sourceSpan
|
||||||
|
* @param isAttr true when binding to an attribute
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
private _assertNoEventBinding(propName: string, sourceSpan: ParseSourceSpan, isAttr: boolean):
|
||||||
|
void {
|
||||||
if (propName.toLowerCase().startsWith('on')) {
|
if (propName.toLowerCase().startsWith('on')) {
|
||||||
this._reportError(
|
let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` +
|
||||||
`Binding to event attribute '${propName}' is disallowed ` +
|
`please use (${propName.slice(2)})=...`;
|
||||||
`for security reasons, please use (${propName.slice(2)})=...`,
|
if (!isAttr) {
|
||||||
sourceSpan, ParseErrorLevel.FATAL);
|
msg +=
|
||||||
|
`\nIf '${propName}' is a directive input, make sure the directive is imported by the` +
|
||||||
|
` current module.`;
|
||||||
|
}
|
||||||
|
this._reportError(msg, sourceSpan, ParseErrorLevel.FATAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,7 +955,7 @@ class TemplateParseVisitor implements html.Visitor {
|
|||||||
if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {
|
if (!matchElement && !this._schemaRegistry.hasElement(elName, this._schemas)) {
|
||||||
const errorMsg = `'${elName}' is not a known element:\n` +
|
const errorMsg = `'${elName}' is not a known element:\n` +
|
||||||
`1. If '${elName}' is an Angular component, then verify that it is part of this module.\n` +
|
`1. If '${elName}' is an Angular component, then verify that it is part of this module.\n` +
|
||||||
`2. If '${elName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message.`;
|
`2. If '${elName}' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.`;
|
||||||
this._reportError(errorMsg, element.sourceSpan);
|
this._reportError(errorMsg, element.sourceSpan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ export class CompileElement extends CompileNode {
|
|||||||
setComponentView(compViewExpr: o.Expression) {
|
setComponentView(compViewExpr: o.Expression) {
|
||||||
this._compViewExpr = compViewExpr;
|
this._compViewExpr = compViewExpr;
|
||||||
this.contentNodesByNgContentIndex =
|
this.contentNodesByNgContentIndex =
|
||||||
ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length);
|
new Array(this.component.template.ngContentSelectors.length);
|
||||||
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||||
this.contentNodesByNgContentIndex[i] = [];
|
this.contentNodesByNgContentIndex[i] = [];
|
||||||
}
|
}
|
||||||
|
@ -340,7 +340,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
// Notice that the first guard condition is the left hand of the left most safe access node
|
// Notice that the first guard condition is the left hand of the left most safe access node
|
||||||
// which comes in as leftMostSafe to this routine.
|
// which comes in as leftMostSafe to this routine.
|
||||||
|
|
||||||
let guardedExpression = this.visit(leftMostSafe.receiver, mode);
|
let guardedExpression = this.visit(leftMostSafe.receiver, _Mode.Expression);
|
||||||
let temporary: o.ReadVarExpr;
|
let temporary: o.ReadVarExpr;
|
||||||
if (this.needsTemporary(leftMostSafe.receiver)) {
|
if (this.needsTemporary(leftMostSafe.receiver)) {
|
||||||
// If the expression has method calls or pipes then we need to save the result into a
|
// If the expression has method calls or pipes then we need to save the result into a
|
||||||
@ -369,7 +369,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Recursively convert the node now without the guarded member access.
|
// Recursively convert the node now without the guarded member access.
|
||||||
const access = this.visit(ast, mode);
|
const access = this.visit(ast, _Mode.Expression);
|
||||||
|
|
||||||
// Remove the mapping. This is not strictly required as the converter only traverses each node
|
// Remove the mapping. This is not strictly required as the converter only traverses each node
|
||||||
// once but is safer if the conversion is changed to traverse the nodes more than once.
|
// once but is safer if the conversion is changed to traverse the nodes more than once.
|
||||||
@ -381,7 +381,7 @@ class _AstToIrVisitor implements cdAst.AstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Produce the conditional
|
// Produce the conditional
|
||||||
return condition.conditional(o.literal(null), access);
|
return convertToStatementIfNeeded(mode, condition.conditional(o.literal(null), access));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Given a expression of the form a?.b.c?.d.e the the left most safe node is
|
// Given a expression of the form a?.b.c?.d.e the the left most safe node is
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
|
||||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, Directive, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation} from '@angular/core';
|
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, Component, DoCheck, Injectable, NgModule, OnChanges, OnDestroy, OnInit, Pipe, SimpleChanges, ViewEncapsulation} from '@angular/core';
|
||||||
import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks';
|
import {LIFECYCLE_HOOKS_VALUES} from '@angular/core/src/metadata/lifecycle_hooks';
|
||||||
import {TestBed, inject} from '@angular/core/testing';
|
import {TestBed, inject} from '@angular/core/testing';
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ export function main() {
|
|||||||
describe('getDirectiveMetadata', () => {
|
describe('getDirectiveMetadata', () => {
|
||||||
it('should read metadata',
|
it('should read metadata',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
var meta = resolver.getDirectiveMetadata(ComponentWithEverything);
|
const meta = resolver.getDirectiveMetadata(ComponentWithEverything);
|
||||||
expect(meta.selector).toEqual('someSelector');
|
expect(meta.selector).toEqual('someSelector');
|
||||||
expect(meta.exportAs).toEqual('someExportAs');
|
expect(meta.exportAs).toEqual('someExportAs');
|
||||||
expect(meta.isComponent).toBe(true);
|
expect(meta.isComponent).toBe(true);
|
||||||
@ -52,6 +52,17 @@ export function main() {
|
|||||||
expect(value.endsWith(expectedEndValue)).toBe(true);
|
expect(value.endsWith(expectedEndValue)).toBe(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should throw when the moduleId is not a string',
|
||||||
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
|
expect(() => resolver.getDirectiveMetadata(ComponentWithInvalidModuleId))
|
||||||
|
.toThrowError(
|
||||||
|
`moduleId should be a string in "ComponentWithInvalidModuleId". See` +
|
||||||
|
` https://goo.gl/wIDDiL for more information.\n` +
|
||||||
|
`If you're using Webpack you should inline the template and the styles, see` +
|
||||||
|
` https://goo.gl/X2J8zc.`);
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should throw when metadata is incorrectly typed',
|
it('should throw when metadata is incorrectly typed',
|
||||||
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
|
||||||
expect(() => resolver.getDirectiveMetadata(MalformedStylesComponent))
|
expect(() => resolver.getDirectiveMetadata(MalformedStylesComponent))
|
||||||
@ -175,18 +186,14 @@ export function main() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: 'a-directive'})
|
|
||||||
class ADirective {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Directive({selector: 'someSelector'})
|
|
||||||
class SomeDirective {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({selector: 'someComponent', template: ''})
|
@Component({selector: 'someComponent', template: ''})
|
||||||
class ComponentWithoutModuleId {
|
class ComponentWithoutModuleId {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'someComponent', template: '', moduleId: <any>0})
|
||||||
|
class ComponentWithInvalidModuleId {
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'someSelector',
|
selector: 'someSelector',
|
||||||
inputs: ['someProp'],
|
inputs: ['someProp'],
|
||||||
|
@ -91,6 +91,7 @@ export function main() {
|
|||||||
expect(s('one[attr$="value"] {}', 'a')).toEqual('one[attr$="value"][a] {}');
|
expect(s('one[attr$="value"] {}', 'a')).toEqual('one[attr$="value"][a] {}');
|
||||||
expect(s('one[attr*="value"] {}', 'a')).toEqual('one[attr*="value"][a] {}');
|
expect(s('one[attr*="value"] {}', 'a')).toEqual('one[attr*="value"][a] {}');
|
||||||
expect(s('one[attr|="value"] {}', 'a')).toEqual('one[attr|="value"][a] {}');
|
expect(s('one[attr|="value"] {}', 'a')).toEqual('one[attr|="value"][a] {}');
|
||||||
|
expect(s('one[attr~="value"] {}', 'a')).toEqual('one[attr~="value"][a] {}');
|
||||||
expect(s('one[attr] {}', 'a')).toEqual('one[attr][a] {}');
|
expect(s('one[attr] {}', 'a')).toEqual('one[attr][a] {}');
|
||||||
expect(s('[is="one"] {}', 'a')).toEqual('[is="one"][a] {}');
|
expect(s('[is="one"] {}', 'a')).toEqual('[is="one"][a] {}');
|
||||||
});
|
});
|
||||||
|
@ -262,7 +262,7 @@ export function main() {
|
|||||||
.toThrowError(`Template parse errors:
|
.toThrowError(`Template parse errors:
|
||||||
Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
|
Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
|
||||||
1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module.
|
1. If 'my-component' is an Angular component and it has 'invalidProp' input, then verify that it is part of this module.
|
||||||
2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message.
|
2. If 'my-component' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message.
|
||||||
("<my-component [ERROR ->][invalidProp]="bar"></my-component>"): TestComp@0:14`);
|
("<my-component [ERROR ->][invalidProp]="bar"></my-component>"): TestComp@0:14`);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -270,14 +270,14 @@ Can't bind to 'invalidProp' since it isn't a known property of 'my-component'.
|
|||||||
expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors:
|
expect(() => parse('<unknown></unknown>', [])).toThrowError(`Template parse errors:
|
||||||
'unknown' is not a known element:
|
'unknown' is not a known element:
|
||||||
1. If 'unknown' is an Angular component, then verify that it is part of this module.
|
1. If 'unknown' is an Angular component, then verify that it is part of this module.
|
||||||
2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`);
|
2. If 'unknown' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<unknown></unknown>"): TestComp@0:0`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error when binding to an unknown custom element w/o bindings', () => {
|
it('should throw error when binding to an unknown custom element w/o bindings', () => {
|
||||||
expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors:
|
expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors:
|
||||||
'un-known' is not a known element:
|
'un-known' is not a known element:
|
||||||
1. If 'un-known' is an Angular component, then verify that it is part of this module.
|
1. If 'un-known' is an Angular component, then verify that it is part of this module.
|
||||||
2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`);
|
2. If 'un-known' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schemas' of this component to suppress this message. ("[ERROR ->]<un-known></un-known>"): TestComp@0:0`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
import {DirectiveResolver} from '@angular/compiler';
|
import {DirectiveResolver} from '@angular/compiler';
|
||||||
import {AnimationEntryMetadata, Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef} from '@angular/core';
|
import {AnimationEntryMetadata, Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef} from '@angular/core';
|
||||||
|
|
||||||
import {Map} from './facade/collection';
|
import {isPresent} from './facade/lang';
|
||||||
import {isArray, isPresent} from './facade/lang';
|
|
||||||
import {ViewMetadata} from './private_import_core';
|
import {ViewMetadata} from './private_import_core';
|
||||||
|
|
||||||
|
|
||||||
@ -156,7 +155,7 @@ function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void {
|
|||||||
if (!isPresent(tree)) return;
|
if (!isPresent(tree)) return;
|
||||||
for (var i = 0; i < tree.length; i++) {
|
for (var i = 0; i < tree.length; i++) {
|
||||||
var item = resolveForwardRef(tree[i]);
|
var item = resolveForwardRef(tree[i]);
|
||||||
if (isArray(item)) {
|
if (Array.isArray(item)) {
|
||||||
flattenArray(item, out);
|
flattenArray(item, out);
|
||||||
} else {
|
} else {
|
||||||
out.push(item);
|
out.push(item);
|
||||||
|
@ -9,18 +9,12 @@
|
|||||||
import {NgModuleResolver} from '@angular/compiler';
|
import {NgModuleResolver} from '@angular/compiler';
|
||||||
import {Compiler, Injectable, Injector, NgModule, Type} from '@angular/core';
|
import {Compiler, Injectable, Injector, NgModule, Type} from '@angular/core';
|
||||||
|
|
||||||
import {Map} from './facade/collection';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MockNgModuleResolver extends NgModuleResolver {
|
export class MockNgModuleResolver extends NgModuleResolver {
|
||||||
private _ngModules = new Map<Type<any>, NgModule>();
|
private _ngModules = new Map<Type<any>, NgModule>();
|
||||||
|
|
||||||
constructor(private _injector: Injector) { super(); }
|
constructor(private _injector: Injector) { super(); }
|
||||||
|
|
||||||
private get _compiler(): Compiler { return this._injector.get(Compiler); }
|
|
||||||
|
|
||||||
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overrides the {@link NgModule} for a module.
|
* Overrides the {@link NgModule} for a module.
|
||||||
*/
|
*/
|
||||||
@ -36,10 +30,10 @@ export class MockNgModuleResolver extends NgModuleResolver {
|
|||||||
* `NgModuleResolver`, see `setNgModule`.
|
* `NgModuleResolver`, see `setNgModule`.
|
||||||
*/
|
*/
|
||||||
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
|
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
|
||||||
var metadata = this._ngModules.get(type);
|
return this._ngModules.get(type) || super.resolve(type, throwIfNotFound);
|
||||||
if (!metadata) {
|
|
||||||
metadata = super.resolve(type, throwIfNotFound);
|
|
||||||
}
|
|
||||||
return metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get _compiler(): Compiler { return this._injector.get(Compiler); }
|
||||||
|
|
||||||
|
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@
|
|||||||
import {PipeResolver} from '@angular/compiler';
|
import {PipeResolver} from '@angular/compiler';
|
||||||
import {Compiler, Injectable, Injector, Pipe, Type} from '@angular/core';
|
import {Compiler, Injectable, Injector, Pipe, Type} from '@angular/core';
|
||||||
|
|
||||||
import {Map} from './facade/collection';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class MockPipeResolver extends PipeResolver {
|
export class MockPipeResolver extends PipeResolver {
|
||||||
private _pipes = new Map<Type<any>, Pipe>();
|
private _pipes = new Map<Type<any>, Pipe>();
|
||||||
|
@ -7,11 +7,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {ResourceLoader} from '@angular/compiler';
|
import {ResourceLoader} from '@angular/compiler';
|
||||||
import {ListWrapper, Map} from './facade/collection';
|
import {ListWrapper} from './facade/collection';
|
||||||
import {isBlank, normalizeBlank} from './facade/lang';
|
import {isBlank, normalizeBlank} from './facade/lang';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mock implementation of {@link ResourceLoader} that allows outgoing requests to be mocked
|
* A mock implementation of {@link ResourceLoader} that allows outgoing requests to be mocked
|
||||||
* and responded to within a single test, without going to the network.
|
* and responded to within a single test, without going to the network.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@angular/core",
|
"name": "@angular/core",
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
"version": "0.0.0-PLACEHOLDER",
|
||||||
"description": "Angular 2 core",
|
"description": "Angular - the core framework",
|
||||||
"main": "bundles/core.umd.js",
|
"main": "bundles/core.umd.js",
|
||||||
"module": "index.js",
|
"module": "index.js",
|
||||||
"typings": "index.d.ts",
|
"typings": "index.d.ts",
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListWrapper, Map, StringMapWrapper} from '../facade/collection';
|
import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
|
|
||||||
import {AnimationPlayer} from './animation_player';
|
import {AnimationPlayer} from './animation_player';
|
||||||
@ -47,12 +47,12 @@ export class ViewAnimationMap {
|
|||||||
getAllPlayers(): AnimationPlayer[] { return this._allPlayers; }
|
getAllPlayers(): AnimationPlayer[] { return this._allPlayers; }
|
||||||
|
|
||||||
remove(element: any, animationName: string): void {
|
remove(element: any, animationName: string): void {
|
||||||
var playersByAnimation = this._map.get(element);
|
const playersByAnimation = this._map.get(element);
|
||||||
if (isPresent(playersByAnimation)) {
|
if (playersByAnimation) {
|
||||||
var player = playersByAnimation[animationName];
|
const player = playersByAnimation[animationName];
|
||||||
delete playersByAnimation[animationName];
|
delete playersByAnimation[animationName];
|
||||||
var index = this._allPlayers.indexOf(player);
|
const index = this._allPlayers.indexOf(player);
|
||||||
ListWrapper.removeAt(this._allPlayers, index);
|
this._allPlayers.splice(index, 1);
|
||||||
|
|
||||||
if (StringMapWrapper.isEmpty(playersByAnimation)) {
|
if (StringMapWrapper.isEmpty(playersByAnimation)) {
|
||||||
this._map.delete(element);
|
this._map.delete(element);
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {isPromise} from '../src/facade/lang';
|
import {isPromise} from '../src/util/lang';
|
||||||
|
|
||||||
import {Inject, Injectable, OpaqueToken, Optional} from './di';
|
import {Inject, Injectable, OpaqueToken, Optional} from './di';
|
||||||
|
|
||||||
|
@ -14,7 +14,6 @@ import {LOCALE_ID} from './i18n/tokens';
|
|||||||
import {Compiler} from './linker/compiler';
|
import {Compiler} from './linker/compiler';
|
||||||
import {ViewUtils} from './linker/view_utils';
|
import {ViewUtils} from './linker/view_utils';
|
||||||
import {NgModule} from './metadata';
|
import {NgModule} from './metadata';
|
||||||
import {Type} from './type';
|
|
||||||
|
|
||||||
export function _iterableDiffersFactory() {
|
export function _iterableDiffersFactory() {
|
||||||
return defaultIterableDiffers;
|
return defaultIterableDiffers;
|
||||||
|
@ -9,7 +9,8 @@
|
|||||||
import {ErrorHandler} from '../src/error_handler';
|
import {ErrorHandler} from '../src/error_handler';
|
||||||
import {ListWrapper} from '../src/facade/collection';
|
import {ListWrapper} from '../src/facade/collection';
|
||||||
import {unimplemented} from '../src/facade/errors';
|
import {unimplemented} from '../src/facade/errors';
|
||||||
import {isBlank, isPresent, isPromise, stringify} from '../src/facade/lang';
|
import {stringify} from '../src/facade/lang';
|
||||||
|
import {isPromise} from '../src/util/lang';
|
||||||
|
|
||||||
import {ApplicationInitStatus} from './application_init';
|
import {ApplicationInitStatus} from './application_init';
|
||||||
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
|
import {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
|
||||||
@ -25,9 +26,9 @@ import {Testability, TestabilityRegistry} from './testability/testability';
|
|||||||
import {Type} from './type';
|
import {Type} from './type';
|
||||||
import {NgZone} from './zone/ng_zone';
|
import {NgZone} from './zone/ng_zone';
|
||||||
|
|
||||||
var _devMode: boolean = true;
|
let _devMode: boolean = true;
|
||||||
var _runModeLocked: boolean = false;
|
let _runModeLocked: boolean = false;
|
||||||
var _platform: PlatformRef;
|
let _platform: PlatformRef;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disable Angular's development mode, which turns off assertions and other
|
* Disable Angular's development mode, which turns off assertions and other
|
||||||
@ -66,13 +67,13 @@ export function isDevMode(): boolean {
|
|||||||
* @experimental APIs related to application bootstrap are currently under review.
|
* @experimental APIs related to application bootstrap are currently under review.
|
||||||
*/
|
*/
|
||||||
export function createPlatform(injector: Injector): PlatformRef {
|
export function createPlatform(injector: Injector): PlatformRef {
|
||||||
if (isPresent(_platform) && !_platform.destroyed) {
|
if (_platform && !_platform.destroyed) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'There can be only one platform. Destroy the previous one to create a new one.');
|
'There can be only one platform. Destroy the previous one to create a new one.');
|
||||||
}
|
}
|
||||||
_platform = injector.get(PlatformRef);
|
_platform = injector.get(PlatformRef);
|
||||||
const inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
|
const inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
|
||||||
if (isPresent(inits)) inits.forEach(init => init());
|
if (inits) inits.forEach(init => init());
|
||||||
return _platform;
|
return _platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +107,17 @@ export function createPlatformFactory(
|
|||||||
* @experimental APIs related to application bootstrap are currently under review.
|
* @experimental APIs related to application bootstrap are currently under review.
|
||||||
*/
|
*/
|
||||||
export function assertPlatform(requiredToken: any): PlatformRef {
|
export function assertPlatform(requiredToken: any): PlatformRef {
|
||||||
var platform = getPlatform();
|
const platform = getPlatform();
|
||||||
if (isBlank(platform)) {
|
|
||||||
|
if (!platform) {
|
||||||
throw new Error('No platform exists!');
|
throw new Error('No platform exists!');
|
||||||
}
|
}
|
||||||
if (isPresent(platform) && isBlank(platform.injector.get(requiredToken, null))) {
|
|
||||||
|
if (!platform.injector.get(requiredToken, null)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'A platform with a different configuration has been created. Please destroy it first.');
|
'A platform with a different configuration has been created. Please destroy it first.');
|
||||||
}
|
}
|
||||||
|
|
||||||
return platform;
|
return platform;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,7 +127,7 @@ export function assertPlatform(requiredToken: any): PlatformRef {
|
|||||||
* @experimental APIs related to application bootstrap are currently under review.
|
* @experimental APIs related to application bootstrap are currently under review.
|
||||||
*/
|
*/
|
||||||
export function destroyPlatform(): void {
|
export function destroyPlatform(): void {
|
||||||
if (isPresent(_platform) && !_platform.destroyed) {
|
if (_platform && !_platform.destroyed) {
|
||||||
_platform.destroy();
|
_platform.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -134,7 +138,7 @@ export function destroyPlatform(): void {
|
|||||||
* @experimental APIs related to application bootstrap are currently under review.
|
* @experimental APIs related to application bootstrap are currently under review.
|
||||||
*/
|
*/
|
||||||
export function getPlatform(): PlatformRef {
|
export function getPlatform(): PlatformRef {
|
||||||
return isPresent(_platform) && !_platform.destroyed ? _platform : null;
|
return _platform && !_platform.destroyed ? _platform : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -223,9 +227,9 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () =
|
|||||||
// rethrow as the exception handler might not do it
|
// rethrow as the exception handler might not do it
|
||||||
throw e;
|
throw e;
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorHandler.handleError(e);
|
errorHandler.handleError(e);
|
||||||
// rethrow as the exception handler might not do it
|
// rethrow as the exception handler might not do it
|
||||||
@ -237,7 +241,6 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () =
|
|||||||
export class PlatformRef_ extends PlatformRef {
|
export class PlatformRef_ extends PlatformRef {
|
||||||
private _modules: NgModuleRef<any>[] = [];
|
private _modules: NgModuleRef<any>[] = [];
|
||||||
private _destroyListeners: Function[] = [];
|
private _destroyListeners: Function[] = [];
|
||||||
|
|
||||||
private _destroyed: boolean = false;
|
private _destroyed: boolean = false;
|
||||||
|
|
||||||
constructor(private _injector: Injector) { super(); }
|
constructor(private _injector: Injector) { super(); }
|
||||||
@ -252,8 +255,8 @@ export class PlatformRef_ extends PlatformRef {
|
|||||||
if (this._destroyed) {
|
if (this._destroyed) {
|
||||||
throw new Error('The platform has already been destroyed!');
|
throw new Error('The platform has already been destroyed!');
|
||||||
}
|
}
|
||||||
ListWrapper.clone(this._modules).forEach((app) => app.destroy());
|
this._modules.slice().forEach(module => module.destroy());
|
||||||
this._destroyListeners.forEach((dispose) => dispose());
|
this._destroyListeners.forEach(listener => listener());
|
||||||
this._destroyed = true;
|
this._destroyed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,7 +303,7 @@ export class PlatformRef_ extends PlatformRef {
|
|||||||
componentFactoryCallback?: any): Promise<NgModuleRef<M>> {
|
componentFactoryCallback?: any): Promise<NgModuleRef<M>> {
|
||||||
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
|
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
|
||||||
const compiler = compilerFactory.createCompiler(
|
const compiler = compilerFactory.createCompiler(
|
||||||
compilerOptions instanceof Array ? compilerOptions : [compilerOptions]);
|
Array.isArray(compilerOptions) ? compilerOptions : [compilerOptions]);
|
||||||
|
|
||||||
// ugly internal api hack: generate host component factories for all declared components and
|
// ugly internal api hack: generate host component factories for all declared components and
|
||||||
// pass the factories into the callback - this is used by UpdateAdapter to get hold of all
|
// pass the factories into the callback - this is used by UpdateAdapter to get hold of all
|
||||||
@ -423,10 +426,10 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||||||
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
|
componentFactory = this._componentFactoryResolver.resolveComponentFactory(componentOrFactory);
|
||||||
}
|
}
|
||||||
this._rootComponentTypes.push(componentFactory.componentType);
|
this._rootComponentTypes.push(componentFactory.componentType);
|
||||||
var compRef = componentFactory.create(this._injector, [], componentFactory.selector);
|
const compRef = componentFactory.create(this._injector, [], componentFactory.selector);
|
||||||
compRef.onDestroy(() => { this._unloadComponent(compRef); });
|
compRef.onDestroy(() => { this._unloadComponent(compRef); });
|
||||||
var testability = compRef.injector.get(Testability, null);
|
const testability = compRef.injector.get(Testability, null);
|
||||||
if (isPresent(testability)) {
|
if (testability) {
|
||||||
compRef.injector.get(TestabilityRegistry)
|
compRef.injector.get(TestabilityRegistry)
|
||||||
.registerApplication(compRef.location.nativeElement, testability);
|
.registerApplication(compRef.location.nativeElement, testability);
|
||||||
}
|
}
|
||||||
@ -453,7 +456,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_unloadComponent(componentRef: ComponentRef<any>): void {
|
_unloadComponent(componentRef: ComponentRef<any>): void {
|
||||||
if (!ListWrapper.contains(this._rootComponents, componentRef)) {
|
if (this._rootComponents.indexOf(componentRef) == -1) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.unregisterChangeDetector(componentRef.changeDetectorRef);
|
this.unregisterChangeDetector(componentRef.changeDetectorRef);
|
||||||
@ -465,7 +468,7 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||||||
throw new Error('ApplicationRef.tick is called recursively');
|
throw new Error('ApplicationRef.tick is called recursively');
|
||||||
}
|
}
|
||||||
|
|
||||||
var s = ApplicationRef_._tickScope();
|
const scope = ApplicationRef_._tickScope();
|
||||||
try {
|
try {
|
||||||
this._runningTick = true;
|
this._runningTick = true;
|
||||||
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
|
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
|
||||||
@ -474,13 +477,13 @@ export class ApplicationRef_ extends ApplicationRef {
|
|||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
this._runningTick = false;
|
this._runningTick = false;
|
||||||
wtfLeave(s);
|
wtfLeave(scope);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy() {
|
ngOnDestroy() {
|
||||||
// TODO(alxhub): Dispose of the NgZone.
|
// TODO(alxhub): Dispose of the NgZone.
|
||||||
ListWrapper.clone(this._rootComponents).forEach((ref) => ref.destroy());
|
this._rootComponents.slice().forEach((component) => component.destroy());
|
||||||
}
|
}
|
||||||
|
|
||||||
get componentTypes(): Type<any>[] { return this._rootComponentTypes; }
|
get componentTypes(): Type<any>[] { return this._rootComponentTypes; }
|
||||||
|
@ -34,7 +34,7 @@ export function _appIdRandomProviderFactory() {
|
|||||||
export const APP_ID_RANDOM_PROVIDER = {
|
export const APP_ID_RANDOM_PROVIDER = {
|
||||||
provide: APP_ID,
|
provide: APP_ID,
|
||||||
useFactory: _appIdRandomProviderFactory,
|
useFactory: _appIdRandomProviderFactory,
|
||||||
deps: <any[]>[]
|
deps: <any[]>[],
|
||||||
};
|
};
|
||||||
|
|
||||||
function _randomChar(): string {
|
function _randomChar(): string {
|
||||||
|
@ -39,6 +39,7 @@ import * as reflector_reader from './reflection/reflector_reader';
|
|||||||
import * as reflection_types from './reflection/types';
|
import * as reflection_types from './reflection/types';
|
||||||
import * as api from './render/api';
|
import * as api from './render/api';
|
||||||
import * as decorators from './util/decorators';
|
import * as decorators from './util/decorators';
|
||||||
|
import {isPromise} from './util/lang';
|
||||||
|
|
||||||
export var __core_private__: {
|
export var __core_private__: {
|
||||||
isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy,
|
isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy,
|
||||||
@ -118,9 +119,11 @@ export var __core_private__: {
|
|||||||
ANY_STATE: typeof ANY_STATE_,
|
ANY_STATE: typeof ANY_STATE_,
|
||||||
DEFAULT_STATE: typeof DEFAULT_STATE_,
|
DEFAULT_STATE: typeof DEFAULT_STATE_,
|
||||||
EMPTY_STATE: typeof EMPTY_STATE_,
|
EMPTY_STATE: typeof EMPTY_STATE_,
|
||||||
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_, _ComponentStillLoadingError?: ComponentStillLoadingError
|
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_,
|
||||||
ComponentStillLoadingError: typeof ComponentStillLoadingError
|
_ComponentStillLoadingError?: ComponentStillLoadingError,
|
||||||
} = {
|
ComponentStillLoadingError: typeof ComponentStillLoadingError,
|
||||||
|
isPromise: typeof isPromise
|
||||||
|
} = {
|
||||||
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
|
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
|
||||||
ChangeDetectorStatus: constants.ChangeDetectorStatus,
|
ChangeDetectorStatus: constants.ChangeDetectorStatus,
|
||||||
CHANGE_DETECTION_STRATEGY_VALUES: constants.CHANGE_DETECTION_STRATEGY_VALUES,
|
CHANGE_DETECTION_STRATEGY_VALUES: constants.CHANGE_DETECTION_STRATEGY_VALUES,
|
||||||
@ -185,5 +188,6 @@ export var __core_private__: {
|
|||||||
DEFAULT_STATE: DEFAULT_STATE_,
|
DEFAULT_STATE: DEFAULT_STATE_,
|
||||||
EMPTY_STATE: EMPTY_STATE_,
|
EMPTY_STATE: EMPTY_STATE_,
|
||||||
FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
|
FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
|
||||||
ComponentStillLoadingError: ComponentStillLoadingError
|
ComponentStillLoadingError: ComponentStillLoadingError,
|
||||||
|
isPromise: isPromise
|
||||||
};
|
};
|
||||||
|
@ -22,6 +22,23 @@ class _NullInjector implements Injector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @whatItDoes Injector interface
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* const injector: Injector = ...;
|
||||||
|
* injector.get(...);
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/injector_spec.ts region='Injector'}
|
||||||
|
*
|
||||||
|
* `Injector` returns itself when given `Injector` as a token:
|
||||||
|
* {@example core/di/ts/injector_spec.ts region='injectInjector'}
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class Injector {
|
export abstract class Injector {
|
||||||
@ -34,23 +51,6 @@ export abstract class Injector {
|
|||||||
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
|
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
|
||||||
* Injector.THROW_IF_NOT_FOUND is given
|
* Injector.THROW_IF_NOT_FOUND is given
|
||||||
* - Returns the `notFoundValue` otherwise
|
* - Returns the `notFoundValue` otherwise
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/HeXSHg?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* var injector = ReflectiveInjector.resolveAndCreate([
|
|
||||||
* {provide: "validToken", useValue: "Value"}
|
|
||||||
* ]);
|
|
||||||
* expect(injector.get("validToken")).toEqual("Value");
|
|
||||||
* expect(() => injector.get("invalidToken")).toThrowError();
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* `Injector` returns itself when given `Injector` as a token.
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* var injector = ReflectiveInjector.resolveAndCreate([]);
|
|
||||||
* expect(injector.get(Injector)).toBe(injector);
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
get(token: any, notFoundValue?: any): any { return unimplemented(); }
|
get(token: any, notFoundValue?: any): any { return unimplemented(); }
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {stringify} from '../facade/lang';
|
|
||||||
import {makeParamDecorator} from '../util/decorators';
|
import {makeParamDecorator} from '../util/decorators';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,45 +15,29 @@ import {makeParamDecorator} from '../util/decorators';
|
|||||||
*/
|
*/
|
||||||
export interface InjectDecorator {
|
export interface InjectDecorator {
|
||||||
/**
|
/**
|
||||||
* A parameter metadata that specifies a dependency.
|
* @whatItDoes A parameter decorator that specifies a dependency.
|
||||||
*
|
* @howToUse
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/6uHYJK?p=preview))
|
* ```
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* class Engine {}
|
|
||||||
*
|
|
||||||
* @Injectable()
|
* @Injectable()
|
||||||
* class Car {
|
* class Car {
|
||||||
* engine;
|
* constructor(@Inject("MyEngine") public engine:Engine) {}
|
||||||
* constructor(@Inject("MyEngine") engine:Engine) {
|
|
||||||
* this.engine = engine;
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
|
||||||
* var injector = Injector.resolveAndCreate([
|
|
||||||
* {provide: "MyEngine", useClass: Engine},
|
|
||||||
* Car
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
|
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/metadata_spec.ts region='Inject'}
|
||||||
|
*
|
||||||
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the
|
* When `@Inject()` is not present, {@link Injector} will use the type annotation of the
|
||||||
* parameter.
|
* parameter.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* ```typescript
|
* {@example core/di/ts/metadata_spec.ts region='InjectWithoutDecorator'}
|
||||||
* class Engine {}
|
|
||||||
*
|
*
|
||||||
* @Injectable()
|
|
||||||
* class Car {
|
|
||||||
* constructor(public engine: Engine) {} //same as constructor(@Inject(Engine) engine:Engine)
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var injector = Injector.resolveAndCreate([Engine, Car]);
|
|
||||||
* expect(injector.get(Car).engine instanceof Engine).toBe(true);
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(token: any): any;
|
(token: any): any;
|
||||||
@ -84,25 +67,23 @@ export const Inject: InjectDecorator = makeParamDecorator('Inject', [['token', u
|
|||||||
*/
|
*/
|
||||||
export interface OptionalDecorator {
|
export interface OptionalDecorator {
|
||||||
/**
|
/**
|
||||||
* A parameter metadata that marks a dependency as optional. {@link Injector} provides `null` if
|
* @whatItDoes A parameter metadata that marks a dependency as optional.
|
||||||
* the dependency is not found.
|
* {@link Injector} provides `null` if the dependency is not found.
|
||||||
*
|
* @howToUse
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/AsryOm?p=preview))
|
* ```
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* class Engine {}
|
|
||||||
*
|
|
||||||
* @Injectable()
|
* @Injectable()
|
||||||
* class Car {
|
* class Car {
|
||||||
* engine;
|
* constructor(@Optional() public engine:Engine) {}
|
||||||
* constructor(@Optional() engine:Engine) {
|
|
||||||
* this.engine = engine;
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
*
|
|
||||||
* var injector = Injector.resolveAndCreate([Car]);
|
|
||||||
* expect(injector.get(Car).engine).toBeNull();
|
|
||||||
* ```
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/metadata_spec.ts region='Optional'}
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(): any;
|
(): any;
|
||||||
@ -131,35 +112,25 @@ export const Optional: OptionalDecorator = makeParamDecorator('Optional', []);
|
|||||||
*/
|
*/
|
||||||
export interface InjectableDecorator {
|
export interface InjectableDecorator {
|
||||||
/**
|
/**
|
||||||
* A marker metadata that marks a class as available to {@link Injector} for creation.
|
* @whatItDoes A marker metadata that marks a class as available to {@link Injector} for creation.
|
||||||
*
|
* @howToUse
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/Wk4DMQ?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Injectable()
|
|
||||||
* class UsefulService {}
|
|
||||||
*
|
|
||||||
* @Injectable()
|
|
||||||
* class NeedsService {
|
|
||||||
* constructor(public service:UsefulService) {}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
|
|
||||||
* expect(injector.get(NeedsService).service instanceof UsefulService).toBe(true);
|
|
||||||
* ```
|
* ```
|
||||||
|
* @Injectable()
|
||||||
|
* class Car {}
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/metadata_spec.ts region='Injectable'}
|
||||||
|
*
|
||||||
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
|
* {@link Injector} will throw {@link NoAnnotationError} when trying to instantiate a class that
|
||||||
* does not have `@Injectable` marker, as shown in the example below.
|
* does not have `@Injectable` marker, as shown in the example below.
|
||||||
*
|
*
|
||||||
* ```typescript
|
* {@example core/di/ts/metadata_spec.ts region='InjectableThrows'}
|
||||||
* class UsefulService {}
|
|
||||||
*
|
*
|
||||||
* class NeedsService {
|
|
||||||
* constructor(public service:UsefulService) {}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var injector = Injector.resolveAndCreate([NeedsService, UsefulService]);
|
|
||||||
* expect(() => injector.get(NeedsService)).toThrowError();
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(): any;
|
(): any;
|
||||||
@ -188,31 +159,22 @@ export const Injectable: InjectableDecorator = makeParamDecorator('Injectable',
|
|||||||
*/
|
*/
|
||||||
export interface SelfDecorator {
|
export interface SelfDecorator {
|
||||||
/**
|
/**
|
||||||
* Specifies that an {@link Injector} should retrieve a dependency only from itself.
|
* @whatItDoes Specifies that an {@link Injector} should retrieve a dependency only from itself.
|
||||||
*
|
* @howToUse
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/NeagAg?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* class Dependency {
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Injectable()
|
|
||||||
* class NeedsDependency {
|
|
||||||
* dependency;
|
|
||||||
* constructor(@Self() dependency:Dependency) {
|
|
||||||
* this.dependency = dependency;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
|
|
||||||
* var nd = inj.get(NeedsDependency);
|
|
||||||
*
|
|
||||||
* expect(nd.dependency instanceof Dependency).toBe(true);
|
|
||||||
*
|
|
||||||
* var inj = Injector.resolveAndCreate([Dependency]);
|
|
||||||
* var child = inj.resolveAndCreateChild([NeedsDependency]);
|
|
||||||
* expect(() => child.get(NeedsDependency)).toThrowError();
|
|
||||||
* ```
|
* ```
|
||||||
|
* @Injectable()
|
||||||
|
* class Car {
|
||||||
|
* constructor(@Self() public engine:Engine) {}
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/metadata_spec.ts region='Self'}
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(): any;
|
(): any;
|
||||||
@ -242,29 +204,22 @@ export const Self: SelfDecorator = makeParamDecorator('Self', []);
|
|||||||
*/
|
*/
|
||||||
export interface SkipSelfDecorator {
|
export interface SkipSelfDecorator {
|
||||||
/**
|
/**
|
||||||
* Specifies that the dependency resolution should start from the parent injector.
|
* @whatItDoes Specifies that the dependency resolution should start from the parent injector.
|
||||||
*
|
* @howToUse
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/Wchdzb?p=preview))
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* class Dependency {
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Injectable()
|
|
||||||
* class NeedsDependency {
|
|
||||||
* dependency;
|
|
||||||
* constructor(@SkipSelf() dependency:Dependency) {
|
|
||||||
* this.dependency = dependency;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* var parent = Injector.resolveAndCreate([Dependency]);
|
|
||||||
* var child = parent.resolveAndCreateChild([NeedsDependency]);
|
|
||||||
* expect(child.get(NeedsDependency).dependency instanceof Depedency).toBe(true);
|
|
||||||
*
|
|
||||||
* var inj = Injector.resolveAndCreate([Dependency, NeedsDependency]);
|
|
||||||
* expect(() => inj.get(NeedsDependency)).toThrowError();
|
|
||||||
* ```
|
* ```
|
||||||
|
* @Injectable()
|
||||||
|
* class Car {
|
||||||
|
* constructor(@SkipSelf() public engine:Engine) {}
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
|
* ### Example
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/metadata_spec.ts region='SkipSelf'}
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(): any;
|
(): any;
|
||||||
@ -293,56 +248,23 @@ export const SkipSelf: SkipSelfDecorator = makeParamDecorator('SkipSelf', []);
|
|||||||
*/
|
*/
|
||||||
export interface HostDecorator {
|
export interface HostDecorator {
|
||||||
/**
|
/**
|
||||||
* Specifies that an injector should retrieve a dependency from any injector until reaching the
|
* @whatItDoes Specifies that an injector should retrieve a dependency from any injector until
|
||||||
* closest host.
|
* reaching the host element of the current component.
|
||||||
*
|
* @howToUse
|
||||||
* In Angular, a component element is automatically declared as a host for all the injectors in
|
* ```
|
||||||
* its view.
|
* @Injectable()
|
||||||
*
|
* class Car {
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/GX79pV?p=preview))
|
* constructor(@Host() public engine:Engine) {}
|
||||||
*
|
|
||||||
* In the following example `App` contains `ParentCmp`, which contains `ChildDirective`.
|
|
||||||
* So `ParentCmp` is the host of `ChildDirective`.
|
|
||||||
*
|
|
||||||
* `ChildDirective` depends on two services: `HostService` and `OtherService`.
|
|
||||||
* `HostService` is defined at `ParentCmp`, and `OtherService` is defined at `App`.
|
|
||||||
*
|
|
||||||
*```typescript
|
|
||||||
* class OtherService {}
|
|
||||||
* class HostService {}
|
|
||||||
*
|
|
||||||
* @Directive({
|
|
||||||
* selector: 'child-directive'
|
|
||||||
* })
|
|
||||||
* class ChildDirective {
|
|
||||||
* constructor(@Optional() @Host() os:OtherService, @Optional() @Host() hs:HostService){
|
|
||||||
* console.log("os is null", os);
|
|
||||||
* console.log("hs is NOT null", hs);
|
|
||||||
* }
|
|
||||||
* }
|
* }
|
||||||
|
* ```
|
||||||
*
|
*
|
||||||
* @Component({
|
* @description
|
||||||
* selector: 'parent-cmp',
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
* providers: [HostService],
|
*
|
||||||
* template: `
|
* ### Example
|
||||||
* Dir: <child-directive></child-directive>
|
*
|
||||||
* `,
|
* {@example core/di/ts/metadata_spec.ts region='Host'}
|
||||||
* directives: [ChildDirective]
|
|
||||||
* })
|
|
||||||
* class ParentCmp {
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* providers: [OtherService],
|
|
||||||
* template: `
|
|
||||||
* Parent: <parent-cmp></parent-cmp>
|
|
||||||
* `,
|
|
||||||
* directives: [ParentCmp]
|
|
||||||
* })
|
|
||||||
* class App {
|
|
||||||
* }
|
|
||||||
*```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
(): any;
|
(): any;
|
||||||
|
@ -9,50 +9,45 @@
|
|||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link Injector} to return an instance of `Type` when `Type' is used as token.
|
* @whatItDoes Configures the {@link Injector} to return an instance of `Type` when `Type' is used
|
||||||
|
* as token.
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* @Injectable()
|
||||||
|
* class MyService {}
|
||||||
|
*
|
||||||
|
* const provider: TypeProvider = MyService;
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* Create an instance by invoking the `new` operator and supplying additional arguments.
|
* Create an instance by invoking the `new` operator and supplying additional arguments.
|
||||||
* This form is a short form of `TypeProvider`;
|
* This form is a short form of `TypeProvider`;
|
||||||
*
|
*
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* @Injectable()
|
|
||||||
* class Greeting {
|
|
||||||
* text: 'Hello';
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* @Injectable()
|
* {@example core/di/ts/provider_spec.ts region='TypeProvider'}
|
||||||
* class MyClass {
|
|
||||||
* greeting:string;
|
|
||||||
* constructor(greeting: Greeting) {
|
|
||||||
* this.greeting = greeting.text;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* Greeting, // Shorthand for { provide: Greeting, useClass: Greeting }
|
|
||||||
* MyClass // Shorthand for { provide: MyClass, useClass: MyClass }
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const myClass: MyClass = injector.get(MyClass);
|
|
||||||
* expect(myClass.greeting).toEqual('Hello');
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface TypeProvider extends Type<any> {}
|
export interface TypeProvider extends Type<any> {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link Injector} to return a value for a token.
|
* @whatItDoes Configures the {@link Injector} to return a value for a token.
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* const provider: ValueProvider = {provide: 'someToken', useValue: 'someValue'};
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* {provide: String, useValue: 'Hello'}
|
|
||||||
* ]);
|
|
||||||
*
|
*
|
||||||
* expect(injector.get(String)).toEqual('Hello');
|
* {@example core/di/ts/provider_spec.ts region='ValueProvider'}
|
||||||
* ```
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface ValueProvider {
|
export interface ValueProvider {
|
||||||
@ -71,62 +66,31 @@ export interface ValueProvider {
|
|||||||
* providers spread across many files to provide configuration information to a common token.
|
* providers spread across many files to provide configuration information to a common token.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* var locale = new OpaqueToken('local');
|
|
||||||
*
|
*
|
||||||
* const injector = Injector.resolveAndCreate([
|
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||||
* { provide: locale, multi: true, useValue: 'en' },
|
|
||||||
* { provide: locale, multi: true, useValue: 'sk' },
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const locales: string[] = injector.get(locale);
|
|
||||||
* expect(locales).toEqual(['en', 'sk']);
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link Injector} to return an instance of `useClass` for a token.
|
* @whatItDoes Configures the {@link Injector} to return an instance of `useClass` for a token.
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* @Injectable()
|
||||||
|
* class MyService {}
|
||||||
|
*
|
||||||
|
* const provider: ClassProvider = {provide: 'someToken', useClass: MyService};
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* abstract class Shape {
|
|
||||||
* name: string;
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* class Square extends Shape {
|
* {@example core/di/ts/provider_spec.ts region='ClassProvider'}
|
||||||
* name = 'square';
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* const injector = Injector.resolveAndCreate([
|
* Note that following two providers are not equal:
|
||||||
* {provide: Shape, useClass: Square}
|
* {@example core/di/ts/provider_spec.ts region='ClassProviderDifference'}
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const shape: Shape = injector.get(Shape);
|
|
||||||
* expect(shape.name).toEqual('square');
|
|
||||||
* expect(shape instanceof Square).toBe(true);
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Note that following is not equal:
|
|
||||||
* ```javascript
|
|
||||||
* class Greeting {
|
|
||||||
* salutation = 'Hello';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* class FormalGreeting extends Greeting {
|
|
||||||
* salutation = 'Greetings';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* FormalGreeting,
|
|
||||||
* {provide: Greeting, useClass: FormalGreeting}
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* // The injector returns different instances.
|
|
||||||
* // See: {provide: ?, useExisting: ?} if you want the same instance.
|
|
||||||
* expect(injector.get(FormalGreeting)).not.toBe(injector.get(Greeting));
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -146,56 +110,26 @@ export interface ClassProvider {
|
|||||||
* providers spread across many files to provide configuration information to a common token.
|
* providers spread across many files to provide configuration information to a common token.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* abstract class Locale {
|
|
||||||
* name: string;
|
|
||||||
* };
|
|
||||||
*
|
*
|
||||||
* @Injectable()
|
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||||
* class EnLocale extends Locale {
|
|
||||||
* name: 'en';
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* @Injectable()
|
|
||||||
* class SkLocale extends Locale {
|
|
||||||
* name: 'sk';
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* { provide: Locale, useValue: EnLocale, multi: true },
|
|
||||||
* { provide: Locale, useValue: SkLocale, multi: true },
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const locales: Locale[] = injector.get(Locale);
|
|
||||||
* const localeNames: string[] = locals.map((l) => l.name);
|
|
||||||
* expect(localeNames).toEqual(['en', 'sk']);
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link Injector} to return a value of another `useExisting` token.
|
* @whatItDoes Configures the {@link Injector} to return a value of another `useExisting` token.
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* const provider: ExistingProvider = {provide: 'someToken', useExisting: 'someOtherToken'};
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* class Greeting {
|
|
||||||
* salutation = 'Hello';
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* class FormalGreeting extends Greeting {
|
* {@example core/di/ts/provider_spec.ts region='ExistingProvider'}
|
||||||
* salutation = 'Greetings';
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* FormalGreeting,
|
|
||||||
* {provide: Greeting, useExisting: FormalGreeting}
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* expect(injector.get(Greeting).name).toEqual('Hello');
|
|
||||||
* expect(injector.get(FormalGreeting).name).toEqual('Hello');
|
|
||||||
* expect(injector.get(Salutation).name).toBe(injector.get(Greeting));
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface ExistingProvider {
|
export interface ExistingProvider {
|
||||||
@ -214,52 +148,32 @@ export interface ExistingProvider {
|
|||||||
* providers spread across many files to provide configuration information to a common token.
|
* providers spread across many files to provide configuration information to a common token.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* abstract class Locale {
|
|
||||||
* name: string;
|
|
||||||
* };
|
|
||||||
*
|
*
|
||||||
* @Injectable()
|
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||||
* class EnLocale extends Locale {
|
|
||||||
* name: 'en';
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* @Injectable()
|
|
||||||
* class SkLocale extends Locale {
|
|
||||||
* name: 'sk';
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* EnLocale,
|
|
||||||
* SkLocale
|
|
||||||
* { provide: Locale, useExisting: EnLocale, multi: true },
|
|
||||||
* { provide: Locale, useExisting: SkLocale, multi: true },
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const locales: Locale[] = injector.get(Locale);
|
|
||||||
* const localeNames: string[] = locals.map((l) => l.name);
|
|
||||||
* expect(localeNames).toEqual(['en', 'sk']);
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configures the {@link Injector} to return a value by invoking a `useFactory` function.
|
* @whatItDoes Configures the {@link Injector} to return a value by invoking a `useFactory`
|
||||||
|
* function.
|
||||||
|
* @howToUse
|
||||||
|
* ```
|
||||||
|
* function serviceFactory() { ... }
|
||||||
|
*
|
||||||
|
* const provider: FactoryProvider = {provide: 'someToken', useFactory: serviceFactory, deps: []};
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* const HASH = new OpaqueToken('hash');
|
|
||||||
*
|
*
|
||||||
* const injector = Injector.resolveAndCreate([
|
* {@example core/di/ts/provider_spec.ts region='FactoryProvider'}
|
||||||
* {provide: Location, useValue: window.location},
|
|
||||||
* {provide: HASH, useFactory: (location: Location) => location.hash, deps: [Location]}
|
|
||||||
* ]);
|
|
||||||
*
|
*
|
||||||
|
* Dependencies can also be marked as optional:
|
||||||
|
* {@example core/di/ts/provider_spec.ts region='FactoryProviderOptionalDeps'}
|
||||||
*
|
*
|
||||||
* // Assume location is: http://angular.io/#someLocation
|
|
||||||
* expect(injector.get(HASH)).toEqual('someLocation');
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface FactoryProvider {
|
export interface FactoryProvider {
|
||||||
@ -285,65 +199,21 @@ export interface FactoryProvider {
|
|||||||
* providers spread across many files to provide configuration information to a common token.
|
* providers spread across many files to provide configuration information to a common token.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
* ```javascript
|
|
||||||
* class Locale {
|
|
||||||
* constructor(public name: string) {}
|
|
||||||
* };
|
|
||||||
* const PRIMARY = new OpequeToken('primary');
|
|
||||||
* const SECONDARY = new OpequeToken('secondary');
|
|
||||||
*
|
*
|
||||||
* const injector = Injector.resolveAndCreate([
|
* {@example core/di/ts/provider_spec.ts region='MultiProviderAspect'}
|
||||||
* { provide: PRIMARY: useValue: 'en'},
|
|
||||||
* { provide: SECONDARY: useValue: 'sk'},
|
|
||||||
* { provide: Locale, useFactory: (n) => new Locale(n), deps: [PRIMARY], multi: true},
|
|
||||||
* { provide: Locale, useFactory: (n) => new Locale(n), deps: [SECONDARY], multi: true},
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* const locales: Locale[] = injector.get(Locale);
|
|
||||||
* const localeNames: string[] = locals.map((l) => l.name);
|
|
||||||
* expect(localeNames).toEqual(['en', 'sk']);
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
multi?: boolean;
|
multi?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Describes how the {@link Injector} should be configured.
|
* @whatItDoes Describes how the {@link Injector} should be configured.
|
||||||
*
|
* @howToUse
|
||||||
* See {@link TypeProvider}, {@link ValueProvider}, {@link ClassProvider}, {@link ExistingProvider},
|
* See {@link TypeProvider}, {@link ValueProvider}, {@link ClassProvider}, {@link ExistingProvider},
|
||||||
* {@link FactoryProvider}.
|
* {@link FactoryProvider}.
|
||||||
*
|
*
|
||||||
* ```javascript
|
* @description
|
||||||
* class Greeting {
|
* For more details, see the {@linkDocs guide/dependency-injection "Dependency Injection Guide"}.
|
||||||
* salutation = 'Hello';
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* class FormalGreeting extends Greeting {
|
|
||||||
* salutation = 'Greetings';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* abstract class Operation {
|
|
||||||
* apply(a,b): any;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* class AddOperation extends Operation {
|
|
||||||
* apply(a,b) { return a+b; }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* const injector = Injector.resolveAndCreate([
|
|
||||||
* FormalGreeting,
|
|
||||||
* {provide: String, useValue: 'Hello World!'},
|
|
||||||
* {provide: Greeting, useExisting: FormalGreeting},
|
|
||||||
* {provide: Operation, useClass: AddOperation},
|
|
||||||
* {provide: Number, useFactory: (op) =>op.apply(1,2), deps: [Operation] }
|
|
||||||
* ]);
|
|
||||||
*
|
|
||||||
* expect(injector.get(FormalGreeting).name).toEqual('Greetings');
|
|
||||||
* expect(injector.get(String).name).toEqual('Hello World!');
|
|
||||||
* expect(injector.get(Greeting).name).toBe(injector.get(FormalGreeting));
|
|
||||||
* expect(injector.get(Number).toEqual(3);
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export type Provider =
|
export type Provider =
|
||||||
|
@ -121,7 +121,7 @@ export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoIn
|
|||||||
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
|
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
|
||||||
var len = providers.length;
|
var len = providers.length;
|
||||||
|
|
||||||
this.keyIds = ListWrapper.createFixedSize(len);
|
this.keyIds = new Array(len);
|
||||||
|
|
||||||
for (var i = 0; i < len; i++) {
|
for (var i = 0; i < len; i++) {
|
||||||
this.keyIds[i] = providers[i].key.id;
|
this.keyIds[i] = providers[i].key.id;
|
||||||
@ -286,7 +286,7 @@ export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStra
|
|||||||
constructor(
|
constructor(
|
||||||
public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
|
public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
|
||||||
public injector: ReflectiveInjector_) {
|
public injector: ReflectiveInjector_) {
|
||||||
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
|
this.objs = new Array(protoStrategy.providers.length);
|
||||||
ListWrapper.fill(this.objs, UNDEFINED);
|
ListWrapper.fill(this.objs, UNDEFINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -648,7 +648,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
|
|||||||
|
|
||||||
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
|
||||||
if (provider.multiProvider) {
|
if (provider.multiProvider) {
|
||||||
var res = ListWrapper.createFixedSize(provider.resolvedFactories.length);
|
var res = new Array(provider.resolvedFactories.length);
|
||||||
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
|
for (var i = 0; i < provider.resolvedFactories.length; ++i) {
|
||||||
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,6 @@ import {APP_ID} from '../application_tokens';
|
|||||||
import {devModeEqual} from '../change_detection/change_detection';
|
import {devModeEqual} from '../change_detection/change_detection';
|
||||||
import {UNINITIALIZED} from '../change_detection/change_detection_util';
|
import {UNINITIALIZED} from '../change_detection/change_detection_util';
|
||||||
import {Inject, Injectable} from '../di';
|
import {Inject, Injectable} from '../di';
|
||||||
import {ListWrapper} from '../facade/collection';
|
|
||||||
import {isBlank, isPresent, looseIdentical} from '../facade/lang';
|
import {isBlank, isPresent, looseIdentical} from '../facade/lang';
|
||||||
import {ViewEncapsulation} from '../metadata/view';
|
import {ViewEncapsulation} from '../metadata/view';
|
||||||
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
|
||||||
@ -77,7 +76,7 @@ export function ensureSlotCount(projectableNodes: any[][], expectedSlotCount: nu
|
|||||||
res = EMPTY_ARR;
|
res = EMPTY_ARR;
|
||||||
} else if (projectableNodes.length < expectedSlotCount) {
|
} else if (projectableNodes.length < expectedSlotCount) {
|
||||||
var givenSlotCount = projectableNodes.length;
|
var givenSlotCount = projectableNodes.length;
|
||||||
res = ListWrapper.createFixedSize(expectedSlotCount);
|
res = new Array(expectedSlotCount);
|
||||||
for (var i = 0; i < expectedSlotCount; i++) {
|
for (var i = 0; i < expectedSlotCount; i++) {
|
||||||
res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR;
|
res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR;
|
||||||
}
|
}
|
||||||
|
@ -15,11 +15,9 @@ import {Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren
|
|||||||
import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
|
import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
|
||||||
import {ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module';
|
import {ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module';
|
||||||
import {ViewEncapsulation} from './metadata/view';
|
import {ViewEncapsulation} from './metadata/view';
|
||||||
import {Type} from './type';
|
|
||||||
import {TypeDecorator, makeParamDecorator, makePropDecorator} from './util/decorators';
|
|
||||||
|
|
||||||
export {ANALYZE_FOR_ENTRY_COMPONENTS, Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren} from './metadata/di';
|
export {ANALYZE_FOR_ENTRY_COMPONENTS, Attribute, ContentChild, ContentChildDecorator, ContentChildren, ContentChildrenDecorator, Query, ViewChild, ViewChildDecorator, ViewChildren, ViewChildrenDecorator} from './metadata/di';
|
||||||
export {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
|
export {Component, ComponentDecorator, Directive, DirectiveDecorator, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
|
||||||
export {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './metadata/lifecycle_hooks';
|
export {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, DoCheck, OnChanges, OnDestroy, OnInit} from './metadata/lifecycle_hooks';
|
||||||
export {CUSTOM_ELEMENTS_SCHEMA, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModule, SchemaMetadata} from './metadata/ng_module';
|
export {CUSTOM_ELEMENTS_SCHEMA, ModuleWithProviders, NO_ERRORS_SCHEMA, NgModule, SchemaMetadata} from './metadata/ng_module';
|
||||||
export {ViewEncapsulation} from './metadata/view';
|
export {ViewEncapsulation} from './metadata/view';
|
||||||
|
@ -6,9 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {resolveForwardRef} from '../di/forward_ref';
|
|
||||||
import {OpaqueToken} from '../di/opaque_token';
|
import {OpaqueToken} from '../di/opaque_token';
|
||||||
import {StringWrapper, isString, stringify} from '../facade/lang';
|
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
import {makeParamDecorator, makePropDecorator} from '../util/decorators';
|
import {makeParamDecorator, makePropDecorator} from '../util/decorators';
|
||||||
|
|
||||||
@ -48,7 +46,6 @@ import {makeParamDecorator, makePropDecorator} from '../util/decorators';
|
|||||||
*/
|
*/
|
||||||
export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComponents');
|
export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComponents');
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of the Attribute decorator / constructor function.
|
* Type of the Attribute decorator / constructor function.
|
||||||
*
|
*
|
||||||
@ -56,63 +53,62 @@ export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComp
|
|||||||
*/
|
*/
|
||||||
export interface AttributeDecorator {
|
export interface AttributeDecorator {
|
||||||
/**
|
/**
|
||||||
* Specifies that a constant attribute value should be injected.
|
* Specifies that a constant attribute value should be injected.
|
||||||
*
|
*
|
||||||
* The directive can inject constant string literals of host element attributes.
|
* The directive can inject constant string literals of host element attributes.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* Suppose we have an `<input>` element and want to know its `type`.
|
* Suppose we have an `<input>` element and want to know its `type`.
|
||||||
*
|
*
|
||||||
* ```html
|
* ```html
|
||||||
* <input type="text">
|
* <input type="text">
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* A decorator can inject string literal `text` like so:
|
* A decorator can inject string literal `text` like so:
|
||||||
*
|
*
|
||||||
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
|
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
|
||||||
*
|
*
|
||||||
* ### Example as TypeScript Decorator
|
* ### Example as TypeScript Decorator
|
||||||
*
|
*
|
||||||
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
|
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
|
||||||
*
|
*
|
||||||
* ### Example as ES5 DSL
|
* ### Example as ES5 DSL
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* var MyComponent = ng
|
* var MyComponent = ng
|
||||||
* .Component({...})
|
* .Component({...})
|
||||||
* .Class({
|
* .Class({
|
||||||
* constructor: [new ng.Attribute('title'), function(title) {
|
* constructor: [new ng.Attribute('title'), function(title) {
|
||||||
* ...
|
* ...
|
||||||
* }]
|
* }]
|
||||||
* })
|
* })
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* ### Example as ES5 annotation
|
* ### Example as ES5 annotation
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* var MyComponent = function(title) {
|
* var MyComponent = function(title) {
|
||||||
* ...
|
* ...
|
||||||
* };
|
* };
|
||||||
*
|
*
|
||||||
* MyComponent.annotations = [
|
* MyComponent.annotations = [
|
||||||
* new ng.Component({...})
|
* new ng.Component({...})
|
||||||
* ]
|
* ]
|
||||||
* MyComponent.parameters = [
|
* MyComponent.parameters = [
|
||||||
* [new ng.Attribute('title')]
|
* [new ng.Attribute('title')]
|
||||||
* ]
|
* ]
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/ (name: string): any;
|
*/
|
||||||
|
(name: string): any;
|
||||||
new (name: string): Attribute;
|
new (name: string): Attribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of the Attribute metadata.
|
* Type of the Attribute metadata.
|
||||||
*
|
|
||||||
* @stable
|
|
||||||
*/
|
*/
|
||||||
export interface Attribute { attributeName?: string; }
|
export interface Attribute { attributeName?: string; }
|
||||||
|
|
||||||
@ -139,7 +135,10 @@ export interface Query {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for query metadata
|
* Base class for query metadata.
|
||||||
|
*
|
||||||
|
* See {@link ContentChildren}, {@link ContentChild}, {@link ViewChildren}, {@link ViewChild} for
|
||||||
|
* more information.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
@ -148,29 +147,41 @@ export abstract class Query {}
|
|||||||
/**
|
/**
|
||||||
* Type of the ContentChildren decorator / constructor function.
|
* Type of the ContentChildren decorator / constructor function.
|
||||||
*
|
*
|
||||||
|
* See {@link ContentChildren}.
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface ContentChildrenDecorator {
|
export interface ContentChildrenDecorator {
|
||||||
/**
|
/**
|
||||||
* Configures a content query.
|
* @whatItDoes Configures a content query.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/contentChildren/content_children_howto.ts region='HowTo'}
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* You can use ContentChildren to get the {@link QueryList} of elements or directives from the
|
||||||
|
* content DOM. Any time a child element is added, removed, or moved, the query list will be
|
||||||
|
* updated,
|
||||||
|
* and the changes observable of the query list will emit a new value.
|
||||||
*
|
*
|
||||||
* Content queries are set before the `ngAfterContentInit` callback is called.
|
* Content queries are set before the `ngAfterContentInit` callback is called.
|
||||||
*
|
*
|
||||||
* ### Example
|
* **Metadata Properties**:
|
||||||
*
|
*
|
||||||
* ```
|
* * **selector** - the directive type or the name used for querying.
|
||||||
* @Directive({
|
* * **descendants** - include only direct children or all descendants.
|
||||||
* selector: 'someDir'
|
* * **read** - read a different token from the queried elements.
|
||||||
* })
|
*
|
||||||
* class SomeDir {
|
* Let's look at an example:
|
||||||
* @ContentChildren(ChildDirective) contentChildren: QueryList<ChildDirective>;
|
*
|
||||||
|
* {@example core/di/ts/contentChildren/content_children_example.ts region='Component'}
|
||||||
|
*
|
||||||
|
* **npm package**: `@angular/core`
|
||||||
*
|
*
|
||||||
* ngAfterContentInit() {
|
|
||||||
* // contentChildren is set
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
|
* @Annotation
|
||||||
*/
|
*/
|
||||||
(selector: Type<any>|Function|string,
|
(selector: Type<any>|Function|string,
|
||||||
{descendants, read}?: {descendants?: boolean, read?: any}): any;
|
{descendants, read}?: {descendants?: boolean, read?: any}): any;
|
||||||
@ -183,57 +194,38 @@ export interface ContentChildrenDecorator {
|
|||||||
* Type of the ContentChildren metadata.
|
* Type of the ContentChildren metadata.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
|
* @Annotation
|
||||||
*/
|
*/
|
||||||
export type ContentChildren = Query;
|
export type ContentChildren = Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ContentChildren decorator and metadata.
|
* ContentChildren decorator and metadata.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
* @Annotation
|
* @Annotation
|
||||||
*/
|
*/
|
||||||
export const ContentChildren: ContentChildrenDecorator = makePropDecorator(
|
export const ContentChildren: ContentChildrenDecorator =
|
||||||
'ContentChildren',
|
<ContentChildrenDecorator>makePropDecorator(
|
||||||
[
|
'ContentChildren',
|
||||||
['selector', undefined],
|
[
|
||||||
{first: false, isViewQuery: false, descendants: false, read: undefined}
|
['selector', undefined], {
|
||||||
],
|
first: false,
|
||||||
Query);
|
isViewQuery: false,
|
||||||
|
descendants: false,
|
||||||
|
read: undefined,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
Query);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of the ContentChild decorator / constructor function.
|
* Type of the ContentChild decorator / constructor function.
|
||||||
*
|
*
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface ContentChildDecorator {
|
export interface ContentChildDecorator {
|
||||||
/**
|
/**
|
||||||
* Configures a content query.
|
* @docsNotRequired
|
||||||
*
|
|
||||||
* Content queries are set before the `ngAfterContentInit` callback is called.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({
|
|
||||||
* selector: 'someDir'
|
|
||||||
* })
|
|
||||||
* class SomeDir {
|
|
||||||
* @ContentChild(ChildDirective) contentChild;
|
|
||||||
* @ContentChild('container_ref') containerChild
|
|
||||||
*
|
|
||||||
* ngAfterContentInit() {
|
|
||||||
* // contentChild is set
|
|
||||||
* // containerChild is set
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* ```html
|
|
||||||
* <container #container_ref>
|
|
||||||
* <item>a</item>
|
|
||||||
* <item>b</item>
|
|
||||||
* </container>
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
(selector: Type<any>|Function|string, {read}?: {read?: any}): any;
|
(selector: Type<any>|Function|string, {read}?: {read?: any}): any;
|
||||||
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ContentChild;
|
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ContentChild;
|
||||||
@ -242,12 +234,37 @@ export interface ContentChildDecorator {
|
|||||||
/**
|
/**
|
||||||
* Type of the ContentChild metadata.
|
* Type of the ContentChild metadata.
|
||||||
*
|
*
|
||||||
|
* See {@link ContentChild}.
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export type ContentChild = Query;
|
export type ContentChild = Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ContentChild decorator and metadata.
|
* @whatItDoes Configures a content query.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/contentChild/content_child_howto.ts region='HowTo'}
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* You can use ContentChild to get the first element or the directive matching the selector from the
|
||||||
|
* content DOM. If the content DOM changes, and a new child matches the selector,
|
||||||
|
* the property will be updated.
|
||||||
|
*
|
||||||
|
* Content queries are set before the `ngAfterContentInit` callback is called.
|
||||||
|
*
|
||||||
|
* **Metadata Properties**:
|
||||||
|
*
|
||||||
|
* * **selector** - the directive type or the name used for querying.
|
||||||
|
* * **read** - read a different token from the queried element.
|
||||||
|
*
|
||||||
|
* Let's look at an example:
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/contentChild/content_child_example.ts region='Component'}
|
||||||
|
*
|
||||||
|
* **npm package**: `@angular/core`
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
* @Annotation
|
* @Annotation
|
||||||
@ -258,7 +275,7 @@ export const ContentChild: ContentChildDecorator = makePropDecorator(
|
|||||||
['selector', undefined], {
|
['selector', undefined], {
|
||||||
first: true,
|
first: true,
|
||||||
isViewQuery: false,
|
isViewQuery: false,
|
||||||
descendants: false,
|
descendants: true,
|
||||||
read: undefined,
|
read: undefined,
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
@ -267,86 +284,13 @@ export const ContentChild: ContentChildDecorator = makePropDecorator(
|
|||||||
/**
|
/**
|
||||||
* Type of the ViewChildren decorator / constructor function.
|
* Type of the ViewChildren decorator / constructor function.
|
||||||
*
|
*
|
||||||
|
* See {@ViewChildren}.
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface ViewChildrenDecorator {
|
export interface ViewChildrenDecorator {
|
||||||
/**
|
/**
|
||||||
* Declares a list of child element references.
|
* @docsNotRequired
|
||||||
*
|
|
||||||
* Angular automatically updates the list when the DOM is updated.
|
|
||||||
*
|
|
||||||
* `ViewChildren` takes an argument to select elements.
|
|
||||||
*
|
|
||||||
* - If the argument is a type, directives or components with the type will be bound.
|
|
||||||
*
|
|
||||||
* - If the argument is a string, the string is interpreted as a list of comma-separated selectors.
|
|
||||||
* For each selector, an element containing the matching template variable (e.g. `#child`) will be
|
|
||||||
* bound.
|
|
||||||
*
|
|
||||||
* View children are set before the `ngAfterViewInit` callback is called.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* With type selector:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Component({
|
|
||||||
* selector: 'child-cmp',
|
|
||||||
* template: '<p>child</p>'
|
|
||||||
* })
|
|
||||||
* class ChildCmp {
|
|
||||||
* doSomething() {}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'some-cmp',
|
|
||||||
* template: `
|
|
||||||
* <child-cmp></child-cmp>
|
|
||||||
* <child-cmp></child-cmp>
|
|
||||||
* <child-cmp></child-cmp>
|
|
||||||
* `,
|
|
||||||
* directives: [ChildCmp]
|
|
||||||
* })
|
|
||||||
* class SomeCmp {
|
|
||||||
* @ViewChildren(ChildCmp) children:QueryList<ChildCmp>;
|
|
||||||
*
|
|
||||||
* ngAfterViewInit() {
|
|
||||||
* // children are set
|
|
||||||
* this.children.toArray().forEach((child)=>child.doSomething());
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* With string selector:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Component({
|
|
||||||
* selector: 'child-cmp',
|
|
||||||
* template: '<p>child</p>'
|
|
||||||
* })
|
|
||||||
* class ChildCmp {
|
|
||||||
* doSomething() {}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'some-cmp',
|
|
||||||
* template: `
|
|
||||||
* <child-cmp #child1></child-cmp>
|
|
||||||
* <child-cmp #child2></child-cmp>
|
|
||||||
* <child-cmp #child3></child-cmp>
|
|
||||||
* `,
|
|
||||||
* directives: [ChildCmp]
|
|
||||||
* })
|
|
||||||
* class SomeCmp {
|
|
||||||
* @ViewChildren('child1,child2,child3') children:QueryList<ChildCmp>;
|
|
||||||
*
|
|
||||||
* ngAfterViewInit() {
|
|
||||||
* // children are set
|
|
||||||
* this.children.toArray().forEach((child)=>child.doSomething());
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
|
||||||
*/ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
|
*/ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
|
||||||
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChildren;
|
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChildren;
|
||||||
}
|
}
|
||||||
@ -359,7 +303,30 @@ export interface ViewChildrenDecorator {
|
|||||||
export type ViewChildren = Query;
|
export type ViewChildren = Query;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewChildren decorator and metadata.
|
* @whatItDoes Configures a view query.
|
||||||
|
*
|
||||||
|
* @howToUse
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/viewChildren/view_children_howto.ts region='HowTo'}
|
||||||
|
*
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* You can use ViewChildren to get the {@link QueryList} of elements or directives from the
|
||||||
|
* view DOM. Any time a child element is added, removed, or moved, the query list will be updated,
|
||||||
|
* and the changes observable of the query list will emit a new value.
|
||||||
|
*
|
||||||
|
* View queries are set before the `ngAfterViewInit` callback is called.
|
||||||
|
*
|
||||||
|
* **Metadata Properties**:
|
||||||
|
*
|
||||||
|
* * **selector** - the directive type or the name used for querying.
|
||||||
|
* * **read** - read a different token from the queried elements.
|
||||||
|
*
|
||||||
|
* Let's look at an example:
|
||||||
|
*
|
||||||
|
* {@example core/di/ts/viewChildren/view_children_example.ts region='Component'}
|
||||||
|
*
|
||||||
|
* **npm package**: `@angular/core`
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
* @Annotation
|
* @Annotation
|
||||||
@ -376,84 +343,42 @@ export const ViewChildren: ViewChildrenDecorator = makePropDecorator(
|
|||||||
],
|
],
|
||||||
Query);
|
Query);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Type of the ViewChild decorator / constructor function.
|
* Type of the ViewChild decorator / constructor function.
|
||||||
*
|
*
|
||||||
|
* See {@link ViewChild}
|
||||||
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export interface ViewChildDecorator {
|
export interface ViewChildDecorator {
|
||||||
/**
|
/**
|
||||||
*
|
* @whatItDoes Configures a view query.
|
||||||
* Declares a reference of child element.
|
*
|
||||||
*
|
* @howToUse
|
||||||
* `ViewChildren` takes an argument to select elements.
|
*
|
||||||
*
|
* {@example core/di/ts/viewChild/view_child_howto.ts region='HowTo'}
|
||||||
* - If the argument is a type, a directive or a component with the type will be bound.
|
*
|
||||||
*
|
* @description
|
||||||
* If the argument is a string, the string is interpreted as a selector. An element containing the
|
*
|
||||||
* matching template variable (e.g. `#child`) will be bound.
|
* You can use ViewChild to get the first element or the directive matching the selector from the
|
||||||
*
|
* view DOM. If the view DOM changes, and a new child matches the selector,
|
||||||
* In either case, `@ViewChild()` assigns the first (looking from above) element if there are
|
* the property will be updated.
|
||||||
multiple matches.
|
*
|
||||||
*
|
* View queries are set before the `ngAfterViewInit` callback is called.
|
||||||
* View child is set before the `ngAfterViewInit` callback is called.
|
*
|
||||||
*
|
* **Metadata Properties**:
|
||||||
* ### Example
|
*
|
||||||
*
|
* * **selector** - the directive type or the name used for querying.
|
||||||
* With type selector:
|
* * **read** - read a different token from the queried elements.
|
||||||
*
|
*
|
||||||
* ```
|
* {@example core/di/ts/viewChild/view_child_example.ts region='Component'}
|
||||||
* @Component({
|
*
|
||||||
* selector: 'child-cmp',
|
* **npm package**: `@angular/core`
|
||||||
* template: '<p>child</p>'
|
*
|
||||||
* })
|
* @stable
|
||||||
* class ChildCmp {
|
* @Annotation
|
||||||
* doSomething() {}
|
*/
|
||||||
* }
|
(selector: Type<any>|Function|string, {read}?: {read?: any}): any;
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'some-cmp',
|
|
||||||
* template: '<child-cmp></child-cmp>',
|
|
||||||
* directives: [ChildCmp]
|
|
||||||
* })
|
|
||||||
* class SomeCmp {
|
|
||||||
* @ViewChild(ChildCmp) child:ChildCmp;
|
|
||||||
*
|
|
||||||
* ngAfterViewInit() {
|
|
||||||
* // child is set
|
|
||||||
* this.child.doSomething();
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* With string selector:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Component({
|
|
||||||
* selector: 'child-cmp',
|
|
||||||
* template: '<p>child</p>'
|
|
||||||
* })
|
|
||||||
* class ChildCmp {
|
|
||||||
* doSomething() {}
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'some-cmp',
|
|
||||||
* template: '<child-cmp #child></child-cmp>',
|
|
||||||
* directives: [ChildCmp]
|
|
||||||
* })
|
|
||||||
* class SomeCmp {
|
|
||||||
* @ViewChild('child') child:ChildCmp;
|
|
||||||
*
|
|
||||||
* ngAfterViewInit() {
|
|
||||||
* // child is set
|
|
||||||
* this.child.doSomething();
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
|
||||||
*/ (selector: Type<any>|Function|string, {read}?: {read?: any}): any;
|
|
||||||
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChild;
|
new (selector: Type<any>|Function|string, {read}?: {read?: any}): ViewChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,420 +23,51 @@ import {ViewEncapsulation} from './view';
|
|||||||
*/
|
*/
|
||||||
export interface DirectiveDecorator {
|
export interface DirectiveDecorator {
|
||||||
/**
|
/**
|
||||||
* Directives allow you to attach behavior to elements in the DOM.
|
* @whatItDoes Marks a class as an Angular directive and collects directive configuration
|
||||||
|
* metadata.
|
||||||
*
|
*
|
||||||
* {@link Directive}s with an embedded view are called {@link Component}s.
|
* @howToUse
|
||||||
*
|
|
||||||
* A directive consists of a single directive annotation and a controller class. When the
|
|
||||||
* directive's `selector` matches
|
|
||||||
* elements in the DOM, the following steps occur:
|
|
||||||
*
|
|
||||||
* 1. For each directive, the `ElementInjector` attempts to resolve the directive's constructor
|
|
||||||
* arguments.
|
|
||||||
* 2. Angular instantiates directives for each matched element using `ElementInjector` in a
|
|
||||||
* depth-first order,
|
|
||||||
* as declared in the HTML.
|
|
||||||
*
|
|
||||||
* ## Understanding How Injection Works
|
|
||||||
*
|
|
||||||
* There are three stages of injection resolution.
|
|
||||||
* - *Pre-existing Injectors*:
|
|
||||||
* - The terminal {@link Injector} cannot resolve dependencies. It either throws an error or, if
|
|
||||||
* the dependency was
|
|
||||||
* specified as `@Optional`, returns `null`.
|
|
||||||
* - The platform injector resolves browser singleton resources, such as: cookies, title,
|
|
||||||
* location, and others.
|
|
||||||
* - *Component Injectors*: Each component instance has its own {@link Injector}, and they follow
|
|
||||||
* the same parent-child hierarchy
|
|
||||||
* as the component instances in the DOM.
|
|
||||||
* - *Element Injectors*: Each component instance has a Shadow DOM. Within the Shadow DOM each
|
|
||||||
* element has an `ElementInjector`
|
|
||||||
* which follow the same parent-child hierarchy as the DOM elements themselves.
|
|
||||||
*
|
|
||||||
* When a template is instantiated, it also must instantiate the corresponding directives in a
|
|
||||||
* depth-first order. The
|
|
||||||
* current `ElementInjector` resolves the constructor dependencies for each directive.
|
|
||||||
*
|
|
||||||
* Angular then resolves dependencies as follows, according to the order in which they appear in
|
|
||||||
* the
|
|
||||||
* {@link Component}:
|
|
||||||
*
|
|
||||||
* 1. Dependencies on the current element
|
|
||||||
* 2. Dependencies on element injectors and their parents until it encounters a Shadow DOM
|
|
||||||
* boundary
|
|
||||||
* 3. Dependencies on component injectors and their parents until it encounters the root component
|
|
||||||
* 4. Dependencies on pre-existing injectors
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* The `ElementInjector` can inject other directives, element-specific special objects, or it can
|
|
||||||
* delegate to the parent
|
|
||||||
* injector.
|
|
||||||
*
|
|
||||||
* To inject other directives, declare the constructor parameter as:
|
|
||||||
* - `directive:DirectiveType`: a directive on the current element only
|
|
||||||
* - `@Host() directive:DirectiveType`: any directive that matches the type between the current
|
|
||||||
* element and the
|
|
||||||
* Shadow DOM root.
|
|
||||||
* - `@Query(DirectiveType) query:QueryList<DirectiveType>`: A live collection of direct child
|
|
||||||
* directives.
|
|
||||||
* - `@QueryDescendants(DirectiveType) query:QueryList<DirectiveType>`: A live collection of any
|
|
||||||
* child directives.
|
|
||||||
*
|
|
||||||
* To inject element-specific special objects, declare the constructor parameter as:
|
|
||||||
* - `element: ElementRef` to obtain a reference to logical element in the view.
|
|
||||||
* - `viewContainer: ViewContainerRef` to control child template instantiation, for
|
|
||||||
* {@link Directive} directives only
|
|
||||||
* - `bindingPropagation: BindingPropagation` to control change detection in a more granular way.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* The following example demonstrates how dependency injection resolves constructor arguments in
|
|
||||||
* practice.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* Assume this HTML template:
|
|
||||||
*
|
*
|
||||||
* ```
|
* ```
|
||||||
* <div dependency="1">
|
* import {Directive} from '@angular/core';
|
||||||
* <div dependency="2">
|
|
||||||
* <div dependency="3" my-directive>
|
|
||||||
* <div dependency="4">
|
|
||||||
* <div dependency="5"></div>
|
|
||||||
* </div>
|
|
||||||
* <div dependency="6"></div>
|
|
||||||
* </div>
|
|
||||||
* </div>
|
|
||||||
* </div>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* With the following `dependency` decorator and `SomeService` injectable class.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Injectable()
|
|
||||||
* class SomeService {
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* @Directive({
|
* @Directive({
|
||||||
* selector: '[dependency]',
|
* selector: 'my-directive',
|
||||||
* inputs: [
|
|
||||||
* 'id: dependency'
|
|
||||||
* ]
|
|
||||||
* })
|
* })
|
||||||
* class Dependency {
|
* export class MyDirective {
|
||||||
* id:string;
|
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* Let's step through the different ways in which `MyDirective` could be declared...
|
* @description
|
||||||
*
|
*
|
||||||
*
|
* Directive decorator allows you to mark a class as an Angular directive and provide additional
|
||||||
* ### No injection
|
* metadata that determines how the directive should be processed, instantiated and used at
|
||||||
*
|
|
||||||
* Here the constructor is declared with no arguments, therefore nothing is injected into
|
|
||||||
* `MyDirective`.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor() {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* This directive would be instantiated with no dependencies.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ### Component-level injection
|
|
||||||
*
|
|
||||||
* Directives can inject any injectable instance from the closest component injector or any of its
|
|
||||||
* parents.
|
|
||||||
*
|
|
||||||
* Here, the constructor declares a parameter, `someService`, and injects the `SomeService` type
|
|
||||||
* from the parent
|
|
||||||
* component's injector.
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor(someService: SomeService) {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* This directive would be instantiated with a dependency on `SomeService`.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ### Injecting a directive from the current element
|
|
||||||
*
|
|
||||||
* Directives can inject other directives declared on the current element.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor(dependency: Dependency) {
|
|
||||||
* expect(dependency.id).toEqual(3);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* This directive would be instantiated with `Dependency` declared at the same element, in this
|
|
||||||
* case
|
|
||||||
* `dependency="3"`.
|
|
||||||
*
|
|
||||||
* ### Injecting a directive from any ancestor elements
|
|
||||||
*
|
|
||||||
* Directives can inject other directives declared on any ancestor element (in the current Shadow
|
|
||||||
* DOM), i.e. on the current element, the
|
|
||||||
* parent element, or its parents.
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor(@Host() dependency: Dependency) {
|
|
||||||
* expect(dependency.id).toEqual(2);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* `@Host` checks the current element, the parent, as well as its parents recursively. If
|
|
||||||
* `dependency="2"` didn't
|
|
||||||
* exist on the direct parent, this injection would
|
|
||||||
* have returned
|
|
||||||
* `dependency="1"`.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ### Injecting a live collection of direct child directives
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* A directive can also query for other child directives. Since parent directives are instantiated
|
|
||||||
* before child directives, a directive can't simply inject the list of child directives. Instead,
|
|
||||||
* the directive injects a {@link QueryList}, which updates its contents as children are added,
|
|
||||||
* removed, or moved by a directive that uses a {@link ViewContainerRef} such as a `ngFor`, an
|
|
||||||
* `ngIf`, or an `ngSwitch`.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor(@Query(Dependency) dependencies:QueryList<Dependency>) {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* This directive would be instantiated with a {@link QueryList} which contains `Dependency` 4 and
|
|
||||||
* `Dependency` 6. Here, `Dependency` 5 would not be included, because it is not a direct child.
|
|
||||||
*
|
|
||||||
* ### Injecting a live collection of descendant directives
|
|
||||||
*
|
|
||||||
* By passing the descendant flag to `@Query` above, we can include the children of the child
|
|
||||||
* elements.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor(@Query(Dependency, {descendants: true}) dependencies:QueryList<Dependency>) {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* This directive would be instantiated with a Query which would contain `Dependency` 4, 5 and 6.
|
|
||||||
*
|
|
||||||
* ### Optional injection
|
|
||||||
*
|
|
||||||
* The normal behavior of directives is to return an error when a specified dependency cannot be
|
|
||||||
* resolved. If you
|
|
||||||
* would like to inject `null` on unresolved dependency instead, you can annotate that dependency
|
|
||||||
* with `@Optional()`.
|
|
||||||
* This explicitly permits the author of a template to treat some of the surrounding directives as
|
|
||||||
* optional.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({ selector: '[my-directive]' })
|
|
||||||
* class MyDirective {
|
|
||||||
* constructor(@Optional() dependency:Dependency) {
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* This directive would be instantiated with a `Dependency` directive found on the current
|
|
||||||
* element.
|
|
||||||
* If none can be
|
|
||||||
* found, the injector supplies `null` instead of throwing an error.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* Here we use a decorator directive to simply define basic tool-tip behavior.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({
|
|
||||||
* selector: '[tooltip]',
|
|
||||||
* inputs: [
|
|
||||||
* 'text: tooltip'
|
|
||||||
* ],
|
|
||||||
* host: {
|
|
||||||
* '(mouseenter)': 'onMouseEnter()',
|
|
||||||
* '(mouseleave)': 'onMouseLeave()'
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
* class Tooltip{
|
|
||||||
* text:string;
|
|
||||||
* overlay:Overlay; // NOT YET IMPLEMENTED
|
|
||||||
* overlayManager:OverlayManager; // NOT YET IMPLEMENTED
|
|
||||||
*
|
|
||||||
* constructor(overlayManager:OverlayManager) {
|
|
||||||
* this.overlay = overlay;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* onMouseEnter() {
|
|
||||||
* // exact signature to be determined
|
|
||||||
* this.overlay = this.overlayManager.open(text, ...);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* onMouseLeave() {
|
|
||||||
* this.overlay.close();
|
|
||||||
* this.overlay = null;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* In our HTML template, we can then add this behavior to a `<div>` or any other element with the
|
|
||||||
* `tooltip` selector,
|
|
||||||
* like so:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* <div tooltip="some text here"></div>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Directives can also control the instantiation, destruction, and positioning of inline template
|
|
||||||
* elements:
|
|
||||||
*
|
|
||||||
* A directive uses a {@link ViewContainerRef} to instantiate, insert, move, and destroy views at
|
|
||||||
* runtime.
|
* runtime.
|
||||||
* The {@link ViewContainerRef} is created as a result of `<template>` element, and represents a
|
|
||||||
* location in the current view
|
|
||||||
* where these actions are performed.
|
|
||||||
*
|
*
|
||||||
* Views are always created as children of the current {@link Component}, and as siblings
|
* Directives allow you to attach behavior to elements in the DOM..
|
||||||
* of
|
|
||||||
* the
|
|
||||||
* `<template>` element. Thus a
|
|
||||||
* directive in a child view cannot inject the directive that created it.
|
|
||||||
*
|
*
|
||||||
* Since directives that create views via ViewContainers are common in Angular, and using the full
|
* A directive must belong to an NgModule in order for it to be usable
|
||||||
* `<template>` element syntax is wordy, Angular
|
* by another directive, component, or application. To specify that a directive is a member of an
|
||||||
* also supports a shorthand notation: `<li *foo="bar">` and `<li template="foo: bar">` are
|
* NgModule,
|
||||||
* equivalent.
|
* you should list it in the `declarations` field of that NgModule.
|
||||||
*
|
*
|
||||||
* Thus,
|
* In addition to the metadata configuration specified via the Directive decorator,
|
||||||
|
* directives can control their runtime behavior by implementing various Life-Cycle hooks.
|
||||||
*
|
*
|
||||||
* ```
|
* **Metadata Properties:**
|
||||||
* <ul>
|
|
||||||
* <li *foo="bar" title="text"></li>
|
|
||||||
* </ul>
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* Expands in use to:
|
* * **exportAs** - name under which the component instance is exported in a template
|
||||||
|
* * **host** - map of class property to host element bindings for events, properties and
|
||||||
|
* attributes
|
||||||
|
* * **inputs** - list of class property names to data-bind as component inputs
|
||||||
|
* * **outputs** - list of class property names that expose output events that others can
|
||||||
|
* subscribe to
|
||||||
|
* * **providers** - list of providers available to this component and its children
|
||||||
|
* * **queries** - configure queries that can be injected into the component
|
||||||
|
* * **selector** - css selector that identifies this component in a template
|
||||||
*
|
*
|
||||||
* ```
|
* @stable
|
||||||
* <ul>
|
* @Annotation
|
||||||
* <template [foo]="bar">
|
|
||||||
* <li title="text"></li>
|
|
||||||
* </template>
|
|
||||||
* </ul>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Notice that although the shorthand places `*foo="bar"` within the `<li>` element, the binding
|
|
||||||
* for
|
|
||||||
* the directive
|
|
||||||
* controller is correctly instantiated on the `<template>` element rather than the `<li>`
|
|
||||||
* element.
|
|
||||||
*
|
|
||||||
* ## Lifecycle hooks
|
|
||||||
*
|
|
||||||
* When the directive class implements some {@linkDocs guide/lifecycle-hooks} the
|
|
||||||
* callbacks are called by the change detection at defined points in time during the life of the
|
|
||||||
* directive.
|
|
||||||
*
|
|
||||||
* ### Example
|
|
||||||
*
|
|
||||||
* Let's suppose we want to implement the `unless` behavior, to conditionally include a template.
|
|
||||||
*
|
|
||||||
* Here is a simple directive that triggers on an `unless` selector:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Directive({
|
|
||||||
* selector: '[unless]',
|
|
||||||
* inputs: ['unless']
|
|
||||||
* })
|
|
||||||
* export class Unless {
|
|
||||||
* viewContainer: ViewContainerRef;
|
|
||||||
* templateRef: TemplateRef;
|
|
||||||
* prevCondition: boolean;
|
|
||||||
*
|
|
||||||
* constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef) {
|
|
||||||
* this.viewContainer = viewContainer;
|
|
||||||
* this.templateRef = templateRef;
|
|
||||||
* this.prevCondition = null;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* set unless(newCondition) {
|
|
||||||
* if (newCondition && (isBlank(this.prevCondition) || !this.prevCondition)) {
|
|
||||||
* this.prevCondition = true;
|
|
||||||
* this.viewContainer.clear();
|
|
||||||
* } else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
|
|
||||||
* this.prevCondition = false;
|
|
||||||
* this.viewContainer.create(this.templateRef);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* We can then use this `unless` selector in a template:
|
|
||||||
* ```
|
|
||||||
* <ul>
|
|
||||||
* <li *unless="expr"></li>
|
|
||||||
* </ul>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Once the directive instantiates the child view, the shorthand notation for the template expands
|
|
||||||
* and the result is:
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* <ul>
|
|
||||||
* <template [unless]="exp">
|
|
||||||
* <li></li>
|
|
||||||
* </template>
|
|
||||||
* <li></li>
|
|
||||||
* </ul>
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Note also that although the `<li></li>` template still exists inside the
|
|
||||||
* `<template></template>`,
|
|
||||||
* the instantiated
|
|
||||||
* view occurs on the second `<li></li>` which is a sibling to the `<template>` element.
|
|
||||||
*
|
|
||||||
* ### Example as TypeScript Decorator
|
|
||||||
*
|
|
||||||
* {@example core/ts/metadata/metadata.ts region='directive'}
|
|
||||||
*
|
|
||||||
* ### Example as ES5 DSL
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* var MyDirective = ng
|
|
||||||
* .Directive({...})
|
|
||||||
* .Class({
|
|
||||||
* constructor: function() {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* ### Example as ES5 annotation
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* var MyDirective = function() {
|
|
||||||
* ...
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* MyDirective.annotations = [
|
|
||||||
* new ng.Directive({...})
|
|
||||||
* ]
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
(obj: Directive): TypeDecorator;
|
(obj: Directive): TypeDecorator;
|
||||||
|
|
||||||
@ -787,58 +418,60 @@ export const Directive: DirectiveDecorator = <DirectiveDecorator>makeDecorator('
|
|||||||
*/
|
*/
|
||||||
export interface ComponentDecorator {
|
export interface ComponentDecorator {
|
||||||
/**
|
/**
|
||||||
* Declare reusable UI building blocks for an application.
|
* @whatItDoes Marks a class as an Angular component and collects component configuration
|
||||||
|
* metadata.
|
||||||
*
|
*
|
||||||
* Each Angular component requires a single `@Component` annotation. The
|
* @howToUse
|
||||||
* `@Component`
|
|
||||||
* annotation specifies when a component is instantiated, and which properties and hostListeners
|
|
||||||
* it
|
|
||||||
* binds to.
|
|
||||||
*
|
*
|
||||||
* When a component is instantiated, Angular
|
* {@example core/ts/metadata/metadata.ts region='component'}
|
||||||
* - creates a shadow DOM for the component.
|
|
||||||
* - loads the selected template into the shadow DOM.
|
|
||||||
* - creates all the injectable objects configured with `providers` and `viewProviders`.
|
|
||||||
*
|
*
|
||||||
* All template expressions and statements are then evaluated against the component instance.
|
* @description
|
||||||
|
* Component decorator allows you to mark a class as an Angular component and provide additional
|
||||||
|
* metadata that determines how the component should be processed, instantiated and used at
|
||||||
|
* runtime.
|
||||||
*
|
*
|
||||||
* ## Lifecycle hooks
|
* Components are the most basic building block of an UI in an Angular application.
|
||||||
|
* An Angular application is a tree of Angular components.
|
||||||
|
* Angular components are a subset of directives. Unlike directives, components always have
|
||||||
|
* a template and only one component can be instantiated per an element in a template.
|
||||||
*
|
*
|
||||||
* When the component class implements some {@linkDocs guide/lifecycle-hooks} the
|
* A component must belong to an NgModule in order for it to be usable
|
||||||
* callbacks are called by the change detection at defined points in time during the life of the
|
* by another component or application. To specify that a component is a member of an NgModule,
|
||||||
* component.
|
* you should list it in the `declarations` field of that NgModule.
|
||||||
|
*
|
||||||
|
* In addition to the metadata configuration specified via the Component decorator,
|
||||||
|
* components can control their runtime behavior by implementing various Life-Cycle hooks.
|
||||||
|
*
|
||||||
|
* **Metadata Properties:**
|
||||||
|
*
|
||||||
|
* * **animations** - list of animations of this component
|
||||||
|
* * **changeDetection** - change detection strategy used by this component
|
||||||
|
* * **encapsulation** - style encapsulation strategy used by this component
|
||||||
|
* * **entryComponents** - list of components that are dynamically inserted into the view of this
|
||||||
|
* component
|
||||||
|
* * **exportAs** - name under which the component instance is exported in a template
|
||||||
|
* * **host** - map of class property to host element bindings for events, properties and
|
||||||
|
* attributes
|
||||||
|
* * **inputs** - list of class property names to data-bind as component inputs
|
||||||
|
* * **interpolation** - custom interpolation markers used in this component's template
|
||||||
|
* * **moduleId** - ES/CommonJS module id of the file in which this component is defined
|
||||||
|
* * **outputs** - list of class property names that expose output events that others can
|
||||||
|
* subscribe to
|
||||||
|
* * **providers** - list of providers available to this component and its children
|
||||||
|
* * **queries** - configure queries that can be injected into the component
|
||||||
|
* * **selector** - css selector that identifies this component in a template
|
||||||
|
* * **styleUrls** - list of urls to stylesheets to be applied to this component's view
|
||||||
|
* * **styles** - inline-defined styles to be applied to this component's view
|
||||||
|
* * **template** - inline-defined template for the view
|
||||||
|
* * **templateUrl** - url to an external file containing a template for the view
|
||||||
|
* * **viewProviders** - list of providers available to this component and its view children
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
*
|
*
|
||||||
* {@example core/ts/metadata/metadata.ts region='component'}
|
* {@example core/ts/metadata/metadata.ts region='component'}
|
||||||
*
|
*
|
||||||
* ### Example as TypeScript Decorator
|
* @stable
|
||||||
*
|
* @Annotation
|
||||||
* {@example core/ts/metadata/metadata.ts region='component'}
|
|
||||||
*
|
|
||||||
* ### Example as ES5 DSL
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* var MyComponent = ng
|
|
||||||
* .Component({...})
|
|
||||||
* .Class({
|
|
||||||
* constructor: function() {
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* })
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* ### Example as ES5 annotation
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* var MyComponent = function() {
|
|
||||||
* ...
|
|
||||||
* };
|
|
||||||
*
|
|
||||||
* MyComponent.annotations = [
|
|
||||||
* new ng.Component({...})
|
|
||||||
* ]
|
|
||||||
* ```
|
|
||||||
*/
|
*/
|
||||||
(obj: Component): TypeDecorator;
|
(obj: Component): TypeDecorator;
|
||||||
/**
|
/**
|
||||||
@ -927,23 +560,19 @@ export interface Component extends Directive {
|
|||||||
/**
|
/**
|
||||||
* Specifies a template URL for an Angular component.
|
* Specifies a template URL for an Angular component.
|
||||||
*
|
*
|
||||||
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
|
*Only one of `templateUrl` or `template` can be defined per View.
|
||||||
*
|
|
||||||
* <!-- TODO: what's the url relative to? -->
|
|
||||||
*/
|
*/
|
||||||
templateUrl?: string;
|
templateUrl?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies an inline template for an Angular component.
|
* Specifies an inline template for an Angular component.
|
||||||
*
|
*
|
||||||
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
|
* Only one of `templateUrl` or `template` can be defined per Component.
|
||||||
*/
|
*/
|
||||||
template?: string;
|
template?: string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies stylesheet URLs for an Angular component.
|
* Specifies stylesheet URLs for an Angular component.
|
||||||
*
|
|
||||||
* <!-- TODO: what's the url relative to? -->
|
|
||||||
*/
|
*/
|
||||||
styleUrls?: string[];
|
styleUrls?: string[];
|
||||||
|
|
||||||
@ -1026,20 +655,33 @@ export interface Component extends Directive {
|
|||||||
animations?: AnimationEntryMetadata[];
|
animations?: AnimationEntryMetadata[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specify how the template and the styles should be encapsulated.
|
* Specifies how the template and the styles should be encapsulated:
|
||||||
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
|
* - {@link ViewEncapsulation#Native `ViewEncapsulation.Native`} to use shadow roots - only works
|
||||||
* has styles,
|
* if natively available on the platform,
|
||||||
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
|
* - {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} to use shimmed CSS that
|
||||||
|
* emulates the native behavior,
|
||||||
|
* - {@link ViewEncapsulation#None `ViewEncapsulation.None`} to use global CSS without any
|
||||||
|
* encapsulation.
|
||||||
|
*
|
||||||
|
* When no `encapsulation` is defined for the component, the default value from the
|
||||||
|
* {@link CompilerConfig} is used. The default is `ViewEncapsulation.Emulated`}. Provide a new
|
||||||
|
* `CompilerConfig` to override this value.
|
||||||
|
*
|
||||||
|
* If the encapsulation is set to `ViewEncapsulation.Emulated` and the component has no `styles`
|
||||||
|
* nor `styleUrls` the encapsulation will automatically be switched to `ViewEncapsulation.None`.
|
||||||
*/
|
*/
|
||||||
encapsulation?: ViewEncapsulation;
|
encapsulation?: ViewEncapsulation;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Overrides the default encapsulation start and end delimiters (respectively `{{` and `}}`)
|
||||||
|
*/
|
||||||
interpolation?: [string, string];
|
interpolation?: [string, string];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines the components that should be compiled as well when
|
* Defines the components that should be compiled as well when
|
||||||
* this component is defined. For each components listed here,
|
* this component is defined. For each components listed here,
|
||||||
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
|
* Angular will create a {@link ComponentFactory} and store it in the
|
||||||
* {@link ComponentFactoryResolver ComponentFactoryResolver}.
|
* {@link ComponentFactoryResolver}.
|
||||||
*/
|
*/
|
||||||
entryComponents?: Array<Type<any>|any[]>;
|
entryComponents?: Array<Type<any>|any[]>;
|
||||||
}
|
}
|
||||||
|
@ -36,447 +36,123 @@ export var LIFECYCLE_HOOKS_VALUES = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lifecycle hooks are guaranteed to be called in the following order:
|
* @whatItDoes Lifecycle hook that is called when any data-bound property of a directive changes.
|
||||||
* - `OnChanges` (if any bindings have changed),
|
* @howToUse
|
||||||
* - `OnInit` (after the first check only),
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnChanges'}
|
||||||
* - `DoCheck`,
|
|
||||||
* - `AfterContentInit`,
|
|
||||||
* - `AfterContentChecked`,
|
|
||||||
* - `AfterViewInit`,
|
|
||||||
* - `AfterViewChecked`,
|
|
||||||
* - `OnDestroy` (at the very end before destruction)
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implement this interface to get notified when any data-bound property of your directive changes.
|
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
* `ngOnChanges` is called right after the data-bound properties have been checked and before view
|
* `ngOnChanges` is called right after the data-bound properties have been checked and before view
|
||||||
* and content children are checked if at least one of them has changed.
|
* and content children are checked if at least one of them has changed.
|
||||||
|
* The `changes` parameter contains the changed properties.
|
||||||
*
|
*
|
||||||
* The `changes` parameter contains an entry for each of the changed data-bound property. The key is
|
* See {@linkDocs guide/lifecycle-hooks#onchanges "Lifecycle Hooks Guide"}.
|
||||||
* the property name and the value is an instance of {@link SimpleChange}.
|
|
||||||
*
|
*
|
||||||
* ### Example ([live example](http://plnkr.co/edit/AHrB6opLqHDBPkt4KpdT?p=preview)):
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-cmp',
|
|
||||||
* template: `<p>myProp = {{myProp}}</p>`
|
|
||||||
* })
|
|
||||||
* class MyComponent implements OnChanges {
|
|
||||||
* @Input() myProp: any;
|
|
||||||
*
|
|
||||||
* ngOnChanges(changes: SimpleChanges) {
|
|
||||||
* console.log('ngOnChanges - myProp = ' + changes['myProp'].currentValue);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <button (click)="value = value + 1">Change MyComponent</button>
|
|
||||||
* <my-cmp [my-prop]="value"></my-cmp>`,
|
|
||||||
* directives: [MyComponent]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* value = 0;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class OnChanges { abstract ngOnChanges(changes: SimpleChanges): void; }
|
export abstract class OnChanges { abstract ngOnChanges(changes: SimpleChanges): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to execute custom initialization logic after your directive's
|
* @whatItDoes Lifecycle hook that is called after data-bound properties of a directive are
|
||||||
* data-bound properties have been initialized.
|
* initialized.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnInit'}
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
* `ngOnInit` is called right after the directive's data-bound properties have been checked for the
|
* `ngOnInit` is called right after the directive's data-bound properties have been checked for the
|
||||||
* first time, and before any of its children have been checked. It is invoked only once when the
|
* first time, and before any of its children have been checked. It is invoked only once when the
|
||||||
* directive is instantiated.
|
* directive is instantiated.
|
||||||
*
|
*
|
||||||
* ### Example ([live example](http://plnkr.co/edit/1MBypRryXd64v4pV03Yn?p=preview))
|
* See {@linkDocs guide/lifecycle-hooks "Lifecycle Hooks Guide"}.
|
||||||
*
|
*
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-cmp',
|
|
||||||
* template: `<p>my-component</p>`
|
|
||||||
* })
|
|
||||||
* class MyComponent implements OnInit, OnDestroy {
|
|
||||||
* ngOnInit() {
|
|
||||||
* console.log('ngOnInit');
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngOnDestroy() {
|
|
||||||
* console.log('ngOnDestroy');
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <button (click)="hasChild = !hasChild">
|
|
||||||
* {{hasChild ? 'Destroy' : 'Create'}} MyComponent
|
|
||||||
* </button>
|
|
||||||
* <my-cmp *ngIf="hasChild"></my-cmp>`,
|
|
||||||
* directives: [MyComponent, NgIf]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* hasChild = true;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class OnInit { abstract ngOnInit(): void; }
|
export abstract class OnInit { abstract ngOnInit(): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to supplement the default change detection algorithm in your directive.
|
* @whatItDoes Lifecycle hook that is called when Angular dirty checks a directive.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='DoCheck'}
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
* `ngDoCheck` gets called to check the changes in the directives in addition to the default
|
* `ngDoCheck` gets called to check the changes in the directives in addition to the default
|
||||||
* algorithm.
|
* algorithm. The default change detection algorithm looks for differences by comparing
|
||||||
*
|
* bound-property values by reference across change detection runs.
|
||||||
* The default change detection algorithm looks for differences by comparing bound-property values
|
|
||||||
* by reference across change detection runs.
|
|
||||||
*
|
*
|
||||||
* Note that a directive typically should not use both `DoCheck` and {@link OnChanges} to respond to
|
* Note that a directive typically should not use both `DoCheck` and {@link OnChanges} to respond to
|
||||||
* changes on the same input. `ngOnChanges` will continue to be called when the default change
|
* changes on the same input, as `ngOnChanges` will continue to be called when the default change
|
||||||
* detector
|
* detector detects changes.
|
||||||
* detects changes, so it is usually unnecessary to respond to changes on the same input in both
|
|
||||||
* hooks.
|
|
||||||
* Reaction to the changes have to be handled from within the `ngDoCheck` callback.
|
|
||||||
*
|
*
|
||||||
* You can use {@link KeyValueDiffers} and {@link IterableDiffers} to help add your custom check
|
* See {@link KeyValueDiffers} and {@link IterableDiffers} for implementing custom dirty checking
|
||||||
* mechanisms.
|
* for collections.
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/QpnIlF0CR2i5bcYbHEUJ?p=preview))
|
* See {@linkDocs guide/lifecycle-hooks#docheck "Lifecycle Hooks Guide"}.
|
||||||
*
|
*
|
||||||
* In the following example `ngDoCheck` uses an {@link IterableDiffers} to detect the updates to the
|
|
||||||
* array `list`:
|
|
||||||
*
|
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'custom-check',
|
|
||||||
* template: `
|
|
||||||
* <p>Changes:</p>
|
|
||||||
* <ul>
|
|
||||||
* <li *ngFor="let line of logs">{{line}}</li>
|
|
||||||
* </ul>`,
|
|
||||||
* directives: [NgFor]
|
|
||||||
* })
|
|
||||||
* class CustomCheckComponent implements DoCheck {
|
|
||||||
* @Input() list: any[];
|
|
||||||
* differ: any;
|
|
||||||
* logs = [];
|
|
||||||
*
|
|
||||||
* constructor(differs: IterableDiffers) {
|
|
||||||
* this.differ = differs.find([]).create(null);
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngDoCheck() {
|
|
||||||
* var changes = this.differ.diff(this.list);
|
|
||||||
*
|
|
||||||
* if (changes) {
|
|
||||||
* changes.forEachAddedItem(r => this.logs.push('added ' + r.item));
|
|
||||||
* changes.forEachRemovedItem(r => this.logs.push('removed ' + r.item))
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <button (click)="list.push(list.length)">Push</button>
|
|
||||||
* <button (click)="list.pop()">Pop</button>
|
|
||||||
* <custom-check [list]="list"></custom-check>`,
|
|
||||||
* directives: [CustomCheckComponent]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* list = [];
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class DoCheck { abstract ngDoCheck(): void; }
|
export abstract class DoCheck { abstract ngDoCheck(): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to get notified when your directive is destroyed.
|
* @whatItDoes Lifecycle hook that is called when a directive or pipe is destroyed.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='OnDestroy'}
|
||||||
*
|
*
|
||||||
|
* @description
|
||||||
* `ngOnDestroy` callback is typically used for any custom cleanup that needs to occur when the
|
* `ngOnDestroy` callback is typically used for any custom cleanup that needs to occur when the
|
||||||
* instance is destroyed
|
* instance is destroyed.
|
||||||
*
|
*
|
||||||
* ### Example ([live example](http://plnkr.co/edit/1MBypRryXd64v4pV03Yn?p=preview))
|
* See {@linkDocs guide/lifecycle-hooks "Lifecycle Hooks Guide"}.
|
||||||
*
|
|
||||||
* ```typesript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'my-cmp',
|
|
||||||
* template: `<p>my-component</p>`
|
|
||||||
* })
|
|
||||||
* class MyComponent implements OnInit, OnDestroy {
|
|
||||||
* ngOnInit() {
|
|
||||||
* console.log('ngOnInit');
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngOnDestroy() {
|
|
||||||
* console.log('ngOnDestroy');
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <button (click)="hasChild = !hasChild">
|
|
||||||
* {{hasChild ? 'Destroy' : 'Create'}} MyComponent
|
|
||||||
* </button>
|
|
||||||
* <my-cmp *ngIf="hasChild"></my-cmp>`,
|
|
||||||
* directives: [MyComponent, NgIf]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* hasChild = true;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* To create a stateful Pipe, you should implement this interface and set the `pure`
|
|
||||||
* parameter to `false` in the {@link Pipe}.
|
|
||||||
*
|
|
||||||
* A stateful pipe may produce different output, given the same input. It is
|
|
||||||
* likely that a stateful pipe may contain state that should be cleaned up when
|
|
||||||
* a binding is destroyed. For example, a subscription to a stream of data may need to
|
|
||||||
* be disposed, or an interval may need to be cleared.
|
|
||||||
*
|
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/i8pm5brO4sPaLxBx56MR?p=preview))
|
|
||||||
*
|
|
||||||
* In this example, a pipe is created to countdown its input value, updating it every
|
|
||||||
* 50ms. Because it maintains an internal interval, it automatically clears
|
|
||||||
* the interval when the binding is destroyed or the countdown completes.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* import {OnDestroy, Pipe, PipeTransform} from '@angular/core'
|
|
||||||
* @Pipe({name: 'countdown', pure: false})
|
|
||||||
* class CountDown implements PipeTransform, OnDestroy {
|
|
||||||
* remainingTime:Number;
|
|
||||||
* interval:SetInterval;
|
|
||||||
* ngOnDestroy() {
|
|
||||||
* if (this.interval) {
|
|
||||||
* clearInterval(this.interval);
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* transform(value: any, args: any[] = []) {
|
|
||||||
* if (!parseInt(value, 10)) return null;
|
|
||||||
* if (typeof this.remainingTime !== 'number') {
|
|
||||||
* this.remainingTime = parseInt(value, 10);
|
|
||||||
* }
|
|
||||||
* if (!this.interval) {
|
|
||||||
* this.interval = setInterval(() => {
|
|
||||||
* this.remainingTime-=50;
|
|
||||||
* if (this.remainingTime <= 0) {
|
|
||||||
* this.remainingTime = 0;
|
|
||||||
* clearInterval(this.interval);
|
|
||||||
* delete this.interval;
|
|
||||||
* }
|
|
||||||
* }, 50);
|
|
||||||
* }
|
|
||||||
* return this.remainingTime;
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
* Invoking `{{ 10000 | countdown }}` would cause the value to be decremented by 50,
|
|
||||||
* every 50ms, until it reaches 0.
|
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class OnDestroy { abstract ngOnDestroy(): void; }
|
export abstract class OnDestroy { abstract ngOnDestroy(): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to get notified when your directive's content has been fully
|
*
|
||||||
|
* @whatItDoes Lifecycle hook that is called after a directive's content has been fully
|
||||||
* initialized.
|
* initialized.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterContentInit'}
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/plamXUpsLQbIXpViZhUO?p=preview))
|
* @description
|
||||||
|
* See {@linkDocs guide/lifecycle-hooks#aftercontent "Lifecycle Hooks Guide"}.
|
||||||
*
|
*
|
||||||
* ```typescript
|
|
||||||
* @Component({
|
|
||||||
* selector: 'child-cmp',
|
|
||||||
* template: `{{where}} child`
|
|
||||||
* })
|
|
||||||
* class ChildComponent {
|
|
||||||
* @Input() where: string;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'parent-cmp',
|
|
||||||
* template: `<ng-content></ng-content>`
|
|
||||||
* })
|
|
||||||
* class ParentComponent implements AfterContentInit {
|
|
||||||
* @ContentChild(ChildComponent) contentChild: ChildComponent;
|
|
||||||
*
|
|
||||||
* constructor() {
|
|
||||||
* // contentChild is not initialized yet
|
|
||||||
* console.log(this.getMessage(this.contentChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngAfterContentInit() {
|
|
||||||
* // contentChild is updated after the content has been checked
|
|
||||||
* console.log('AfterContentInit: ' + this.getMessage(this.contentChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private getMessage(cmp: ChildComponent): string {
|
|
||||||
* return cmp ? cmp.where + ' child' : 'no child';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <parent-cmp>
|
|
||||||
* <child-cmp where="content"></child-cmp>
|
|
||||||
* </parent-cmp>`,
|
|
||||||
* directives: [ParentComponent, ChildComponent]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class AfterContentInit { abstract ngAfterContentInit(): void; }
|
export abstract class AfterContentInit { abstract ngAfterContentInit(): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to get notified after every check of your directive's content.
|
* @whatItDoes Lifecycle hook that is called after every check of a directive's content.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterContentChecked'}
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/tGdrytNEKQnecIPkD7NU?p=preview))
|
* @description
|
||||||
|
* See {@linkDocs guide/lifecycle-hooks#aftercontent "Lifecycle Hooks Guide"}.
|
||||||
*
|
*
|
||||||
* ```typescript
|
|
||||||
* @Component({selector: 'child-cmp', template: `{{where}} child`})
|
|
||||||
* class ChildComponent {
|
|
||||||
* @Input() where: string;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({selector: 'parent-cmp', template: `<ng-content></ng-content>`})
|
|
||||||
* class ParentComponent implements AfterContentChecked {
|
|
||||||
* @ContentChild(ChildComponent) contentChild: ChildComponent;
|
|
||||||
*
|
|
||||||
* constructor() {
|
|
||||||
* // contentChild is not initialized yet
|
|
||||||
* console.log(this.getMessage(this.contentChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngAfterContentChecked() {
|
|
||||||
* // contentChild is updated after the content has been checked
|
|
||||||
* console.log('AfterContentChecked: ' + this.getMessage(this.contentChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private getMessage(cmp: ChildComponent): string {
|
|
||||||
* return cmp ? cmp.where + ' child' : 'no child';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `
|
|
||||||
* <parent-cmp>
|
|
||||||
* <button (click)="hasContent = !hasContent">Toggle content child</button>
|
|
||||||
* <child-cmp *ngIf="hasContent" where="content"></child-cmp>
|
|
||||||
* </parent-cmp>`,
|
|
||||||
* directives: [NgIf, ParentComponent, ChildComponent]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* hasContent = true;
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class AfterContentChecked { abstract ngAfterContentChecked(): void; }
|
export abstract class AfterContentChecked { abstract ngAfterContentChecked(): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to get notified when your component's view has been fully initialized.
|
* @whatItDoes Lifecycle hook that is called after a component's view has been fully
|
||||||
|
* initialized.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterViewInit'}
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/LhTKVMEM0fkJgyp4CI1W?p=preview))
|
* @description
|
||||||
|
* See {@linkDocs guide/lifecycle-hooks#afterview "Lifecycle Hooks Guide"}.
|
||||||
*
|
*
|
||||||
* ```typescript
|
|
||||||
* @Component({selector: 'child-cmp', template: `{{where}} child`})
|
|
||||||
* class ChildComponent {
|
|
||||||
* @Input() where: string;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'parent-cmp',
|
|
||||||
* template: `<child-cmp where="view"></child-cmp>`,
|
|
||||||
* directives: [ChildComponent]
|
|
||||||
* })
|
|
||||||
* class ParentComponent implements AfterViewInit {
|
|
||||||
* @ViewChild(ChildComponent) viewChild: ChildComponent;
|
|
||||||
*
|
|
||||||
* constructor() {
|
|
||||||
* // viewChild is not initialized yet
|
|
||||||
* console.log(this.getMessage(this.viewChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngAfterViewInit() {
|
|
||||||
* // viewChild is updated after the view has been initialized
|
|
||||||
* console.log('ngAfterViewInit: ' + this.getMessage(this.viewChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private getMessage(cmp: ChildComponent): string {
|
|
||||||
* return cmp ? cmp.where + ' child' : 'no child';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `<parent-cmp></parent-cmp>`,
|
|
||||||
* directives: [ParentComponent]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class AfterViewInit { abstract ngAfterViewInit(): void; }
|
export abstract class AfterViewInit { abstract ngAfterViewInit(): void; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implement this interface to get notified after every check of your component's view.
|
* @whatItDoes Lifecycle hook that is called after every check of a component's view.
|
||||||
|
* @howToUse
|
||||||
|
* {@example core/ts/metadata/lifecycle_hooks_spec.ts region='AfterViewChecked'}
|
||||||
*
|
*
|
||||||
* ### Example ([live demo](http://plnkr.co/edit/0qDGHcPQkc25CXhTNzKU?p=preview))
|
* @description
|
||||||
|
* See {@linkDocs guide/lifecycle-hooks#afterview "Lifecycle Hooks Guide"}.
|
||||||
*
|
*
|
||||||
* ```typescript
|
|
||||||
* @Component({selector: 'child-cmp', template: `{{where}} child`})
|
|
||||||
* class ChildComponent {
|
|
||||||
* @Input() where: string;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'parent-cmp',
|
|
||||||
* template: `
|
|
||||||
* <button (click)="showView = !showView">Toggle view child</button>
|
|
||||||
* <child-cmp *ngIf="showView" where="view"></child-cmp>`,
|
|
||||||
* directives: [NgIf, ChildComponent]
|
|
||||||
* })
|
|
||||||
* class ParentComponent implements AfterViewChecked {
|
|
||||||
* @ViewChild(ChildComponent) viewChild: ChildComponent;
|
|
||||||
* showView = true;
|
|
||||||
*
|
|
||||||
* constructor() {
|
|
||||||
* // viewChild is not initialized yet
|
|
||||||
* console.log(this.getMessage(this.viewChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* ngAfterViewChecked() {
|
|
||||||
* // viewChild is updated after the view has been checked
|
|
||||||
* console.log('AfterViewChecked: ' + this.getMessage(this.viewChild));
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* private getMessage(cmp: ChildComponent): string {
|
|
||||||
* return cmp ? cmp.where + ' child' : 'no child';
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'app',
|
|
||||||
* template: `<parent-cmp></parent-cmp>`,
|
|
||||||
* directives: [ParentComponent]
|
|
||||||
* })
|
|
||||||
* export class App {
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export abstract class AfterViewChecked { abstract ngAfterViewChecked(): void; }
|
export abstract class AfterViewChecked { abstract ngAfterViewChecked(): void; }
|
||||||
|
@ -7,8 +7,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {AnimationEntryMetadata} from '../animation/metadata';
|
import {AnimationEntryMetadata} from '../animation/metadata';
|
||||||
import {Type} from '../type';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines template and style encapsulation options available for Component's {@link Component}.
|
* Defines template and style encapsulation options available for Component's {@link Component}.
|
||||||
@ -46,13 +44,6 @@ export var VIEW_ENCAPSULATION_VALUES =
|
|||||||
/**
|
/**
|
||||||
* Metadata properties available for configuring Views.
|
* Metadata properties available for configuring Views.
|
||||||
*
|
*
|
||||||
* Each Angular component requires a single `@Component` and at least one `@View` annotation. The
|
|
||||||
* `@View` annotation specifies the HTML template to use, and lists the directives that are active
|
|
||||||
* within the template.
|
|
||||||
*
|
|
||||||
* When a component is instantiated, the template is loaded into the component's shadow root, and
|
|
||||||
* the expressions and statements in the template are evaluated against the component.
|
|
||||||
*
|
|
||||||
* For details on the `@Component` annotation, see {@link Component}.
|
* For details on the `@Component` annotation, see {@link Component}.
|
||||||
*
|
*
|
||||||
* ### Example
|
* ### Example
|
||||||
@ -61,7 +52,6 @@ export var VIEW_ENCAPSULATION_VALUES =
|
|||||||
* @Component({
|
* @Component({
|
||||||
* selector: 'greet',
|
* selector: 'greet',
|
||||||
* template: 'Hello {{name}}!',
|
* template: 'Hello {{name}}!',
|
||||||
* directives: [GreetUser, Bold]
|
|
||||||
* })
|
* })
|
||||||
* class Greet {
|
* class Greet {
|
||||||
* name: string;
|
* name: string;
|
||||||
@ -73,46 +63,23 @@ export var VIEW_ENCAPSULATION_VALUES =
|
|||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* @deprecated Use Component instead.
|
* @deprecated Use Component instead.
|
||||||
|
*
|
||||||
|
* {@link Component}
|
||||||
*/
|
*/
|
||||||
export class ViewMetadata {
|
export class ViewMetadata {
|
||||||
/**
|
/** {@link Component.templateUrl} */
|
||||||
* Specifies a template URL for an Angular component.
|
|
||||||
*
|
|
||||||
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
|
|
||||||
*
|
|
||||||
* <!-- TODO: what's the url relative to? -->
|
|
||||||
*/
|
|
||||||
templateUrl: string;
|
templateUrl: string;
|
||||||
|
/** {@link Component.template} */
|
||||||
/**
|
|
||||||
* Specifies an inline template for an Angular component.
|
|
||||||
*
|
|
||||||
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
|
|
||||||
*/
|
|
||||||
template: string;
|
template: string;
|
||||||
|
/** {@link Component.stylesUrl} */
|
||||||
/**
|
|
||||||
* Specifies stylesheet URLs for an Angular component.
|
|
||||||
*
|
|
||||||
* <!-- TODO: what's the url relative to? -->
|
|
||||||
*/
|
|
||||||
styleUrls: string[];
|
styleUrls: string[];
|
||||||
|
/** {@link Component.styles} */
|
||||||
/**
|
|
||||||
* Specifies an inline stylesheet for an Angular component.
|
|
||||||
*/
|
|
||||||
styles: string[];
|
styles: string[];
|
||||||
|
/** {@link Component.encapsulation} */
|
||||||
/**
|
|
||||||
* Specify how the template and the styles should be encapsulated.
|
|
||||||
* The default is {@link ViewEncapsulation#Emulated `ViewEncapsulation.Emulated`} if the view
|
|
||||||
* has styles,
|
|
||||||
* otherwise {@link ViewEncapsulation#None `ViewEncapsulation.None`}.
|
|
||||||
*/
|
|
||||||
encapsulation: ViewEncapsulation;
|
encapsulation: ViewEncapsulation;
|
||||||
|
/** {@link Component.animation} */
|
||||||
animations: AnimationEntryMetadata[];
|
animations: AnimationEntryMetadata[];
|
||||||
|
/** {@link Component.interpolation} */
|
||||||
interpolation: [string, string];
|
interpolation: [string, string];
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref';
|
import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref';
|
||||||
import {Console} from './console';
|
import {Console} from './console';
|
||||||
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './di';
|
import {Provider} from './di';
|
||||||
import {Reflector, reflector} from './reflection/reflection';
|
import {Reflector, reflector} from './reflection/reflection';
|
||||||
import {ReflectorReader} from './reflection/reflector_reader';
|
import {ReflectorReader} from './reflection/reflector_reader';
|
||||||
import {TestabilityRegistry} from './testability/testability';
|
import {TestabilityRegistry} from './testability/testability';
|
||||||
@ -18,9 +18,12 @@ function _reflector(): Reflector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const _CORE_PLATFORM_PROVIDERS: Provider[] = [
|
const _CORE_PLATFORM_PROVIDERS: Provider[] = [
|
||||||
PlatformRef_, {provide: PlatformRef, useExisting: PlatformRef_},
|
PlatformRef_,
|
||||||
|
{provide: PlatformRef, useExisting: PlatformRef_},
|
||||||
{provide: Reflector, useFactory: _reflector, deps: []},
|
{provide: Reflector, useFactory: _reflector, deps: []},
|
||||||
{provide: ReflectorReader, useExisting: Reflector}, TestabilityRegistry, Console
|
{provide: ReflectorReader, useExisting: Reflector},
|
||||||
|
TestabilityRegistry,
|
||||||
|
Console,
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {Map, MapWrapper, Set, SetWrapper, StringMapWrapper} from '../facade/collection';
|
import {MapWrapper, SetWrapper, StringMapWrapper} from '../facade/collection';
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {Type} from '../type';
|
import {Type} from '../type';
|
||||||
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
|
||||||
@ -40,14 +40,9 @@ export class Reflector extends ReflectorReader {
|
|||||||
/** @internal */
|
/** @internal */
|
||||||
_methods = new Map<string, MethodFn>();
|
_methods = new Map<string, MethodFn>();
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_usedKeys: Set<any>;
|
_usedKeys: Set<any> = null;
|
||||||
reflectionCapabilities: PlatformReflectionCapabilities;
|
|
||||||
|
|
||||||
constructor(reflectionCapabilities: PlatformReflectionCapabilities) {
|
constructor(public reflectionCapabilities: PlatformReflectionCapabilities) { super(); }
|
||||||
super();
|
|
||||||
this._usedKeys = null;
|
|
||||||
this.reflectionCapabilities = reflectionCapabilities;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; }
|
updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; }
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {Injectable} from '../di';
|
import {Injectable} from '../di';
|
||||||
import {Map, MapWrapper} from '../facade/collection';
|
import {MapWrapper} from '../facade/collection';
|
||||||
import {scheduleMicroTask} from '../facade/lang';
|
import {scheduleMicroTask} from '../facade/lang';
|
||||||
import {NgZone} from '../zone/ng_zone';
|
import {NgZone} from '../zone/ng_zone';
|
||||||
|
|
||||||
@ -110,6 +110,7 @@ export class Testability implements PublicTestability {
|
|||||||
|
|
||||||
getPendingRequestCount(): number { return this._pendingCount; }
|
getPendingRequestCount(): number { return this._pendingCount; }
|
||||||
|
|
||||||
|
/** @deprecated use findProviders */
|
||||||
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
|
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
|
||||||
// TODO(juliemr): implement.
|
// TODO(juliemr): implement.
|
||||||
return [];
|
return [];
|
||||||
|
@ -7,14 +7,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runtime representation a type that a Component or other object is instances of.
|
* @whatItDoes Represents a type that a Component or other object is instances of.
|
||||||
|
*
|
||||||
|
* @description
|
||||||
*
|
*
|
||||||
* An example of a `Type` is `MyCustomComponent` class, which in JavaScript is be represented by
|
* An example of a `Type` is `MyCustomComponent` class, which in JavaScript is be represented by
|
||||||
* the `MyCustomComponent` constructor function.
|
* the `MyCustomComponent` constructor function.
|
||||||
*
|
*
|
||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export var Type = Function;
|
export const Type = Function;
|
||||||
|
|
||||||
|
|
||||||
export interface Type<T> extends Function { new (...args: any[]): T; }
|
export interface Type<T> extends Function { new (...args: any[]): T; }
|
||||||
|
13
modules/@angular/core/src/util/lang.ts
Normal file
13
modules/@angular/core/src/util/lang.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function isPromise(obj: any): obj is Promise<any> {
|
||||||
|
// allow any Promise/A+ compliant thenable.
|
||||||
|
// It's up to the caller to ensure that obj.then conforms to the spec
|
||||||
|
return !!obj && typeof obj.then === 'function';
|
||||||
|
}
|
@ -6,11 +6,11 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core';
|
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core';
|
||||||
import {TestBed, async} from '@angular/core/testing';
|
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
|
||||||
import {expect} from '@angular/platform-browser/testing/matchers';
|
import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
|
|
||||||
import {isPresent, stringify} from '../../src/facade/lang';
|
import {stringify} from '../../src/facade/lang';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('Query API', () => {
|
describe('Query API', () => {
|
||||||
@ -54,10 +54,7 @@ export function main() {
|
|||||||
'<div text="too-deep"></div>' +
|
'<div text="too-deep"></div>' +
|
||||||
'</div></needs-query>' +
|
'</div></needs-query>' +
|
||||||
'<div text="4"></div>';
|
'<div text="4"></div>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|');
|
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|');
|
||||||
});
|
});
|
||||||
@ -65,15 +62,10 @@ export function main() {
|
|||||||
it('should contain all direct child directives in the content dom', () => {
|
it('should contain all direct child directives in the content dom', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-content-children #q><div text="foo"></div></needs-content-children>';
|
'<needs-content-children #q><div text="foo"></div></needs-content-children>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
|
const q = view.debugElement.children[0].references['q'];
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.textDirChildren.length).toEqual(1);
|
expect(q.textDirChildren.length).toEqual(1);
|
||||||
expect(q.numberOfChildrenAfterContentInit).toEqual(1);
|
expect(q.numberOfChildrenAfterContentInit).toEqual(1);
|
||||||
});
|
});
|
||||||
@ -81,19 +73,14 @@ export function main() {
|
|||||||
it('should contain the first content child', () => {
|
it('should contain the first content child', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-content-child #q><div *ngIf="shouldShow" text="foo"></div></needs-content-child>';
|
'<needs-content-child #q><div *ngIf="shouldShow" text="foo"></div></needs-content-child>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmp(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.componentInstance.shouldShow = true;
|
view.componentInstance.shouldShow = true;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
const q: NeedsContentChild = view.debugElement.children[0].references['q'];
|
||||||
var q: NeedsContentChild = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
||||||
|
|
||||||
view.componentInstance.shouldShow = false;
|
view.componentInstance.shouldShow = false;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.logs).toEqual([
|
expect(q.logs).toEqual([
|
||||||
['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null]
|
['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null]
|
||||||
]);
|
]);
|
||||||
@ -101,17 +88,13 @@ export function main() {
|
|||||||
|
|
||||||
it('should contain the first view child', () => {
|
it('should contain the first view child', () => {
|
||||||
const template = '<needs-view-child #q></needs-view-child>';
|
const template = '<needs-view-child #q></needs-view-child>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
var q: NeedsViewChild = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
|
const q: NeedsViewChild = view.debugElement.children[0].references['q'];
|
||||||
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
||||||
|
|
||||||
q.shouldShow = false;
|
q.shouldShow = false;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.logs).toEqual([
|
expect(q.logs).toEqual([
|
||||||
['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null]
|
['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null]
|
||||||
]);
|
]);
|
||||||
@ -120,20 +103,17 @@ export function main() {
|
|||||||
it('should set static view and content children already after the constructor call', () => {
|
it('should set static view and content children already after the constructor call', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
|
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmp(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references['q'];
|
|
||||||
expect(q.contentChild.text).toBeFalsy();
|
expect(q.contentChild.text).toBeFalsy();
|
||||||
expect(q.viewChild.text).toBeFalsy();
|
expect(q.viewChild.text).toBeFalsy();
|
||||||
|
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.contentChild.text).toEqual('contentFoo');
|
expect(q.contentChild.text).toEqual('contentFoo');
|
||||||
expect(q.viewChild.text).toEqual('viewFoo');
|
expect(q.viewChild.text).toEqual('viewFoo');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain the first view child accross embedded views', () => {
|
it('should contain the first view child across embedded views', () => {
|
||||||
TestBed.overrideComponent(
|
TestBed.overrideComponent(
|
||||||
MyComp0, {set: {template: '<needs-view-child #q></needs-view-child>'}});
|
MyComp0, {set: {template: '<needs-view-child #q></needs-view-child>'}});
|
||||||
TestBed.overrideComponent(NeedsViewChild, {
|
TestBed.overrideComponent(NeedsViewChild, {
|
||||||
@ -145,22 +125,19 @@ export function main() {
|
|||||||
const view = TestBed.createComponent(MyComp0);
|
const view = TestBed.createComponent(MyComp0);
|
||||||
|
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
var q: NeedsViewChild = view.debugElement.children[0].references['q'];
|
const q: NeedsViewChild = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
|
||||||
|
|
||||||
q.shouldShow = false;
|
q.shouldShow = false;
|
||||||
q.shouldShow2 = true;
|
q.shouldShow2 = true;
|
||||||
q.logs = [];
|
q.logs = [];
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.logs).toEqual([['setter', 'bar'], ['check', 'bar']]);
|
expect(q.logs).toEqual([['setter', 'bar'], ['check', 'bar']]);
|
||||||
|
|
||||||
q.shouldShow = false;
|
q.shouldShow = false;
|
||||||
q.shouldShow2 = false;
|
q.shouldShow2 = false;
|
||||||
q.logs = [];
|
q.logs = [];
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.logs).toEqual([['setter', null], ['check', null]]);
|
expect(q.logs).toEqual([['setter', null], ['check', null]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -170,10 +147,8 @@ export function main() {
|
|||||||
'<div text="4"></div>' +
|
'<div text="4"></div>' +
|
||||||
'</div></needs-query-desc>' +
|
'</div></needs-query-desc>' +
|
||||||
'<div text="5"></div>';
|
'<div text="5"></div>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|4|');
|
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|4|');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -181,10 +156,8 @@ export function main() {
|
|||||||
const template = '<div text="1"></div>' +
|
const template = '<div text="1"></div>' +
|
||||||
'<needs-query text="2"><div text="3"></div></needs-query>' +
|
'<needs-query text="2"><div text="3"></div></needs-query>' +
|
||||||
'<div text="4"></div>';
|
'<div text="4"></div>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|');
|
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -192,11 +165,7 @@ export function main() {
|
|||||||
const template = '<div text="1"></div>' +
|
const template = '<div text="1"></div>' +
|
||||||
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
||||||
'<div text="4"></div>';
|
'<div text="4"></div>';
|
||||||
;
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
expect(asNativeElements(view.debugElement.children)).toHaveText('2|');
|
expect(asNativeElements(view.debugElement.children)).toHaveText('2|');
|
||||||
|
|
||||||
view.componentInstance.shouldShow = true;
|
view.componentInstance.shouldShow = true;
|
||||||
@ -208,24 +177,18 @@ export function main() {
|
|||||||
const template = '<div text="1"></div>' +
|
const template = '<div text="1"></div>' +
|
||||||
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
|
||||||
'<div text="4"></div>';
|
'<div text="4"></div>';
|
||||||
;
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
|
||||||
const fixture = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
fixture.componentInstance.shouldShow = true;
|
view.componentInstance.shouldShow = true;
|
||||||
fixture.detectChanges();
|
view.detectChanges();
|
||||||
fixture.destroy();
|
view.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reflect moved directives', () => {
|
it('should reflect moved directives', () => {
|
||||||
const template = '<div text="1"></div>' +
|
const template = '<div text="1"></div>' +
|
||||||
'<needs-query text="2"><div *ngFor="let i of list" [text]="i"></div></needs-query>' +
|
'<needs-query text="2"><div *ngFor="let i of list" [text]="i"></div></needs-query>' +
|
||||||
'<div text="4"></div>';
|
'<div text="4"></div>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(asNativeElements(view.debugElement.children)).toHaveText('2|1d|2d|3d|');
|
expect(asNativeElements(view.debugElement.children)).toHaveText('2|1d|2d|3d|');
|
||||||
|
|
||||||
view.componentInstance.list = ['3d', '2d'];
|
view.componentInstance.list = ['3d', '2d'];
|
||||||
@ -246,11 +209,8 @@ export function main() {
|
|||||||
describe('query for TemplateRef', () => {
|
describe('query for TemplateRef', () => {
|
||||||
it('should find TemplateRefs in the light and shadow dom', () => {
|
it('should find TemplateRefs in the light and shadow dom', () => {
|
||||||
const template = '<needs-tpl><template><div>light</div></template></needs-tpl>';
|
const template = '<needs-tpl><template><div>light</div></template></needs-tpl>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl);
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
var needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl);
|
|
||||||
|
|
||||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).rootNodes[0])
|
expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).rootNodes[0])
|
||||||
.toHaveText('light');
|
.toHaveText('light');
|
||||||
@ -261,11 +221,8 @@ export function main() {
|
|||||||
it('should find named TemplateRefs', () => {
|
it('should find named TemplateRefs', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-named-tpl><template #tpl><div>light</div></template></needs-named-tpl>';
|
'<needs-named-tpl><template #tpl><div>light</div></template></needs-named-tpl>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const needsTpl: NeedsNamedTpl = view.debugElement.children[0].injector.get(NeedsNamedTpl);
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
var needsTpl: NeedsNamedTpl = view.debugElement.children[0].injector.get(NeedsNamedTpl);
|
|
||||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).rootNodes[0])
|
expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).rootNodes[0])
|
||||||
.toHaveText('light');
|
.toHaveText('light');
|
||||||
expect(needsTpl.vc.createEmbeddedView(needsTpl.viewTpl).rootNodes[0]).toHaveText('shadow');
|
expect(needsTpl.vc.createEmbeddedView(needsTpl.viewTpl).rootNodes[0]).toHaveText('shadow');
|
||||||
@ -276,12 +233,9 @@ export function main() {
|
|||||||
it('should contain all content children', () => {
|
it('should contain all content children', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-content-children-read #q text="ca"><div #q text="cb"></div></needs-content-children-read>';
|
'<needs-content-children-read #q text="ca"><div #q text="cb"></div></needs-content-children-read>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
const comp: NeedsContentChildrenWithRead =
|
||||||
|
|
||||||
var comp: NeedsContentChildrenWithRead =
|
|
||||||
view.debugElement.children[0].injector.get(NeedsContentChildrenWithRead);
|
view.debugElement.children[0].injector.get(NeedsContentChildrenWithRead);
|
||||||
expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['ca', 'cb']);
|
expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['ca', 'cb']);
|
||||||
});
|
});
|
||||||
@ -289,36 +243,38 @@ export function main() {
|
|||||||
it('should contain the first content child', () => {
|
it('should contain the first content child', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-content-child-read><div #q text="ca"></div></needs-content-child-read>';
|
'<needs-content-child-read><div #q text="ca"></div></needs-content-child-read>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
const comp: NeedsContentChildWithRead =
|
||||||
|
view.debugElement.children[0].injector.get(NeedsContentChildWithRead);
|
||||||
|
expect(comp.textDirChild.text).toEqual('ca');
|
||||||
|
});
|
||||||
|
|
||||||
var comp: NeedsContentChildWithRead =
|
it('should contain the first descendant content child', () => {
|
||||||
|
const template = '<needs-content-child-read>' +
|
||||||
|
'<div dir><div #q text="ca"></div></div>' +
|
||||||
|
'</needs-content-child-read>';
|
||||||
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
|
|
||||||
|
const comp: NeedsContentChildWithRead =
|
||||||
view.debugElement.children[0].injector.get(NeedsContentChildWithRead);
|
view.debugElement.children[0].injector.get(NeedsContentChildWithRead);
|
||||||
expect(comp.textDirChild.text).toEqual('ca');
|
expect(comp.textDirChild.text).toEqual('ca');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain the first view child', () => {
|
it('should contain the first view child', () => {
|
||||||
const template = '<needs-view-child-read></needs-view-child-read>';
|
const template = '<needs-view-child-read></needs-view-child-read>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
const comp: NeedsViewChildWithRead =
|
||||||
|
|
||||||
var comp: NeedsViewChildWithRead =
|
|
||||||
view.debugElement.children[0].injector.get(NeedsViewChildWithRead);
|
view.debugElement.children[0].injector.get(NeedsViewChildWithRead);
|
||||||
expect(comp.textDirChild.text).toEqual('va');
|
expect(comp.textDirChild.text).toEqual('va');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain all child directives in the view', () => {
|
it('should contain all child directives in the view', () => {
|
||||||
const template = '<needs-view-children-read></needs-view-children-read>';
|
const template = '<needs-view-children-read></needs-view-children-read>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
const comp: NeedsViewChildrenWithRead =
|
||||||
|
|
||||||
var comp: NeedsViewChildrenWithRead =
|
|
||||||
view.debugElement.children[0].injector.get(NeedsViewChildrenWithRead);
|
view.debugElement.children[0].injector.get(NeedsViewChildrenWithRead);
|
||||||
expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['va', 'vb']);
|
expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['va', 'vb']);
|
||||||
});
|
});
|
||||||
@ -326,12 +282,9 @@ export function main() {
|
|||||||
it('should support reading a ViewContainer', () => {
|
it('should support reading a ViewContainer', () => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-viewcontainer-read><template>hello</template></needs-viewcontainer-read>';
|
'<needs-viewcontainer-read><template>hello</template></needs-viewcontainer-read>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
const comp: NeedsViewContainerWithRead =
|
||||||
|
|
||||||
var comp: NeedsViewContainerWithRead =
|
|
||||||
view.debugElement.children[0].injector.get(NeedsViewContainerWithRead);
|
view.debugElement.children[0].injector.get(NeedsViewContainerWithRead);
|
||||||
comp.createView();
|
comp.createView();
|
||||||
expect(view.debugElement.children[0].nativeElement).toHaveText('hello');
|
expect(view.debugElement.children[0].nativeElement).toHaveText('hello');
|
||||||
@ -344,11 +297,9 @@ export function main() {
|
|||||||
'<div text="1"></div>' +
|
'<div text="1"></div>' +
|
||||||
'<div *ngIf="shouldShow" text="2"></div>' +
|
'<div *ngIf="shouldShow" text="2"></div>' +
|
||||||
'</needs-query>';
|
'</needs-query>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
const q = view.debugElement.children[0].references['q'];
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
q.query.changes.subscribe({
|
q.query.changes.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
@ -365,23 +316,18 @@ export function main() {
|
|||||||
() => {
|
() => {
|
||||||
const template =
|
const template =
|
||||||
'<needs-query #q *ngIf="shouldShow"><div text="foo"></div></needs-query>';
|
'<needs-query #q *ngIf="shouldShow"><div text="foo"></div></needs-query>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.componentInstance.shouldShow = true;
|
view.componentInstance.shouldShow = true;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
var q: NeedsQuery = view.debugElement.children[0].references['q'];
|
const q: NeedsQuery = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
expect(q.query.length).toEqual(1);
|
expect(q.query.length).toEqual(1);
|
||||||
|
|
||||||
view.componentInstance.shouldShow = false;
|
view.componentInstance.shouldShow = false;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
view.componentInstance.shouldShow = true;
|
view.componentInstance.shouldShow = true;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
const q2: NeedsQuery = view.debugElement.children[0].references['q'];
|
||||||
var q2: NeedsQuery = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
expect(q2.query.length).toEqual(1);
|
expect(q2.query.length).toEqual(1);
|
||||||
});
|
});
|
||||||
@ -393,15 +339,11 @@ export function main() {
|
|||||||
const template = '<needs-query-by-ref-binding #q>' +
|
const template = '<needs-query-by-ref-binding #q>' +
|
||||||
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
|
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
|
||||||
'</needs-query-by-ref-binding>';
|
'</needs-query-by-ref-binding>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.componentInstance.list = ['1d', '2d'];
|
view.componentInstance.list = ['1d', '2d'];
|
||||||
|
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.query.first.text).toEqual('1d');
|
expect(q.query.first.text).toEqual('1d');
|
||||||
expect(q.query.last.text).toEqual('2d');
|
expect(q.query.last.text).toEqual('2d');
|
||||||
});
|
});
|
||||||
@ -411,11 +353,8 @@ export function main() {
|
|||||||
'<div text="one" #textLabel1="textDir"></div>' +
|
'<div text="one" #textLabel1="textDir"></div>' +
|
||||||
'<div text="two" #textLabel2="textDir"></div>' +
|
'<div text="two" #textLabel2="textDir"></div>' +
|
||||||
'</needs-query-by-ref-bindings>';
|
'</needs-query-by-ref-bindings>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.first.text).toEqual('one');
|
expect(q.query.first.text).toEqual('one');
|
||||||
expect(q.query.last.text).toEqual('two');
|
expect(q.query.last.text).toEqual('two');
|
||||||
@ -425,19 +364,13 @@ export function main() {
|
|||||||
const template = '<needs-query-by-ref-binding #q>' +
|
const template = '<needs-query-by-ref-binding #q>' +
|
||||||
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
|
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
|
||||||
'</needs-query-by-ref-binding>';
|
'</needs-query-by-ref-binding>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.componentInstance.list = ['1d', '2d'];
|
view.componentInstance.list = ['1d', '2d'];
|
||||||
|
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
view.componentInstance.list = ['2d', '1d'];
|
view.componentInstance.list = ['2d', '1d'];
|
||||||
|
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.query.last.text).toEqual('1d');
|
expect(q.query.last.text).toEqual('1d');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -447,15 +380,11 @@ export function main() {
|
|||||||
'<div #textLabel>{{item}}</div>' +
|
'<div #textLabel>{{item}}</div>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</needs-query-by-ref-binding>';
|
'</needs-query-by-ref-binding>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.componentInstance.list = ['1d', '2d'];
|
view.componentInstance.list = ['1d', '2d'];
|
||||||
|
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.query.first.nativeElement).toHaveText('1d');
|
expect(q.query.first.nativeElement).toHaveText('1d');
|
||||||
expect(q.query.last.nativeElement).toHaveText('2d');
|
expect(q.query.last.nativeElement).toHaveText('2d');
|
||||||
});
|
});
|
||||||
@ -464,36 +393,23 @@ export function main() {
|
|||||||
const template = '<needs-query-and-project #q>' +
|
const template = '<needs-query-and-project #q>' +
|
||||||
'<div text="hello"></div><div text="world"></div>' +
|
'<div text="hello"></div><div text="world"></div>' +
|
||||||
'</needs-query-and-project>';
|
'</needs-query-and-project>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(asNativeElements(view.debugElement.children)).toHaveText('hello|world|');
|
expect(asNativeElements(view.debugElement.children)).toHaveText('hello|world|');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support querying the view by using a view query', () => {
|
it('should support querying the view by using a view query', () => {
|
||||||
const template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>';
|
const template = '<needs-view-query-by-ref-binding #q></needs-view-query-by-ref-binding>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
|
||||||
|
|
||||||
var q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q'];
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
|
const q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q'];
|
||||||
expect(q.query.first.nativeElement).toHaveText('text');
|
expect(q.query.first.nativeElement).toHaveText('text');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should contain all child directives in the view dom', () => {
|
it('should contain all child directives in the view dom', () => {
|
||||||
const template = '<needs-view-children #q></needs-view-children>';
|
const template = '<needs-view-children #q></needs-view-children>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.textDirChildren.length).toEqual(1);
|
expect(q.textDirChildren.length).toEqual(1);
|
||||||
expect(q.numberOfChildrenAfterViewInit).toEqual(1);
|
expect(q.numberOfChildrenAfterViewInit).toEqual(1);
|
||||||
});
|
});
|
||||||
@ -502,61 +418,40 @@ export function main() {
|
|||||||
describe('querying in the view', () => {
|
describe('querying in the view', () => {
|
||||||
it('should contain all the elements in the view with that have the given directive', () => {
|
it('should contain all the elements in the view with that have the given directive', () => {
|
||||||
const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
|
const template = '<needs-view-query #q><div text="ignoreme"></div></needs-view-query>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQuery = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQuery = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not include directive present on the host element', () => {
|
it('should not include directive present on the host element', () => {
|
||||||
const template = '<needs-view-query #q text="self"></needs-view-query>';
|
const template = '<needs-view-query #q text="self"></needs-view-query>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQuery = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQuery = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reflect changes in the component', () => {
|
it('should reflect changes in the component', () => {
|
||||||
const template = '<needs-view-query-if #q></needs-view-query-if>';
|
const template = '<needs-view-query-if #q></needs-view-query-if>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQueryIf = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQueryIf = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.length).toBe(0);
|
expect(q.query.length).toBe(0);
|
||||||
|
|
||||||
q.show = true;
|
q.show = true;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
expect(q.query.length).toBe(1);
|
expect(q.query.length).toBe(1);
|
||||||
|
|
||||||
expect(q.query.first.text).toEqual('1');
|
expect(q.query.first.text).toEqual('1');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not be affected by other changes in the component', () => {
|
it('should not be affected by other changes in the component', () => {
|
||||||
const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>';
|
const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.length).toEqual(1);
|
expect(q.query.length).toEqual(1);
|
||||||
expect(q.query.first.text).toEqual('1');
|
expect(q.query.first.text).toEqual('1');
|
||||||
|
|
||||||
q.show = false;
|
q.show = false;
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.query.length).toEqual(1);
|
expect(q.query.length).toEqual(1);
|
||||||
expect(q.query.first.text).toEqual('1');
|
expect(q.query.first.text).toEqual('1');
|
||||||
});
|
});
|
||||||
@ -564,66 +459,46 @@ export function main() {
|
|||||||
it('should maintain directives in pre-order depth-first DOM order after dynamic insertion',
|
it('should maintain directives in pre-order depth-first DOM order after dynamic insertion',
|
||||||
() => {
|
() => {
|
||||||
const template = '<needs-view-query-order #q></needs-view-query-order>';
|
const template = '<needs-view-query-order #q></needs-view-query-order>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||||
|
|
||||||
q.list = ['-3', '2'];
|
q.list = ['-3', '2'];
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should maintain directives in pre-order depth-first DOM order after dynamic insertion',
|
it('should maintain directives in pre-order depth-first DOM order after dynamic insertion',
|
||||||
() => {
|
() => {
|
||||||
const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>';
|
const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
|
||||||
|
|
||||||
q.list = ['-3', '2'];
|
q.list = ['-3', '2'];
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should handle long ngFor cycles', () => {
|
it('should handle long ngFor cycles', () => {
|
||||||
const template = '<needs-view-query-order #q></needs-view-query-order>';
|
const template = '<needs-view-query-order #q></needs-view-query-order>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
|
|
||||||
|
|
||||||
// no significance to 50, just a reasonably large cycle.
|
// no significance to 50, just a reasonably large cycle.
|
||||||
for (var i = 0; i < 50; i++) {
|
for (let i = 0; i < 50; i++) {
|
||||||
var newString = i.toString();
|
const newString = i.toString();
|
||||||
q.list = [newString];
|
q.list = [newString];
|
||||||
view.detectChanges();
|
view.detectChanges();
|
||||||
|
|
||||||
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', newString, '4']);
|
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', newString, '4']);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support more than three queries', () => {
|
it('should support more than three queries', () => {
|
||||||
const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>';
|
const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>';
|
||||||
TestBed.overrideComponent(MyComp0, {set: {template}});
|
const view = createTestCmpAndDetectChanges(MyComp0, template);
|
||||||
const view = TestBed.createComponent(MyComp0);
|
const q = view.debugElement.children[0].references['q'];
|
||||||
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
var q = view.debugElement.children[0].references['q'];
|
|
||||||
expect(q.query1).toBeDefined();
|
expect(q.query1).toBeDefined();
|
||||||
expect(q.query2).toBeDefined();
|
expect(q.query2).toBeDefined();
|
||||||
expect(q.query3).toBeDefined();
|
expect(q.query3).toBeDefined();
|
||||||
@ -663,27 +538,19 @@ class NeedsContentChild implements AfterContentInit, AfterContentChecked {
|
|||||||
@ContentChild(TextDirective)
|
@ContentChild(TextDirective)
|
||||||
set child(value) {
|
set child(value) {
|
||||||
this._child = value;
|
this._child = value;
|
||||||
this.logs.push(['setter', isPresent(value) ? value.text : null]);
|
this.logs.push(['setter', value ? value.text : null]);
|
||||||
}
|
}
|
||||||
|
|
||||||
get child() { return this._child; }
|
get child() { return this._child; }
|
||||||
logs: any[] /** TODO #9100 */ = [];
|
logs: any[] /** TODO #9100 */ = [];
|
||||||
|
|
||||||
ngAfterContentInit() { this.logs.push(['init', isPresent(this.child) ? this.child.text : null]); }
|
ngAfterContentInit() { this.logs.push(['init', this.child ? this.child.text : null]); }
|
||||||
|
|
||||||
ngAfterContentChecked() {
|
ngAfterContentChecked() { this.logs.push(['check', this.child ? this.child.text : null]); }
|
||||||
this.logs.push(['check', isPresent(this.child) ? this.child.text : null]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({selector: 'needs-view-child', template: `<div *ngIf="shouldShow" text="foo"></div>`})
|
||||||
selector: 'needs-view-child',
|
class NeedsViewChild implements AfterViewInit, AfterViewChecked {
|
||||||
template: `
|
|
||||||
<div *ngIf="shouldShow" text="foo"></div>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
class NeedsViewChild implements AfterViewInit,
|
|
||||||
AfterViewChecked {
|
|
||||||
shouldShow: boolean = true;
|
shouldShow: boolean = true;
|
||||||
shouldShow2: boolean = false;
|
shouldShow2: boolean = false;
|
||||||
/** @internal */
|
/** @internal */
|
||||||
@ -692,34 +559,37 @@ class NeedsViewChild implements AfterViewInit,
|
|||||||
@ViewChild(TextDirective)
|
@ViewChild(TextDirective)
|
||||||
set child(value) {
|
set child(value) {
|
||||||
this._child = value;
|
this._child = value;
|
||||||
this.logs.push(['setter', isPresent(value) ? value.text : null]);
|
this.logs.push(['setter', value ? value.text : null]);
|
||||||
}
|
}
|
||||||
|
|
||||||
get child() { return this._child; }
|
get child() { return this._child; }
|
||||||
logs: any[] /** TODO #9100 */ = [];
|
logs: any[] /** TODO #9100 */ = [];
|
||||||
|
|
||||||
ngAfterViewInit() { this.logs.push(['init', isPresent(this.child) ? this.child.text : null]); }
|
ngAfterViewInit() { this.logs.push(['init', this.child ? this.child.text : null]); }
|
||||||
|
|
||||||
ngAfterViewChecked() {
|
ngAfterViewChecked() { this.logs.push(['check', this.child ? this.child.text : null]); }
|
||||||
this.logs.push(['check', isPresent(this.child) ? this.child.text : null]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
function createTestCmp<T>(type: Type<T>, template: string): ComponentFixture<T> {
|
||||||
selector: 'needs-static-content-view-child',
|
const view = TestBed.overrideComponent(type, {set: {template}}).createComponent(type);
|
||||||
template: `
|
return view;
|
||||||
<div text="viewFoo"></div>
|
}
|
||||||
`
|
|
||||||
})
|
|
||||||
|
function createTestCmpAndDetectChanges<T>(type: Type<T>, template: string): ComponentFixture<T> {
|
||||||
|
const view = createTestCmp(type, template);
|
||||||
|
view.detectChanges();
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'needs-static-content-view-child', template: `<div text="viewFoo"></div>`})
|
||||||
class NeedsStaticContentAndViewChild {
|
class NeedsStaticContentAndViewChild {
|
||||||
@ContentChild(TextDirective) contentChild: TextDirective;
|
@ContentChild(TextDirective) contentChild: TextDirective;
|
||||||
|
|
||||||
@ViewChild(TextDirective) viewChild: TextDirective;
|
@ViewChild(TextDirective) viewChild: TextDirective;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive({selector: '[dir]'})
|
@Directive({selector: '[dir]'})
|
||||||
class InertDirective {
|
class InertDirective {
|
||||||
constructor() {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -780,9 +650,8 @@ class NeedsViewQuery {
|
|||||||
|
|
||||||
@Component({selector: 'needs-view-query-if', template: '<div *ngIf="show" text="1"></div>'})
|
@Component({selector: 'needs-view-query-if', template: '<div *ngIf="show" text="1"></div>'})
|
||||||
class NeedsViewQueryIf {
|
class NeedsViewQueryIf {
|
||||||
show: boolean;
|
show: boolean = false;
|
||||||
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
||||||
constructor() { this.show = false; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -790,9 +659,8 @@ class NeedsViewQueryIf {
|
|||||||
template: '<div text="1"><div *ngIf="show"><div dir></div></div></div>'
|
template: '<div text="1"><div *ngIf="show"><div dir></div></div></div>'
|
||||||
})
|
})
|
||||||
class NeedsViewQueryNestedIf {
|
class NeedsViewQueryNestedIf {
|
||||||
show: boolean;
|
show: boolean = true;
|
||||||
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
||||||
constructor() { this.show = true; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -803,8 +671,7 @@ class NeedsViewQueryNestedIf {
|
|||||||
})
|
})
|
||||||
class NeedsViewQueryOrder {
|
class NeedsViewQueryOrder {
|
||||||
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
||||||
list: string[];
|
list: string[] = ['2', '3'];
|
||||||
constructor() { this.list = ['2', '3']; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -815,8 +682,7 @@ class NeedsViewQueryOrder {
|
|||||||
})
|
})
|
||||||
class NeedsViewQueryOrderWithParent {
|
class NeedsViewQueryOrderWithParent {
|
||||||
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
|
||||||
list: string[];
|
list: string[] = ['2', '3'];
|
||||||
constructor() { this.list = ['2', '3']; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'needs-tpl', template: '<template><div>shadow</div></template>'})
|
@Component({selector: 'needs-tpl', template: '<template><div>shadow</div></template>'})
|
||||||
@ -879,12 +745,8 @@ class HasNullQueryCondition {
|
|||||||
|
|
||||||
@Component({selector: 'my-comp', template: ''})
|
@Component({selector: 'my-comp', template: ''})
|
||||||
class MyComp0 {
|
class MyComp0 {
|
||||||
shouldShow: boolean;
|
shouldShow: boolean = false;
|
||||||
list: any /** TODO #9100 */;
|
list: string[] = ['1d', '2d', '3d'];
|
||||||
constructor() {
|
|
||||||
this.shouldShow = false;
|
|
||||||
this.list = ['1d', '2d', '3d'];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'my-comp', template: ''})
|
@Component({selector: 'my-comp', template: ''})
|
||||||
|
@ -79,6 +79,24 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
expect(fixture.nativeElement).toHaveText('counting method value');
|
expect(fixture.nativeElement).toHaveText('counting method value');
|
||||||
expect(MyCountingComp.calls).toBe(1);
|
expect(MyCountingComp.calls).toBe(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should evalute a conditional in a statement binding', () => {
|
||||||
|
@Component({selector: 'some-comp', template: '<p (click)="nullValue?.click()"></p>'})
|
||||||
|
class SomeComponent {
|
||||||
|
nullValue: SomeReferencedClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SomeReferencedClass {
|
||||||
|
click() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
const fixture = TestBed.configureTestingModule({declarations: [SomeComponent]})
|
||||||
|
.createComponent(SomeComponent);
|
||||||
|
|
||||||
|
fixture.detectChanges(/* checkNoChanges */ false);
|
||||||
|
}).not.toThrow();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('providers', () => {
|
describe('providers', () => {
|
||||||
|
@ -6,9 +6,8 @@
|
|||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
|
import {Component, Directive, Input, NO_ERRORS_SCHEMA} from '@angular/core';
|
||||||
import {Component} from '@angular/core/src/metadata';
|
import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing';
|
||||||
import {TestBed, getTestBed} from '@angular/core/testing';
|
|
||||||
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
|
import {afterEach, beforeEach, beforeEachProviders, ddescribe, describe, expect, iit, inject, it} from '@angular/core/testing/testing_internal';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
import {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
|
||||||
@ -24,12 +23,22 @@ class SecuredComponent {
|
|||||||
ctxProp: any = 'some value';
|
ctxProp: any = 'some value';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Directive({selector: '[onPrefixedProp]'})
|
||||||
|
class OnPrefixDir {
|
||||||
|
@Input() onPrefixedProp: any;
|
||||||
|
@Input() onclick: any;
|
||||||
|
}
|
||||||
|
|
||||||
function declareTests({useJit}: {useJit: boolean}) {
|
function declareTests({useJit}: {useJit: boolean}) {
|
||||||
describe('security integration tests', function() {
|
describe('security integration tests', function() {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureCompiler({useJit: useJit});
|
TestBed.configureCompiler({useJit: useJit}).configureTestingModule({
|
||||||
TestBed.configureTestingModule({declarations: [SecuredComponent]});
|
declarations: [
|
||||||
|
SecuredComponent,
|
||||||
|
OnPrefixDir,
|
||||||
|
]
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
let originalLog: (msg: any) => any;
|
let originalLog: (msg: any) => any;
|
||||||
@ -43,15 +52,10 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
it('should disallow binding to attr.on*', () => {
|
it('should disallow binding to attr.on*', () => {
|
||||||
const template = `<div [attr.onclick]="ctxProp"></div>`;
|
const template = `<div [attr.onclick]="ctxProp"></div>`;
|
||||||
TestBed.overrideComponent(SecuredComponent, {set: {template}});
|
TestBed.overrideComponent(SecuredComponent, {set: {template}});
|
||||||
try {
|
|
||||||
TestBed.createComponent(SecuredComponent);
|
expect(() => TestBed.createComponent(SecuredComponent))
|
||||||
throw 'Should throw';
|
.toThrowError(
|
||||||
} catch (e) {
|
/Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../);
|
||||||
expect(e.message).toContain(
|
|
||||||
`Template parse errors:\n` +
|
|
||||||
`Binding to event attribute 'onclick' is disallowed ` +
|
|
||||||
`for security reasons, please use (click)=... `);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should disallow binding to on* with NO_ERRORS_SCHEMA', () => {
|
it('should disallow binding to on* with NO_ERRORS_SCHEMA', () => {
|
||||||
@ -59,17 +63,31 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
|
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
|
||||||
schemas: [NO_ERRORS_SCHEMA]
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
});
|
});
|
||||||
;
|
|
||||||
try {
|
expect(() => TestBed.createComponent(SecuredComponent))
|
||||||
TestBed.createComponent(SecuredComponent);
|
.toThrowError(
|
||||||
throw 'Should throw';
|
/Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../);
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toContain(
|
|
||||||
`Template parse errors:\n` +
|
|
||||||
`Binding to event attribute 'onclick' is disallowed ` +
|
|
||||||
`for security reasons, please use (click)=... `);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should disallow binding to on* unless it is consumed by a directive', () => {
|
||||||
|
const template = `<div [onPrefixedProp]="ctxProp" [onclick]="ctxProp"></div>`;
|
||||||
|
TestBed.overrideComponent(SecuredComponent, {set: {template}}).configureTestingModule({
|
||||||
|
schemas: [NO_ERRORS_SCHEMA]
|
||||||
|
});
|
||||||
|
|
||||||
|
// should not throw for inputs starting with "on"
|
||||||
|
let cmp: ComponentFixture<SecuredComponent>;
|
||||||
|
expect(() => cmp = TestBed.createComponent(SecuredComponent)).not.toThrow();
|
||||||
|
|
||||||
|
// must bind to the directive not to the property of the div
|
||||||
|
const value = cmp.componentInstance.ctxProp = {};
|
||||||
|
cmp.detectChanges();
|
||||||
|
const div = cmp.debugElement.children[0];
|
||||||
|
expect(div.injector.get(OnPrefixDir).onclick).toBe(value);
|
||||||
|
expect(getDOM().getProperty(div.nativeElement, 'onclick')).not.toBe(value);
|
||||||
|
expect(getDOM().hasAttribute(div.nativeElement, 'onclick')).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('safe HTML values', function() {
|
describe('safe HTML values', function() {
|
||||||
@ -157,12 +175,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
|||||||
const template = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
|
const template = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
|
||||||
TestBed.overrideComponent(SecuredComponent, {set: {template}});
|
TestBed.overrideComponent(SecuredComponent, {set: {template}});
|
||||||
|
|
||||||
try {
|
expect(() => TestBed.createComponent(SecuredComponent))
|
||||||
TestBed.createComponent(SecuredComponent);
|
.toThrowError(/Can't bind to 'xlink:href'/);
|
||||||
throw 'Should throw';
|
|
||||||
} catch (e) {
|
|
||||||
expect(e.message).toContain(`Can't bind to 'xlink:href'`);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should escape unsafe HTML values', () => {
|
it('should escape unsafe HTML values', () => {
|
||||||
|
28
modules/@angular/core/test/util/lang_spec.ts
Normal file
28
modules/@angular/core/test/util/lang_spec.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import {isPromise} from '@angular/core/src/util/lang';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe('isPromise', () => {
|
||||||
|
it('should be true for native Promises',
|
||||||
|
() => expect(isPromise(Promise.resolve(true))).toEqual(true));
|
||||||
|
|
||||||
|
it('should be true for thenables', () => expect(isPromise({then: () => {}})).toEqual(true));
|
||||||
|
|
||||||
|
it('should be false if "then" is not a function',
|
||||||
|
() => expect(isPromise({then: 0})).toEqual(false));
|
||||||
|
|
||||||
|
it('should be false if the argument has no "then" function',
|
||||||
|
() => expect(isPromise({})).toEqual(false));
|
||||||
|
|
||||||
|
it('should be false if the argument is undefined or null', () => {
|
||||||
|
expect(isPromise(undefined)).toEqual(false);
|
||||||
|
expect(isPromise(null)).toEqual(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
11
modules/@angular/core/testing/private_import_core.ts
Normal file
11
modules/@angular/core/testing/private_import_core.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {__core_private__ as r} from '@angular/core';
|
||||||
|
|
||||||
|
export const isPromise: typeof r.isPromise = r.isPromise;
|
@ -9,8 +9,7 @@
|
|||||||
import {CompilerOptions, Component, Directive, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, OpaqueToken, Pipe, PlatformRef, Provider, SchemaMetadata, Type} from '@angular/core';
|
import {CompilerOptions, Component, Directive, Injector, ModuleWithComponentFactories, NgModule, NgModuleFactory, NgModuleRef, NgZone, OpaqueToken, Pipe, PlatformRef, Provider, SchemaMetadata, Type} from '@angular/core';
|
||||||
import {AsyncTestCompleter} from './async_test_completer';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
import {ComponentFixture} from './component_fixture';
|
import {ComponentFixture} from './component_fixture';
|
||||||
import {ListWrapper} from './facade/collection';
|
import {stringify} from './facade/lang';
|
||||||
import {FunctionWrapper, stringify} from './facade/lang';
|
|
||||||
import {MetadataOverride} from './metadata_override';
|
import {MetadataOverride} from './metadata_override';
|
||||||
import {TestingCompiler, TestingCompilerFactory} from './test_compiler';
|
import {TestingCompiler, TestingCompilerFactory} from './test_compiler';
|
||||||
|
|
||||||
@ -25,27 +24,36 @@ export class TestComponentRenderer {
|
|||||||
insertRootElement(rootElementId: string) {}
|
insertRootElement(rootElementId: string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _nextRootElementId = 0;
|
let _nextRootElementId = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export var ComponentFixtureAutoDetect = new OpaqueToken('ComponentFixtureAutoDetect');
|
export const ComponentFixtureAutoDetect = new OpaqueToken('ComponentFixtureAutoDetect');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export var ComponentFixtureNoNgZone = new OpaqueToken('ComponentFixtureNoNgZone');
|
export const ComponentFixtureNoNgZone = new OpaqueToken('ComponentFixtureNoNgZone');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export type TestModuleMetadata = {
|
export type TestModuleMetadata = {
|
||||||
providers?: any[]; declarations?: any[]; imports?: any[]; schemas?: Array<SchemaMetadata|any[]>;
|
providers?: any[],
|
||||||
|
declarations?: any[],
|
||||||
|
imports?: any[],
|
||||||
|
schemas?: Array<SchemaMetadata|any[]>,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @whatItDoes Configures and initializes environment for unit testing and provides methods for
|
||||||
|
* creating components and services in unit tests.
|
||||||
|
* @description
|
||||||
|
*
|
||||||
|
* TestBed is the primary api for writing unit tests for Angular applications and libraries.
|
||||||
|
*
|
||||||
|
* @stable
|
||||||
*/
|
*/
|
||||||
export class TestBed implements Injector {
|
export class TestBed implements Injector {
|
||||||
/**
|
/**
|
||||||
@ -63,7 +71,7 @@ export class TestBed implements Injector {
|
|||||||
*/
|
*/
|
||||||
static initTestEnvironment(ngModule: Type<any>, platform: PlatformRef): TestBed {
|
static initTestEnvironment(ngModule: Type<any>, platform: PlatformRef): TestBed {
|
||||||
const testBed = getTestBed();
|
const testBed = getTestBed();
|
||||||
getTestBed().initTestEnvironment(ngModule, platform);
|
testBed.initTestEnvironment(ngModule, platform);
|
||||||
return testBed;
|
return testBed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,16 +224,16 @@ export class TestBed implements Injector {
|
|||||||
configureTestingModule(moduleDef: TestModuleMetadata) {
|
configureTestingModule(moduleDef: TestModuleMetadata) {
|
||||||
this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
|
this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
|
||||||
if (moduleDef.providers) {
|
if (moduleDef.providers) {
|
||||||
this._providers = ListWrapper.concat(this._providers, moduleDef.providers);
|
this._providers.push(...moduleDef.providers);
|
||||||
}
|
}
|
||||||
if (moduleDef.declarations) {
|
if (moduleDef.declarations) {
|
||||||
this._declarations = ListWrapper.concat(this._declarations, moduleDef.declarations);
|
this._declarations.push(...moduleDef.declarations);
|
||||||
}
|
}
|
||||||
if (moduleDef.imports) {
|
if (moduleDef.imports) {
|
||||||
this._imports = ListWrapper.concat(this._imports, moduleDef.imports);
|
this._imports.push(...moduleDef.imports);
|
||||||
}
|
}
|
||||||
if (moduleDef.schemas) {
|
if (moduleDef.schemas) {
|
||||||
this._schemas = ListWrapper.concat(this._schemas, moduleDef.schemas);
|
this._schemas.push(...moduleDef.schemas);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -304,14 +312,14 @@ export class TestBed implements Injector {
|
|||||||
}
|
}
|
||||||
// Tests can inject things from the ng module and from the compiler,
|
// Tests can inject things from the ng module and from the compiler,
|
||||||
// but the ng module can't inject things from the compiler and vice versa.
|
// but the ng module can't inject things from the compiler and vice versa.
|
||||||
let result = this._moduleRef.injector.get(token, UNDEFINED);
|
const result = this._moduleRef.injector.get(token, UNDEFINED);
|
||||||
return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue) : result;
|
return result === UNDEFINED ? this._compiler.injector.get(token, notFoundValue) : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
execute(tokens: any[], fn: Function): any {
|
execute(tokens: any[], fn: Function): any {
|
||||||
this._initIfNeeded();
|
this._initIfNeeded();
|
||||||
var params = tokens.map(t => this.get(t));
|
const params = tokens.map(t => this.get(t));
|
||||||
return FunctionWrapper.apply(fn, params);
|
return fn(...params);
|
||||||
}
|
}
|
||||||
|
|
||||||
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
|
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
|
||||||
@ -350,26 +358,23 @@ export class TestBed implements Injector {
|
|||||||
testComponentRenderer.insertRootElement(rootElId);
|
testComponentRenderer.insertRootElement(rootElId);
|
||||||
|
|
||||||
const initComponent = () => {
|
const initComponent = () => {
|
||||||
var componentRef = componentFactory.create(this, [], `#${rootElId}`);
|
const componentRef = componentFactory.create(this, [], `#${rootElId}`);
|
||||||
return new ComponentFixture<T>(componentRef, ngZone, autoDetect);
|
return new ComponentFixture<T>(componentRef, ngZone, autoDetect);
|
||||||
};
|
};
|
||||||
|
|
||||||
const fixture = ngZone == null ? initComponent() : ngZone.run(initComponent);
|
const fixture = !ngZone ? initComponent() : ngZone.run(initComponent);
|
||||||
this._activeFixtures.push(fixture);
|
this._activeFixtures.push(fixture);
|
||||||
return fixture;
|
return fixture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _testBed: TestBed = null;
|
let _testBed: TestBed = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
export function getTestBed() {
|
export function getTestBed() {
|
||||||
if (_testBed == null) {
|
return _testBed = _testBed || new TestBed();
|
||||||
_testBed = new TestBed();
|
|
||||||
}
|
|
||||||
return _testBed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -397,14 +402,14 @@ export function getTestBed() {
|
|||||||
* @stable
|
* @stable
|
||||||
*/
|
*/
|
||||||
export function inject(tokens: any[], fn: Function): () => any {
|
export function inject(tokens: any[], fn: Function): () => any {
|
||||||
let testBed = getTestBed();
|
const testBed = getTestBed();
|
||||||
if (tokens.indexOf(AsyncTestCompleter) >= 0) {
|
if (tokens.indexOf(AsyncTestCompleter) >= 0) {
|
||||||
return () =>
|
return () =>
|
||||||
// Return an async test method that returns a Promise if AsyncTestCompleter is one of
|
// Return an async test method that returns a Promise if AsyncTestCompleter is one of
|
||||||
// the
|
// the
|
||||||
// injected tokens.
|
// injected tokens.
|
||||||
testBed.compileComponents().then(() => {
|
testBed.compileComponents().then(() => {
|
||||||
let completer: AsyncTestCompleter = testBed.get(AsyncTestCompleter);
|
const completer: AsyncTestCompleter = testBed.get(AsyncTestCompleter);
|
||||||
testBed.execute(tokens, fn);
|
testBed.execute(tokens, fn);
|
||||||
return completer.promise;
|
return completer.promise;
|
||||||
});
|
});
|
||||||
|
@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
import {AsyncTestCompleter} from './async_test_completer';
|
import {AsyncTestCompleter} from './async_test_completer';
|
||||||
import {StringMapWrapper} from './facade/collection';
|
import {StringMapWrapper} from './facade/collection';
|
||||||
import {Math, global, isPromise} from './facade/lang';
|
import {Math, global} from './facade/lang';
|
||||||
|
import {isPromise} from './private_import_core';
|
||||||
import {TestBed, getTestBed, inject} from './test_bed';
|
import {TestBed, getTestBed, inject} from './test_bed';
|
||||||
|
|
||||||
export {AsyncTestCompleter} from './async_test_completer';
|
export {AsyncTestCompleter} from './async_test_completer';
|
||||||
|
@ -11,7 +11,7 @@ declare var expect: any;
|
|||||||
|
|
||||||
// TODO (juliemr): remove this method once this becomes a protractor plugin
|
// TODO (juliemr): remove this method once this becomes a protractor plugin
|
||||||
export function verifyNoBrowserErrors() {
|
export function verifyNoBrowserErrors() {
|
||||||
browser.manage().logs().get('browser').then(function(browserLog) {
|
browser.manage().logs().get('browser').then(function(browserLog: any[]) {
|
||||||
var errors: any[] = [];
|
var errors: any[] = [];
|
||||||
browserLog.filter(logEntry => {
|
browserLog.filter(logEntry => {
|
||||||
var msg = logEntry.message;
|
var msg = logEntry.message;
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#!/bin/sh
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -e -o pipefail
|
||||||
|
|
||||||
#
|
#
|
||||||
# This script is used to compile and copy the contents for each of
|
# This script is used to compile and copy the contents for each of
|
||||||
@ -11,7 +13,7 @@ cd `dirname $0`
|
|||||||
|
|
||||||
DIST="../../../dist/examples";
|
DIST="../../../dist/examples";
|
||||||
rm -rf -- $DIST
|
rm -rf -- $DIST
|
||||||
$(npm bin)/tsc -p .
|
$(npm bin)/tsc -p ./tsconfig-build.json
|
||||||
|
|
||||||
mkdir $DIST/vendor/
|
mkdir $DIST/vendor/
|
||||||
|
|
||||||
|
@ -14,13 +14,14 @@ import {Subscriber} from 'rxjs/Subscriber';
|
|||||||
|
|
||||||
// #docregion AsyncPipePromise
|
// #docregion AsyncPipePromise
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'async-example',
|
selector: 'async-promise-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<p>Wait for it... {{ greeting | async }}</p>
|
<code>promise|async</code>:
|
||||||
<button (click)="clicked()">{{ arrived ? 'Reset' : 'Resolve' }}</button>
|
<button (click)="clicked()">{{ arrived ? 'Reset' : 'Resolve' }}</button>
|
||||||
|
<span>Wait for it... {{ greeting | async }}</span>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class AsyncPipeExample {
|
export class AsyncPromisePipeComponent {
|
||||||
greeting: Promise<string> = null;
|
greeting: Promise<string> = null;
|
||||||
arrived: boolean = false;
|
arrived: boolean = false;
|
||||||
|
|
||||||
@ -45,29 +46,26 @@ export class AsyncPipeExample {
|
|||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
// #docregion AsyncPipeObservable
|
// #docregion AsyncPipeObservable
|
||||||
@Component({selector: 'task-cmp', template: 'Time: {{ time | async }}'})
|
@Component({
|
||||||
class Task {
|
selector: 'async-observable-pipe',
|
||||||
time = new Observable<number>((observer: Subscriber<number>) => {
|
template: '<div><code>observable|async</code>: Time: {{ time | async }}</div>'
|
||||||
setInterval(() => observer.next(new Date().getTime()), 500);
|
})
|
||||||
|
export class AsyncObservablePipeComponent {
|
||||||
|
time = new Observable<string>((observer: Subscriber<string>) => {
|
||||||
|
setInterval(() => observer.next(new Date().toString()), 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
@Component({
|
// For some reason protractor hangs on setInterval. So we will run outside of angular zone so that
|
||||||
selector: 'example-app',
|
// protractor will not see us. Also we want to have this outside the docregion so as not to confuse
|
||||||
template: `
|
// the reader.
|
||||||
<h1>AsyncPipe Example</h1>
|
function setInterval(fn: Function, delay: number) {
|
||||||
<async-example></async-example>
|
var zone = Zone.current;
|
||||||
`
|
var rootZone = zone;
|
||||||
})
|
while (rootZone.parent) {
|
||||||
export class AppCmp {
|
rootZone = rootZone.parent;
|
||||||
}
|
}
|
||||||
|
rootZone.run(
|
||||||
@NgModule(
|
() => { window.setInterval(function() { zone.run(fn, this, arguments as any); }, delay); });
|
||||||
{declarations: [AsyncPipeExample, AppCmp, Task], imports: [BrowserModule], bootstrap: [AppCmp]})
|
|
||||||
class AppModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
||||||
}
|
}
|
@ -12,32 +12,14 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
|||||||
|
|
||||||
// #docregion DatePipe
|
// #docregion DatePipe
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'date-example',
|
selector: 'date-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<p>Today is {{today | date}}</p>
|
<p>Today is {{today | date}}</p>
|
||||||
<p>Or if you prefer, {{today | date:'fullDate'}}</p>
|
<p>Or if you prefer, {{today | date:'fullDate'}}</p>
|
||||||
<p>The time is {{today | date:'jmZ'}}</p>
|
<p>The time is {{today | date:'jmZ'}}</p>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class DatePipeExample {
|
export class DatePipeComponent {
|
||||||
today: number = Date.now();
|
today: number = Date.now();
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'example-app',
|
|
||||||
template: `
|
|
||||||
<h1>DatePipe Example</h1>
|
|
||||||
<date-example></date-example>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
export class AppCmp {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({declarations: [DatePipeExample, AppCmp], imports: [BrowserModule], bootstrap: [AppCmp]})
|
|
||||||
class AppModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
||||||
}
|
|
@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
|
||||||
|
|
||||||
|
function waitForElement(selector: string) {
|
||||||
|
var EC = (<any>protractor).ExpectedConditions;
|
||||||
|
// Waits for the element with id 'abc' to be present on the dom.
|
||||||
|
browser.wait(EC.presenceOf($(selector)), 20000);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('pipe', () => {
|
||||||
|
afterEach(verifyNoBrowserErrors);
|
||||||
|
|
||||||
|
describe('async', () => {
|
||||||
|
var URL = '/common/pipes/ts/';
|
||||||
|
|
||||||
|
it('should resolve and display promise', () => {
|
||||||
|
browser.get(URL);
|
||||||
|
waitForElement('async-promise-pipe');
|
||||||
|
expect(element.all(by.css('async-promise-pipe span')).get(0).getText())
|
||||||
|
.toEqual('Wait for it...');
|
||||||
|
element(by.css('async-promise-pipe button')).click();
|
||||||
|
expect(element.all(by.css('async-promise-pipe span')).get(0).getText())
|
||||||
|
.toEqual('Wait for it... hi there!');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should update lowercase/uppercase', () => {
|
||||||
|
browser.get(URL);
|
||||||
|
waitForElement('lowerupper-pipe');
|
||||||
|
element(by.css('lowerupper-pipe input')).sendKeys('Hello World!');
|
||||||
|
expect(element.all(by.css('lowerupper-pipe pre')).get(0).getText())
|
||||||
|
.toEqual('\'hello world!\'');
|
||||||
|
expect(element.all(by.css('lowerupper-pipe pre')).get(1).getText())
|
||||||
|
.toEqual('\'HELLO WORLD!\'');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
34
modules/@angular/examples/common/pipes/ts/i18n_pipe.ts
Normal file
34
modules/@angular/examples/common/pipes/ts/i18n_pipe.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||||
|
import {Observable} from 'rxjs/Observable';
|
||||||
|
import {Subscriber} from 'rxjs/Subscriber';
|
||||||
|
|
||||||
|
// #docregion I18nPluralPipeComponent
|
||||||
|
@Component({
|
||||||
|
selector: 'i18n-plural-pipe',
|
||||||
|
template: `<div>{{ messages.length | i18nPlural: messageMapping }}</div>`
|
||||||
|
})
|
||||||
|
export class I18nPluralPipeComponent {
|
||||||
|
messages: any[] = ['Message 1'];
|
||||||
|
messageMapping:
|
||||||
|
{[k: string]: string} = {'=0': 'No messages.', '=1': 'One message.', 'other': '# messages.'};
|
||||||
|
}
|
||||||
|
// #enddocregion
|
||||||
|
|
||||||
|
// #docregion I18nSelectPipeComponent
|
||||||
|
@Component(
|
||||||
|
{selector: 'i18n-select-pipe', template: `<div>{{gender | i18nSelect: inviteMap}} </div>`})
|
||||||
|
export class I18nSelectPipeComponent {
|
||||||
|
gender: string = 'male';
|
||||||
|
inviteMap: any = {'male': 'Invite him.', 'female': 'Invite her.', 'other': 'Invite them.'};
|
||||||
|
}
|
||||||
|
//#enddocregion
|
@ -12,7 +12,7 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
|||||||
|
|
||||||
// #docregion JsonPipe
|
// #docregion JsonPipe
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'json-example',
|
selector: 'json-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<p>Without JSON pipe:</p>
|
<p>Without JSON pipe:</p>
|
||||||
<pre>{{object}}</pre>
|
<pre>{{object}}</pre>
|
||||||
@ -20,25 +20,7 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
|||||||
<pre>{{object | json}}</pre>
|
<pre>{{object | json}}</pre>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class JsonPipeExample {
|
export class JsonPipeComponent {
|
||||||
object: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2, 3, 4, 5]}};
|
object: Object = {foo: 'bar', baz: 'qux', nested: {xyz: 3, numbers: [1, 2, 3, 4, 5]}};
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'example-app',
|
|
||||||
template: `
|
|
||||||
<h1>JsonPipe Example</h1>
|
|
||||||
<json-example></json-example>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
export class AppCmp {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({imports: [BrowserModule], bootstrap: [AppCmp], declarations: [AppCmp, JsonPipeExample]})
|
|
||||||
class AppModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
||||||
}
|
|
@ -12,33 +12,15 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
|||||||
|
|
||||||
// #docregion LowerUpperPipe
|
// #docregion LowerUpperPipe
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'lowerupper-example',
|
selector: 'lowerupper-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<label>Name: </label><input #name (keyup)="change(name.value)" type="text">
|
<label>Name: </label><input #name (keyup)="change(name.value)" type="text">
|
||||||
<p>In lowercase: <pre>'{{value | lowercase}}'</pre></p>
|
<p>In lowercase: <pre>'{{value | lowercase}}'</pre>
|
||||||
<p>In uppercase: <pre>'{{value | uppercase}}'</pre></p>
|
<p>In uppercase: <pre>'{{value | uppercase}}'</pre>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class LowerUpperPipeExample {
|
export class LowerUpperPipeComponent {
|
||||||
value: string;
|
value: string;
|
||||||
change(value: string) { this.value = value; }
|
change(value: string) { this.value = value; }
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'example-app',
|
|
||||||
template: `
|
|
||||||
<h1>LowercasePipe & UppercasePipe Example</h1>
|
|
||||||
<lowerupper-example></lowerupper-example>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
export class AppCmp {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({imports: [BrowserModule], bootstrap: [AppCmp]})
|
|
||||||
class AppModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
||||||
}
|
|
66
modules/@angular/examples/common/pipes/ts/module.ts
Normal file
66
modules/@angular/examples/common/pipes/ts/module.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Component, NgModule} from '@angular/core';
|
||||||
|
import {BrowserModule} from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import {AsyncObservablePipeComponent, AsyncPromisePipeComponent} from './async_pipe';
|
||||||
|
import {DatePipeComponent} from './date_pipe';
|
||||||
|
import {I18nPluralPipeComponent, I18nSelectPipeComponent} from './i18n_pipe';
|
||||||
|
import {JsonPipeComponent} from './json_pipe';
|
||||||
|
import {LowerUpperPipeComponent} from './lowerupper_pipe';
|
||||||
|
import {CurrencyPipeComponent, NumberPipeComponent, PercentPipeComponent} from './number_pipe';
|
||||||
|
import {SlicePipeListComponent, SlicePipeStringComponent} from './slice_pipe';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'example-app',
|
||||||
|
template: `
|
||||||
|
<h1>Pipe Example</h1>
|
||||||
|
|
||||||
|
<h2><code>async</code></h2>
|
||||||
|
<async-promise-pipe></async-promise-pipe>
|
||||||
|
<async-observable-pipe></async-observable-pipe>
|
||||||
|
|
||||||
|
<h2><code>date</code></h2>
|
||||||
|
<date-pipe></date-pipe>
|
||||||
|
|
||||||
|
<h2><code>json</code></h2>
|
||||||
|
<json-pipe></json-pipe>
|
||||||
|
|
||||||
|
<h2><code>lower</code>, <code>upper</code></h2>
|
||||||
|
<lowerupper-pipe></lowerupper-pipe>
|
||||||
|
|
||||||
|
<h2><code>number</code></h2>
|
||||||
|
<number-pipe></number-pipe>
|
||||||
|
<percent-pipe></percent-pipe>
|
||||||
|
<currency-pipe></currency-pipe>
|
||||||
|
|
||||||
|
<h2><code>slice</code></h2>
|
||||||
|
<slice-string-pipe></slice-string-pipe>
|
||||||
|
<slice-list-pipe></slice-list-pipe>
|
||||||
|
|
||||||
|
<h2><code>i18n</code></h2>
|
||||||
|
<i18n-plural-pipe></i18n-plural-pipe>
|
||||||
|
<i18n-select-pipe></i18n-select-pipe>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class ExampleAppComponent {
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
AsyncPromisePipeComponent, AsyncObservablePipeComponent, ExampleAppComponent, JsonPipeComponent,
|
||||||
|
DatePipeComponent, LowerUpperPipeComponent, NumberPipeComponent, PercentPipeComponent,
|
||||||
|
CurrencyPipeComponent, SlicePipeStringComponent, SlicePipeListComponent,
|
||||||
|
I18nPluralPipeComponent, I18nSelectPipeComponent
|
||||||
|
],
|
||||||
|
imports: [BrowserModule],
|
||||||
|
bootstrap: [ExampleAppComponent]
|
||||||
|
})
|
||||||
|
export class AppModule {
|
||||||
|
}
|
@ -12,7 +12,7 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
|||||||
|
|
||||||
// #docregion NumberPipe
|
// #docregion NumberPipe
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'number-example',
|
selector: 'number-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<p>e (no formatting): {{e}}</p>
|
<p>e (no formatting): {{e}}</p>
|
||||||
<p>e (3.1-5): {{e | number:'3.1-5'}}</p>
|
<p>e (3.1-5): {{e | number:'3.1-5'}}</p>
|
||||||
@ -20,21 +20,21 @@ import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
|||||||
<p>pi (3.5-5): {{pi | number:'3.5-5'}}</p>
|
<p>pi (3.5-5): {{pi | number:'3.5-5'}}</p>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class NumberPipeExample {
|
export class NumberPipeComponent {
|
||||||
pi: number = 3.141;
|
pi: number = 3.141592;
|
||||||
e: number = 2.718281828459045;
|
e: number = 2.718281828459045;
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
// #docregion PercentPipe
|
// #docregion PercentPipe
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'percent-example',
|
selector: 'percent-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<p>A: {{a | percent}}</p>
|
<p>A: {{a | percent}}</p>
|
||||||
<p>B: {{b | percent:'4.3-5'}}</p>
|
<p>B: {{b | percent:'4.3-5'}}</p>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class PercentPipeExample {
|
export class PercentPipeComponent {
|
||||||
a: number = 0.259;
|
a: number = 0.259;
|
||||||
b: number = 1.3495;
|
b: number = 1.3495;
|
||||||
}
|
}
|
||||||
@ -42,37 +42,14 @@ export class PercentPipeExample {
|
|||||||
|
|
||||||
// #docregion CurrencyPipe
|
// #docregion CurrencyPipe
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'currency-example',
|
selector: 'currency-pipe',
|
||||||
template: `<div>
|
template: `<div>
|
||||||
<p>A: {{a | currency:'USD':false}}</p>
|
<p>A: {{a | currency:'USD':false}}</p>
|
||||||
<p>B: {{b | currency:'USD':true:'4.2-2'}}</p>
|
<p>B: {{b | currency:'USD':true:'4.2-2'}}</p>
|
||||||
</div>`
|
</div>`
|
||||||
})
|
})
|
||||||
export class CurrencyPipeExample {
|
export class CurrencyPipeComponent {
|
||||||
a: number = 0.259;
|
a: number = 0.259;
|
||||||
b: number = 1.3495;
|
b: number = 1.3495;
|
||||||
}
|
}
|
||||||
// #enddocregion
|
// #enddocregion
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'example-app',
|
|
||||||
template: `
|
|
||||||
<h1>Numeric Pipe Examples</h1>
|
|
||||||
<h2>NumberPipe Example</h2>
|
|
||||||
<number-example></number-example>
|
|
||||||
<h2>PercentPipe Example</h2>
|
|
||||||
<percent-example></percent-example>
|
|
||||||
<h2>CurrencyPipeExample</h2>
|
|
||||||
<currency-example></currency-example>
|
|
||||||
`
|
|
||||||
})
|
|
||||||
export class AppCmp {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NgModule({imports: [BrowserModule], bootstrap: [AppCmp]})
|
|
||||||
class AppModule {
|
|
||||||
}
|
|
||||||
|
|
||||||
export function main() {
|
|
||||||
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user