Compare commits

..

51 Commits

Author SHA1 Message Date
2045268cec chore(release): v2.1.0-beta.0 2016-09-23 11:41:35 -07:00
fb1076b44a docs(changelog): release notes for 2.1.0-beta.0 2016-09-23 11:37:28 -07:00
6fc46526ae fix(upgrade): allow attribute selectors for components in ng2 which are not part of upgrade (#11808)
fixes #11280
2016-09-24 02:47:16 +09:00
3ef5ede6d6 chore(compiler): emit ([] as any[]) instead of purely []. (#11846)
In SNC mode `[]` has type of never[], so we cast it to any[] to
typecheck correctly see
https://github.com/Microsoft/TypeScript/issues/10479.

This is temporary workaround, until we fully
migrate the framework to TS 2.0 and strictNullChecks.
2016-09-24 02:21:59 +09:00
136621ebc9 docs(Component): API docs for .encapsulation and .interpolation 2016-09-22 11:01:16 -07:00
f23b22a0f4 refactor: misc cleanup 2016-09-22 11:01:16 -07:00
0ca971c5bd refactor(common): cleanup (#11668) 2016-09-22 10:34:00 -07:00
3a6fcee0e6 docs(core): mark TestBed as stable api and add preliminary docs (#11767)
TestBed was accidentaly ommited from the 'stable' api list during the API sweep before final. We do consider it to be stable.
2016-09-22 10:32:17 -07:00
8972137c29 docs(contributing): remove preview references (#11795) 2016-09-22 10:31:56 -07:00
cc6481077f ci(BrowserStack): add Safari 10 (#11796) 2016-09-22 10:31:38 -07:00
c041b93418 refactor(TemplateParser): clearer error message for on* binding (#11802)
fixes #11756
2016-09-22 10:31:18 -07:00
bc33765913 chore(ISSUE_TEMPLATE): update Angular version field (#11821) 2016-09-22 10:29:12 -07:00
31dce72b7b fix(router): update the router not to reset router state when updating root component (#11799) 2016-09-21 11:37:43 -07:00
212f8dbde7 fix(forms): disable all radios with disable() 2016-09-20 15:00:12 -07:00
44da4984f9 fix(forms): support unbound disabled in ngModel (#11736) 2016-09-20 14:55:47 -07:00
d95344430c chore(zone.js): update to 0.6.25 (#11725) 2016-09-20 14:55:07 -07:00
131626fc61 fix(compiler): Safe property access expressions work in event bindings (#11724) 2016-09-20 14:54:53 -07:00
676bb0fa7d feat(router): update dts files 2016-09-20 14:53:52 -07:00
5a849829c4 feat(router): add router preloader to optimistically preload routes 2016-09-20 14:53:52 -07:00
671f73448c refactor: misc cleanup (#11654) 2016-09-19 17:15:57 -07:00
51d73d3e4e fix(forms): make setDisabledState optional for reactive form directives (#11731)
Closes #11719
2016-09-19 16:26:33 -07:00
bf81b06a28 docs(forms): add select control examples (#11728) 2016-09-19 16:25:33 -07:00
0621f07a2c refactor: misc cleanup 2016-09-19 16:24:31 -07:00
1225ecfb14 chore(node): allow current node version
node current is 6.6.0
see https://github.com/nodejs/LTS#lts_schedule
2016-09-19 16:24:31 -07:00
5509453e72 refactor(common): pipe code cleanup 2016-09-19 16:19:28 -07:00
70488ed382 fix(OfflineCompiler): support older TS versions (#11734) 2016-09-19 15:36:25 -07:00
03aedbe54b fix(OfflineCompiler): Do not provide I18N values when they're not specified
fixes #11643
2016-09-19 10:44:33 -07:00
8395aab25d refactor(OfflineCompiler): cleanup 2016-09-19 10:44:33 -07:00
0dc15eb64a fix(ContentChild): query descendants by default
fixes #1645
2016-09-19 10:42:46 -07:00
cba885a1fb refactor: code cleanup 2016-09-19 10:42:46 -07:00
fa4723a208 docs(forms): add radio button examples (#11676) 2016-09-19 10:41:20 -07:00
5bf08b886f build(npm): update fsevents to 1.0.14 (#11686)
fixes #11685
2016-09-18 16:04:46 -07:00
89802316b9 docs(injector): API docs - remove lone code-block backticks (#11653)
The triple backticks in the markdown of the API entry are unbalanced.
2016-09-18 16:04:04 -07:00
2300c23332 fix(docs): Fixed wording for NgModule schemas (#11620) 2016-09-18 16:03:43 -07:00
fa39965a37 build(gulp): fail on ddescribe / iit left in tests
Closes #10524
2016-09-18 16:01:50 -07:00
115f0fa842 refactor(gulpfile): cleanup, add comments 2016-09-18 16:01:50 -07:00
734b8b8c13 fix(compiler): [attribute~=value] selector (#11696)
Change the seperator regular expression to ignore tildes which are followed by an equal sign.

Closes #9644
2016-09-18 15:58:19 -07:00
54b41f57be docs(Host): fix the API example (#11684)
fixes #11681
2016-09-18 15:56:13 -07:00
df4254ae89 refactor(facade): move isPromise to core private (#10573) 2016-09-18 15:55:08 -07:00
14ee75924b fix(common): fix ngOnChanges signature of NgTemplateOutlet directive 2016-09-15 11:00:30 -07:00
bd4045b6e7 fix(MetadataResolver): throw Component.moduleId is not a string
fixes #11590
2016-09-15 10:57:37 -07:00
255099aa61 refactor(MetadataResolver): cleanup 2016-09-15 10:57:37 -07:00
1c24096650 refactor(benchpress): add more types 2016-09-15 10:17:10 -07:00
32aeb1052d refactor(benchpress): normalize phase b into B and e into E
This simplifies the perflog metrics and prevents future errors.
2016-09-15 10:17:10 -07:00
838d4bbf6c fix(benchpress): support measuring scriptTime and other metrics of page reload.
E.g. for benchmarks that measure page start time
2016-09-15 10:17:10 -07:00
c4114c2f66 finished refactoring 2016-09-15 10:17:10 -07:00
37b8691c8c refactor(benchpress): remove chrome < v44 support 2016-09-15 10:17:10 -07:00
93054d4e3d refactor(benchpress): remove facades from chrome_driver_extension 2016-09-15 10:17:10 -07:00
cfc12c6539 docs(api): changes to correct jade errors in API doc gen (#11619) 2016-09-15 09:09:00 -07:00
c0bdd89b5d docs(readme): update the note about RC 2016-09-14 19:37:19 -07:00
d5515473bf docs: update README.md for npm packages 2016-09-14 17:14:02 -07:00
146 changed files with 2999 additions and 2363 deletions

View File

@ -20,7 +20,7 @@
**Please tell us about your environment:**
<!-- 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 -->
* **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 ]

2
.nvmrc
View File

@ -1 +1 @@
5.4.1
6.6.0

View File

@ -1,7 +1,7 @@
language: node_js
sudo: false
node_js:
- '5.4.1'
- '6.6.0'
addons:
# firefox: "38.0"
@ -20,20 +20,9 @@ cache:
directories:
- ./node_modules
- ./.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:
global:
# - KARMA_JS_BROWSERS=ChromeNoSandbox
# - E2E_BROWSERS=ChromeOnTravis
# - LOGS_DIR=/tmp/angular-build/logs
# - ARCH=linux-x64
# GITHUB_TOKEN_ANGULAR
# This is needed for the e2e Travis matrix task to publish packages to github for continuous packages delivery.
- secure: "fq/U7VDMWO8O8SnAQkdbkoSe2X92PVqg4d044HmRYVmcf6YbO48+xeGJ8yOk0pCBwl3ISO4Q2ot0x546kxfiYBuHkZetlngZxZCtQiFT9kyId8ZKcYdXaIW9OVdw3Gh3tQyUwDucfkVhqcs52D6NZjyE2aWZ4/d1V4kWRO/LMgo="
@ -52,146 +41,11 @@ matrix:
- env: "CI_MODE=saucelabs_optional"
- env: "CI_MODE=browserstack_optional"
install:
- ./scripts/ci-lite/install.sh
before_script:
script:
- ./scripts/ci-lite/build.sh && ./scripts/ci-lite/test.sh
after_script:
- ./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=

View File

@ -1,3 +1,38 @@
<a name="2.1.0-beta.0"></a>
# [2.1.0-beta.0](https://github.com/angular/angular/compare/2.0.0...2.1.0-beta.0) (2016-09-23)
### Features
* **router:** add router preloader to optimistically preload routes ([5a84982](https://github.com/angular/angular/commit/5a84982))
* **router:** update dts files ([676bb0f](https://github.com/angular/angular/commit/676bb0f))
### Bug Fixes
* **router:** update the router not to reset router state when updating root component ([#11799](https://github.com/angular/angular/issues/11799)) ([31dce72](https://github.com/angular/angular/commit/31dce72))
Note: 2.1.0-beta.0 release also contains all the changes present in the 2.0.1 release.
<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)

View File

@ -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?
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?
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?
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
a proposal for your work first, to be sure that we can use it. Angular 2 is in developer preview
and we are not ready to accept major contributions ahead of the full release.
a proposal for your work first, to be sure that we can use it.
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

View File

@ -17,7 +17,6 @@ repository for [Angular 2][ng2] Typescript/JavaScript (JS).
Angular2 for [Dart][dart] can be found at [dart-lang/angular2][ng2dart].
Angular 2 is currently in **Release Candidate**.
## Quickstart
@ -32,7 +31,6 @@ guidelines for [contributing][contributing] and then check out one of our issues
[browserstack]: https://www.browserstack.com/
[contributing]: http://github.com/angular/angular/blob/master/CONTRIBUTING.md
[dart]: http://www.dartlang.org
[dartium]: http://www.dartlang.org/tools/dartium
[quickstart]: https://angular.io/docs/ts/latest/quickstart.html
[ng2]: http://angular.io
[ngDart]: http://angulardart.org

View File

@ -22,9 +22,11 @@ var CIconfiguration = {
'Safari7': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari9': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'Safari10': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'iOS7': { unitTest: {target: 'BS', required: true}, e2e: {target: null, required: true}},
'iOS8': { unitTest: {target: 'BS', required: false}, e2e: {target: null, required: true}},
'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}}
};
@ -83,6 +85,12 @@ var customLaunchers = {
platform: 'OS X 10.11',
version: '9.0'
},
'SL_SAFARI10': {
base: 'SauceLabs',
browserName: 'safari',
platform: 'OS X 10.12',
version: '10.0'
},
'SL_IOS7': {
base: 'SauceLabs',
browserName: 'iphone',
@ -101,6 +109,12 @@ var customLaunchers = {
platform: 'OS X 10.10',
version: '9.3'
},
'SL_IOS10': {
base: 'SauceLabs',
browserName: 'iphone',
platform: 'OS X 10.10',
version: '10.0'
},
'SL_IE9': {
base: 'SauceLabs',
browserName: 'internet explorer',
@ -186,6 +200,12 @@ var customLaunchers = {
os: 'OS X',
os_version: 'El Capitan'
},
'BS_SAFARI10': {
base: 'BrowserStack',
browser: 'safari',
os: 'OS X',
os_version: 'Sierra'
},
'BS_IOS7': {
base: 'BrowserStack',
device: 'iPhone 5S',
@ -204,6 +224,12 @@ var customLaunchers = {
os: 'ios',
os_version: '9.1'
},
'BS_IOS10': {
base: 'BrowserStack',
device: 'iPhone SE',
os: 'ios',
os_version: '10.0'
},
'BS_IE9': {
base: 'BrowserStack',
browser: 'ie',
@ -271,12 +297,12 @@ var customLaunchers = {
var sauceAliases = {
'ALL': Object.keys(customLaunchers).filter(function(item) {return customLaunchers[item].base == 'SauceLabs';}),
'DESKTOP': ['SL_CHROME', 'SL_FIREFOX', 'SL_IE9', 'SL_IE10', 'SL_IE11', 'SL_EDGE', 'SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
'MOBILE': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5', 'SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
'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', 'SL_IOS10'],
'ANDROID': ['SL_ANDROID4.1', 'SL_ANDROID4.2', 'SL_ANDROID4.3', 'SL_ANDROID4.4', 'SL_ANDROID5'],
'IE': ['SL_IE9', 'SL_IE10', 'SL_IE11'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9'],
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9'],
'IOS': ['SL_IOS7', 'SL_IOS8', 'SL_IOS9', 'SL_IOS10'],
'SAFARI': ['SL_SAFARI7', 'SL_SAFARI8', 'SL_SAFARI9', 'SL_SAFARI10'],
'BETA': ['SL_CHROMEBETA', 'SL_FIREFOXBETA'],
'DEV': ['SL_CHROMEDEV', 'SL_FIREFOXDEV'],
'CI_REQUIRED': buildConfiguration('unitTest', 'SL', true),
@ -285,12 +311,12 @@ var sauceAliases = {
var browserstackAliases = {
'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'],
'MOBILE': ['BS_ANDROID4.3', 'BS_ANDROID4.4', 'BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_WINDOWSPHONE'],
'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_IOS10', 'BS_WINDOWSPHONE'],
'ANDROID': ['BS_ANDROID4.3', 'BS_ANDROID4.4'],
'IE': ['BS_IE9', 'BS_IE10', 'BS_IE11'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9'],
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9'],
'IOS': ['BS_IOS7', 'BS_IOS8', 'BS_IOS9', 'BS_IOS10'],
'SAFARI': ['BS_SAFARI7', 'BS_SAFARI8', 'BS_SAFARI9', 'BS_SAFARI10'],
'CI_REQUIRED': buildConfiguration('unitTest', 'BS', true),
'CI_OPTIONAL': buildConfiguration('unitTest', 'BS', false)
};

View File

@ -110,6 +110,7 @@ do
$TSC -p ${SRCDIR}/tsconfig-build.json
cp ${SRCDIR}/package.json ${DESTDIR}/
cp ${PWD}/modules/@angular/README.md ${DESTDIR}/
if [[ -e ${SRCDIR}/tsconfig-testing.json ]]; then
echo "====== COMPILING TESTING: ${TSC} -p ${SRCDIR}/tsconfig-testing.json"

View File

@ -2,18 +2,26 @@
// THIS CHECK SHOULD BE THE FIRST THING IN THIS FILE
// This is to ensure that we catch env issues before we error while requiring other dependencies.
require('./tools/check-environment')(
{requiredNpmVersion: '>=3.5.3 <4.0.0', requiredNodeVersion: '>=5.4.1 <6.0.0'});
require('./tools/check-environment')({
requiredNpmVersion: '>=3.5.3 <4.0.0',
requiredNodeVersion: '>=5.4.1 <7.0.0',
});
const gulp = require('gulp');
const path = require('path');
const os = require('os');
const srcsToFmt =
['tools/**/*.ts', 'modules/@angular/**/*.ts', '!tools/public_api_guard/**/*.d.ts',
'modules/playground/**/*.ts', 'modules/benchmarks/**/*.ts', 'modules/e2e_util/**/*.ts'];
// clang-format entry points
const srcsToFmt = [
'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', () => {
const format = require('gulp-clang-format');
const clangFormat = require('clang-format');
@ -21,6 +29,7 @@ gulp.task('format:enforce', () => {
format.checkFormat('file', clangFormat, {verbose: true, fail: true}));
});
// Format the source code with clang-format (see .clang-format)
gulp.task('format', () => {
const format = require('gulp-clang-format');
const clangFormat = require('clang-format');
@ -49,7 +58,7 @@ const entrypoints = [
'dist/packages-dist/http/index.d.ts',
'dist/packages-dist/http/testing/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 publicApiArgs = [
@ -58,22 +67,24 @@ const publicApiArgs = [
'--allowModuleIdentifiers', 'jasmine',
'--allowModuleIdentifiers', 'protractor',
'--allowModuleIdentifiers', 'angular',
'--onStabilityMissing', 'error'
'--onStabilityMissing', 'error',
].concat(entrypoints);
// Build angular
gulp.task('build.sh', (done) => {
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
gulp.task('public-api:enforce', (done) => {
const childProcess = require('child_process');
childProcess
.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'})
.on('close', (errorCode) => {
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) => {
const childProcess = require('child_process');
childProcess
.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'})
.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');
// Built-in rules are at
// https://github.com/palantir/tslint#supported-rules
@ -105,22 +128,23 @@ gulp.task('lint', ['format:enforce', 'tools:build'], () => {
tslint: require('tslint').default,
configuration: tslintConfig,
rulesDirectory: 'dist/tools/tslint',
formatter: 'prose'
formatter: 'prose',
}))
.pipe(tslint.report({emitError: true}));
});
gulp.task('tools:build', (done) => { tsc('tools/', done); });
// Check for circular dependency in the source code
gulp.task('check-cycle', (done) => {
const madge = require('madge');
var dependencyObject = madge(['dist/all/'], {
const dependencyObject = madge(['dist/all/'], {
format: 'cjs',
extensions: ['.js'],
onParseFile: function(data) { data.src = data.src.replace(/\/\* circular \*\//g, "//"); }
});
var circularDependencies = dependencyObject.circular().getArray();
const circularDependencies = dependencyObject.circular().getArray();
if (circularDependencies.length > 0) {
console.log('Found circular dependencies!');
console.log(circularDependencies);
@ -129,33 +153,36 @@ gulp.task('check-cycle', (done) => {
done();
});
// Serve the built files
gulp.task('serve', () => {
let connect = require('gulp-connect');
let cors = require('cors');
const connect = require('gulp-connect');
const cors = require('cors');
connect.server({
root: `${__dirname}/dist`,
port: 8000,
livereload: false,
open: false,
middleware: (connect, opt) => [cors()]
middleware: (connect, opt) => [cors()],
});
});
// Serve the examples
gulp.task('serve-examples', () => {
let connect = require('gulp-connect');
let cors = require('cors');
const connect = require('gulp-connect');
const cors = require('cors');
connect.server({
root: `${__dirname}/dist/examples`,
port: 8001,
livereload: false,
open: false,
middleware: (connect, opt) => [cors()]
middleware: (connect, opt) => [cors()],
});
});
// Update the changelog with the latest changes
gulp.task('changelog', () => {
const conventionalChangelog = require('gulp-conventional-changelog');
@ -177,8 +204,13 @@ function tsc(projectPath, done) {
childProcess
.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)],
{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;
}

View File

@ -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

View 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

View File

@ -42,11 +42,11 @@ class Profiler {
}
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) {
this._markerEvents.push({ph: 'e', ts: timeEnded - this._profilerStartTime, name: name});
this._markerEvents.push({ph: 'E', ts: timeEnded - this._profilerStartTime, name: name});
}
}

View File

@ -6,18 +6,15 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Map} from './facade/collection';
import {Date, DateWrapper} from './facade/lang';
export class MeasureValues {
constructor(
public runIndex: number, public timeStamp: Date, public values: {[key: string]: any}) {}
toJson() {
return {
'timeStamp': DateWrapper.toJson(this.timeStamp),
'timeStamp': this.timeStamp.toJSON(),
'runIndex': this.runIndex,
'values': this.values
'values': this.values,
};
}
}

View File

@ -9,8 +9,6 @@
import {Inject, Injectable, OpaqueToken} from '@angular/core';
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 {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';
}
}
StringMapWrapper.forEach(
this._microMetrics, (desc, name) => { StringMapWrapper.set(res, name, desc); });
for (let name in this._microMetrics) {
res[name] = this._microMetrics[name];
}
return res;
}
@ -126,8 +125,8 @@ export class PerflogMetric extends Metric {
.then((_) => this._endMeasure(restartMeasure))
.then((forceGcMeasureValues) => {
this._captureFrames = originalFrameCaptureValue;
StringMapWrapper.set(measureValues, 'forcedGcTime', forceGcMeasureValues['gcTime']);
StringMapWrapper.set(measureValues, 'forcedGcAmount', forceGcMeasureValues['gcAmount']);
measureValues['forcedGcTime'] = forceGcMeasureValues['gcTime'];
measureValues['forcedGcAmount'] = forceGcMeasureValues['gcAmount'];
return measureValues;
});
});
@ -152,7 +151,7 @@ export class PerflogMetric extends Metric {
return this._driverExtension.readPerfLog().then((events) => {
this._addEvents(events);
var result = this._aggregateEvents(this._remainingEvents, markName);
if (isPresent(result)) {
if (result) {
this._remainingEvents = events;
return result;
}
@ -166,14 +165,14 @@ export class PerflogMetric extends Metric {
private _addEvents(events: PerfLogEvent[]) {
var needSort = false;
events.forEach(event => {
if (StringWrapper.equals(event['ph'], 'X')) {
if (event['ph'] === 'X') {
needSort = true;
var startEvent: PerfLogEvent = {};
var endEvent: PerfLogEvent = {};
StringMapWrapper.forEach(event, (value, prop) => {
(<any>startEvent)[prop] = value;
(<any>endEvent)[prop] = value;
});
for (let prop in event) {
startEvent[prop] = event[prop];
endEvent[prop] = event[prop];
}
startEvent['ph'] = 'B';
endEvent['ph'] = 'E';
endEvent['ts'] = startEvent['ts'] + startEvent['dur'];
@ -185,7 +184,7 @@ export class PerflogMetric extends Metric {
});
if (needSort) {
// 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'];
return diff > 0 ? 1 : diff < 0 ? -1 : 0;
});
@ -208,7 +207,9 @@ export class PerflogMetric extends Metric {
result['frameTime.worst'] = 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) {
result['receivedData'] = 0;
}
@ -218,6 +219,24 @@ export class PerflogMetric extends Metric {
var markStartEvent: 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 renderTimeInScript = 0;
@ -228,120 +247,99 @@ export class PerflogMetric extends Metric {
var intervalStarts: {[key: string]: PerfLogEvent} = {};
var intervalStartCount: {[key: string]: number} = {};
var inMeasureRange = false;
events.forEach((event) => {
var ph = event['ph'];
var name = event['name'];
var microIterations = 1;
var microIterationsMatch = name.match(_MICRO_ITERATIONS_REGEX);
if (isPresent(microIterationsMatch)) {
if (microIterationsMatch) {
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)) {
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')) {
if (this._requestCount && name === 'sendRequest') {
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'];
} else if (StringWrapper.equals(name, 'navigationStart')) {
// We count data + requests since the last navigationStart
// (there might be chrome extensions loaded by selenium before our page, so there
// will likely be more than one navigationStart).
if (this._receivedData) {
result['receivedData'] = 0;
}
if (ph === 'B' && name === _MARK_NAME_FRAME_CAPUTRE) {
if (frameCaptureStartEvent) {
throw new Error('can capture frames only once per benchmark run');
}
if (this._requestCount) {
result['requestCount'] = 0;
if (!this._captureFrames) {
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 (isPresent(frameCaptureStartEvent) && isBlank(frameCaptureEndEvent) &&
StringWrapper.equals(name, 'frame')) {
frameTimestamps.push(event['ts']);
if (frameTimestamps.length >= 2) {
frameTimes.push(
frameTimestamps[frameTimestamps.length - 1] -
frameTimestamps[frameTimestamps.length - 2]);
}
}
if (ph === 'B') {
if (!intervalStarts[name]) {
intervalStartCount[name] = 1;
intervalStarts[name] = event;
} else {
intervalStartCount[name]++;
}
if (StringWrapper.equals(ph, 'B') || StringWrapper.equals(ph, 'b')) {
if (isBlank(intervalStarts[name])) {
intervalStartCount[name] = 1;
intervalStarts[name] = event;
} else {
intervalStartCount[name]++;
}
} else if (
(StringWrapper.equals(ph, 'E') || StringWrapper.equals(ph, 'e')) &&
isPresent(intervalStarts[name])) {
intervalStartCount[name]--;
if (intervalStartCount[name] === 0) {
var startEvent = intervalStarts[name];
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;
} else if ((ph === 'E') && intervalStarts[name]) {
intervalStartCount[name]--;
if (intervalStartCount[name] === 0) {
var startEvent = intervalStarts[name];
var duration = (event['ts'] - startEvent['ts']);
intervalStarts[name] = null;
if (name === 'gc') {
result['gcTime'] += duration;
var amount =
(startEvent['args']['usedHeapSize'] - event['args']['usedHeapSize']) / 1000;
result['gcAmount'] += amount;
var majorGc = event['args']['majorGc'];
if (majorGc && majorGc) {
result['majorGcTime'] += duration;
}
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) &&
isBlank(frameCaptureEndEvent)) {
if (frameCaptureStartEvent && !frameCaptureEndEvent) {
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');
}
if (frameTimes.length > 0) {

View File

@ -6,7 +6,7 @@
* 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 {StringMapWrapper} from '../facade/collection';
@ -48,9 +48,9 @@ export class UserMetric extends Metric {
if (values.every(isNumber)) {
Promise.all(names.map(name => adapter.executeScript(`delete window.${name}`)))
.then((_: any[]) => {
let map = StringMapWrapper.create();
let map: {[k: string]: any} = {};
for (let i = 0, n = names.length; i < n; i++) {
StringMapWrapper.set(map, names[i], values[i]);
map[names[i]] = values[i];
}
resolve(map);
}, reject);

View File

@ -49,7 +49,7 @@ export class Sampler {
}
private _iterate(lastState: SampleState): Promise<SampleState> {
var resultPromise: Promise<any>;
var resultPromise: Promise<SampleState>;
if (this._prepare !== Options.NO_PREPARE) {
resultPromise = this._driver.waitFor(this._prepare);
} else {
@ -77,5 +77,5 @@ export class Sampler {
}
export class SampleState {
constructor(public completeSample: any[], public validSample: any[]) {}
constructor(public completeSample: MeasureValues[], public validSample: MeasureValues[]) {}
}

View File

@ -12,13 +12,20 @@ import {Options} from './common_options';
import {isBlank, isPresent} from './facade/lang';
export type PerfLogEvent = {
cat?: string,
ph?: 'X' | 'B' | 'E' | 'b' | 'e',
[key: string]: any
} & {
ph?: 'X' | 'B' | 'E' | 'I',
ts?: number,
dur?: number,
name?: 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:
* - cat: category of the event
* - name: event name: 'script', 'gc', 'render', ...
* - ph: phase: 'B' (begin), 'E' (end), 'b' (nestable start), 'e' (nestable end), 'X' (Complete
*event)
* - ph: phase: 'B' (begin), 'E' (end), 'X' (Complete event), 'I' (Instant event)
* - ts: timestamp in ms, e.g. 12345
* - pid: process id
* - args: arguments, e.g. {heapSize: 1234}

View File

@ -9,16 +9,12 @@
import {Inject, Injectable} from '@angular/core';
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 {PerfLogEvent, PerfLogFeatures, WebDriverExtension} from '../web_driver_extension';
/**
* 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'
* to the list above.
@ -35,18 +31,18 @@ export class ChromeDriverExtension extends WebDriverExtension {
}
private _parseChromeVersion(userAgent: string): number {
if (isBlank(userAgent)) {
if (!userAgent) {
return -1;
}
var v = StringWrapper.split(userAgent, /Chrom(e|ium)\//g)[2];
if (isBlank(v)) {
var v = userAgent.split(/Chrom(e|ium)\//g)[2];
if (!v) {
return -1;
}
v = v.split('.')[0];
if (isBlank(v)) {
if (!v) {
return -1;
}
return NumberWrapper.parseInt(v, 10);
return parseInt(v, 10);
}
gc() { return this._driver.executeScript('window.gc()'); }
@ -57,7 +53,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
timeEnd(name: string, restartName: string = null): Promise<any> {
var script = `console.timeEnd('${name}');`;
if (isPresent(restartName)) {
if (restartName) {
script += `console.time('${restartName}');`;
}
return this._driver.executeScript(script);
@ -74,10 +70,10 @@ export class ChromeDriverExtension extends WebDriverExtension {
var events: PerfLogEvent[] = [];
entries.forEach(entry => {
var message = JSON.parse(entry['message'])['message'];
if (StringWrapper.equals(message['method'], 'Tracing.dataCollected')) {
if (message['method'] === 'Tracing.dataCollected') {
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!');
}
});
@ -87,107 +83,64 @@ export class ChromeDriverExtension extends WebDriverExtension {
private _convertPerfRecordsToEvents(
chromeEvents: Array<{[key: string]: any}>, normalizedEvents: PerfLogEvent[] = null) {
if (isBlank(normalizedEvents)) {
if (!normalizedEvents) {
normalizedEvents = [];
}
var majorGCPids = {};
chromeEvents.forEach((event) => {
var categories = this._parseCategories(event['cat']);
var name = event['name'];
if (this._isEvent(categories, name, ['blink.console'])) {
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);
}
const categories = this._parseCategories(event['cat']);
const normalizedEvent = this._convertEvent(event, categories);
if (normalizedEvent != null) normalizedEvents.push(normalizedEvent);
});
return normalizedEvents;
}
private _processAsPreChrome45Event(
event: {[key: string]: any}, categories: string[], majorGCPids: {[key: string]: any}) {
private _convertEvent(event: {[key: string]: any}, categories: string[]) {
var name = event['name'];
var args = event['args'];
var pid = event['pid'];
var ph = event['ph'];
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'});
if (this._isEvent(categories, name, ['blink.console'])) {
return normalizeEvent(event, {'name': name});
} else if (this._isEvent(
categories, name, ['disabled-by-default-devtools.timeline'], 'GCEvent')) {
var normArgs: {[key: string]: any} = {
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
if (StringWrapper.equals(ph, 'E')) {
normArgs['majorGc'] = isPresent(majorGCPids[pid]) && majorGCPids[pid];
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) {
return normalizeEvent(event, {'name': 'frame'});
}
majorGCPids[pid] = false;
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (
this._isEvent(categories, name, ['v8'], 'majorGC') && StringWrapper.equals(ph, 'B')) {
majorGCPids[pid] = true;
}
return null; // nothing useful in this event
}
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')) {
this._isEvent(categories, name, ['disabled-by-default-devtools.timeline'], 'Rasterize') ||
this._isEvent(
categories, name, ['disabled-by-default-devtools.timeline'], 'CompositeLayers')) {
return normalizeEvent(event, {'name': 'render'});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MajorGC')) {
var normArgs = {
'majorGc': true,
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (this._isEvent(categories, name, ['devtools.timeline', 'v8'], 'MinorGC')) {
var normArgs = {
'majorGc': false,
'usedHeapSize': isPresent(args['usedHeapSizeAfter']) ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
'usedHeapSize': args['usedHeapSizeAfter'] !== undefined ? args['usedHeapSizeAfter'] :
args['usedHeapSizeBefore']
};
return normalizeEvent(event, {'name': 'gc', 'args': normArgs});
} else if (
this._isEvent(categories, name, ['devtools.timeline'], 'FunctionCall') &&
(isBlank(args) || isBlank(args['data']) ||
(!StringWrapper.equals(args['data']['scriptName'], 'InjectedScript') &&
!StringWrapper.equals(args['data']['scriptName'], '')))) {
(!args || !args['data'] ||
(args['data']['scriptName'] !== 'InjectedScript' && args['data']['scriptName'] !== ''))) {
return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent(categories, name, ['devtools.timeline'], 'EvaluateScript')) {
return normalizeEvent(event, {'name': 'script'});
} else if (this._isEvent(
categories, name, ['devtools.timeline', 'blink'], 'UpdateLayoutTree')) {
@ -205,7 +158,7 @@ export class ChromeDriverExtension extends WebDriverExtension {
let normArgs = {'url': data['url'], 'method': data['requestMethod']};
return normalizeEvent(event, {'name': 'sendRequest', 'args': normArgs});
} 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
}
@ -216,9 +169,8 @@ export class ChromeDriverExtension extends WebDriverExtension {
eventCategories: string[], eventName: string, expectedCategories: string[],
expectedName: string = null): boolean {
var hasCategories = expectedCategories.reduce(
(value, cat) => { return value && ListWrapper.contains(eventCategories, cat); }, true);
return isBlank(expectedName) ? hasCategories :
hasCategories && StringWrapper.equals(eventName, expectedName);
(value, cat) => { return value && eventCategories.indexOf(cat) !== -1; }, true);
return !expectedName ? hasCategories : hasCategories && eventName === expectedName;
}
perfLogFeatures(): PerfLogFeatures {
@ -226,28 +178,31 @@ export class ChromeDriverExtension extends WebDriverExtension {
}
supports(capabilities: {[key: string]: any}): boolean {
return this._majorChromeVersion != -1 &&
StringWrapper.equals(capabilities['browserName'].toLowerCase(), 'chrome');
return this._majorChromeVersion >= 44 && capabilities['browserName'].toLowerCase() === 'chrome';
}
}
function normalizeEvent(
chromeEvent: {[key: string]: any}, data: {[key: string]: any}): PerfLogEvent {
var ph = chromeEvent['ph'];
if (StringWrapper.equals(ph, 'S')) {
ph = 'b';
} else if (StringWrapper.equals(ph, 'F')) {
ph = 'e';
function normalizeEvent(chromeEvent: {[key: string]: any}, data: PerfLogEvent): PerfLogEvent {
var ph = chromeEvent['ph'].toUpperCase();
if (ph === 'S') {
ph = 'B';
} else if (ph === 'F') {
ph = 'E';
} else if (ph === 'R') {
// mark events from navigation timing
ph = 'I';
}
var result: {[key: string]: any} =
{'pid': chromeEvent['pid'], 'ph': ph, 'cat': 'timeline', 'ts': chromeEvent['ts'] / 1000};
if (chromeEvent['ph'] === 'X') {
if (ph === 'X') {
var dur = chromeEvent['dur'];
if (isBlank(dur)) {
if (dur === undefined) {
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;
}

View File

@ -97,7 +97,7 @@ export class IOsDriverExtension extends WebDriverExtension {
}
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 = {
'cat': 'timeline',
'name': name,
@ -122,9 +122,9 @@ function createEndEvent(name: string, time: number, args: any = null) {
}
function createMarkStartEvent(name: string, time: number) {
return createEvent('b', name, time);
return createEvent('B', name, time);
}
function createMarkEndEvent(name: string, time: number) {
return createEvent('e', name, time);
return createEvent('E', name, time);
}

View File

@ -33,7 +33,7 @@ export function main() {
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
if (isBlank(microMetrics)) {
microMetrics = StringMapWrapper.create();
microMetrics = {};
}
var providers: Provider[] = [
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) => {
var events = [
[

View File

@ -24,7 +24,7 @@ export function main() {
new PerfLogFeatures({render: true, gc: true, frameCapture: true, userTiming: true});
}
if (isBlank(userMetrics)) {
userMetrics = StringMapWrapper.create();
userMetrics = {};
}
wdAdapter = new MockDriverAdapter();
var providers: Provider[] = [

View File

@ -21,16 +21,16 @@ export class TraceEventFactory {
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); }
end(name: string, time: number, args: any = null) { return this.create('E', name, time, args); }
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) {

View File

@ -14,8 +14,6 @@ import {TraceEventFactory} from '../trace_event_factory';
export function main() {
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 =
'"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 = [];
}
if (isBlank(userAgent)) {
userAgent = CHROME44_USER_AGENT;
userAgent = CHROME45_USER_AGENT;
}
log = [];
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',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineEvents.complete('FunctionCall', 1100, 5500, null)])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.complete('script', 1.1, 5.5, null),
]);
async.done();
});
}));
it('should normalize times to ms and forward ph and pid event properties',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineV8Events.complete('FunctionCall', 1100, 5500, null)])
.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 = chromeTimelineEvents.create('X', 'FunctionCall', 1100, null);
event['tdur'] = 5500;
createExtension([event]).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]).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([chromeTimelineEvents.start('FunctionCall', 0)])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('script', 0),
]);
async.done();
});
}));
it('should report FunctionCall events as "script"',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineV8Events.start('FunctionCall', 0)])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('script', 0),
]);
async.done();
});
}));
it('should report gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
]);
async.done();
});
}));
it('should report EvaluateScript events as "script"',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([chromeTimelineV8Events.start('EvaluateScript', 0)])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('script', 0),
]);
async.done();
});
}));
it('should ignore major gc from different processes',
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
v8EventsOtherProcess.start('majorGC', 1100, null),
v8EventsOtherProcess.end('majorGC', 1200, null),
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': false}),
]);
async.done();
});
}));
it('should report minor gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension([
chromeTimelineV8Events.start('MinorGC', 1000, {'usedHeapSizeBefore': 1000}),
chromeTimelineV8Events.end('MinorGC', 2000, {'usedHeapSizeAfter': 0}),
])
.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([
chromeTimelineEvents.start('GCEvent', 1000, {'usedHeapSizeBefore': 1000}),
v8Events.start('majorGC', 1100, null),
v8Events.end('majorGC', 1200, null),
chromeTimelineEvents.end('GCEvent', 2000, {'usedHeapSizeAfter': 0}),
])
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.start('gc', 1.0, {'usedHeapSize': 1000}),
normEvents.end('gc', 2.0, {'usedHeapSize': 0, 'majorGc': true}),
]);
async.done();
});
}));
it('should report major gc', inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[
chromeTimelineV8Events.start('MajorGC', 1000, {'usedHeapSizeBefore': 1000}),
chromeTimelineV8Events.end('MajorGC', 2000, {'usedHeapSizeAfter': 0}),
], )
.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();
});
}));
['RecalculateStyles', 'Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
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"`,
['Layout', 'UpdateLayerTree', 'Paint'].forEach((recordType) => {
it(`should report ${recordType} as "render"`,
inject([AsyncTestCompleter], (async: AsyncTestCompleter) => {
createExtension(
[
chromeBlinkTimelineEvents.start('UpdateLayoutTree', 1234),
chromeBlinkTimelineEvents.end('UpdateLayoutTree', 2345)
],
CHROME45_USER_AGENT)
chrome45TimelineEvents.start(recordType, 1234),
chrome45TimelineEvents.end(recordType, 2345)
], )
.readPerfLog()
.then((events) => {
expect(events).toEqual([
@ -320,70 +185,80 @@ export function main() {
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)', () => {
it('should execute a dummy script before reading them',
@ -404,8 +279,7 @@ export function main() {
[
chromeTimelineEvents.start(recordType, 1234),
chromeTimelineEvents.end(recordType, 2345)
],
CHROME45_USER_AGENT)
], )
.readPerfLog()
.then((events) => {
expect(events).toEqual([
@ -426,7 +300,7 @@ export function main() {
.readPerfLog()
.then((events) => {
expect(events).toEqual([
normEvents.create('i', 'frame', 1.1),
normEvents.instant('frame', 1.1),
]);
async.done();
});

View File

@ -61,8 +61,7 @@ export class NgPlural {
addCase(value: string, switchView: SwitchView): void { this._caseViews[value] = switchView; }
/** @internal */
_updateView(): void {
private _updateView(): void {
this._clearViews();
const cases = Object.keys(this._caseViews);
@ -70,13 +69,11 @@ export class NgPlural {
this._activateView(this._caseViews[key]);
}
/** @internal */
_clearViews() {
private _clearViews() {
if (this._activeView) this._activeView.destroy();
}
/** @internal */
_activateView(view: SwitchView) {
private _activateView(view: SwitchView) {
if (view) {
this._activeView = view;
this._activeView.create();
@ -91,10 +88,12 @@ export class NgPlural {
* 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>
* ```
* <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.
*

View File

@ -32,10 +32,8 @@ import {Directive, DoCheck, ElementRef, Input, KeyValueChangeRecord, KeyValueDif
*/
@Directive({selector: '[ngStyle]'})
export class NgStyle implements DoCheck {
/** @internal */
_ngStyle: {[key: string]: string};
/** @internal */
_differ: KeyValueDiffer;
private _ngStyle: {[key: string]: string};
private _differ: KeyValueDiffer;
constructor(
private _differs: KeyValueDiffers, private _ngEl: ElementRef, private _renderer: Renderer) {}

View File

@ -111,8 +111,7 @@ export class NgSwitch {
}
}
/** @internal */
_emptyAllActiveViews(): void {
private _emptyAllActiveViews(): void {
const activeContainers = this._activeViews;
for (var i = 0; i < activeContainers.length; i++) {
activeContainers[i].destroy();
@ -120,9 +119,7 @@ export class NgSwitch {
this._activeViews = [];
}
/** @internal */
_activateViews(views: SwitchView[]): void {
// TODO(vicb): assert(this._activeViews.length === 0);
private _activateViews(views: SwitchView[]): void {
if (views) {
for (var i = 0; i < views.length; i++) {
views[i].create();
@ -141,8 +138,7 @@ export class NgSwitch {
views.push(view);
}
/** @internal */
_deregisterView(value: any, view: SwitchView): void {
private _deregisterView(value: any, view: SwitchView): void {
// `_CASE_DEFAULT` is used a marker for non-registered cases
if (value === _CASE_DEFAULT) return;
const views = this._valueViews.get(value);
@ -162,10 +158,11 @@ export class NgSwitch {
* expression.
*
* @howToUse
* <container-element [ngSwitch]="switch_expression">
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
* </container-element>
*
* ```
* <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
@ -180,10 +177,8 @@ export class NgSwitch {
@Directive({selector: '[ngSwitchCase]'})
export class NgSwitchCase {
// `_CASE_DEFAULT` is used as a marker for a not yet initialized value
/** @internal */
_value: any = _CASE_DEFAULT;
/** @internal */
_view: SwitchView;
private _value: any = _CASE_DEFAULT;
private _view: SwitchView;
private _switch: NgSwitch;
constructor(
@ -207,10 +202,12 @@ export class NgSwitchCase {
* 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>
* ```
* <container-element [ngSwitch]="switch_expression">
* <some-element *ngSwitchCase="match_expression_1">...</some-element>
* <some-other-element *ngSwitchDefault>...</some-other-element>
* </container-element>
* ```
*
* @description
*

View File

@ -6,7 +6,7 @@
* 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';
/**
* @ngModule CommonModule
@ -44,7 +44,7 @@ export class NgTemplateOutlet implements OnChanges {
@Input()
set ngTemplateOutlet(templateRef: TemplateRef<Object>) { this._templateRef = templateRef; }
ngOnChanges() {
ngOnChanges(changes: SimpleChanges) {
if (this._viewRef) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._viewRef));
}

View File

@ -67,7 +67,7 @@ export enum Plural {
Two,
Few,
Many,
Other
Other,
}
/**

View File

@ -49,16 +49,20 @@ export class Location {
_subject: EventEmitter<any> = new EventEmitter();
/** @internal */
_baseHref: string;
/** @internal */
_platformStrategy: LocationStrategy;
constructor(platformStrategy: LocationStrategy) {
this._platformStrategy = platformStrategy;
var browserBaseHref = this._platformStrategy.getBaseHref();
const browserBaseHref = this._platformStrategy.getBaseHref();
this._baseHref = Location.stripTrailingSlash(_stripIndexHtml(browserBaseHref));
this._platformStrategy.onPopState(
(ev) => { this._subject.emit({'url': this.path(true), 'pop': true, 'type': ev.type}); });
this._platformStrategy.onPopState((ev) => {
this._subject.emit({
'url': this.path(true),
'pop': true,
'type': ev.type,
});
});
}
/**

View File

@ -8,7 +8,7 @@
import {ChangeDetectorRef, OnDestroy, Pipe, WrappedValue} from '@angular/core';
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';
interface SubscriptionStrategy {
@ -37,9 +37,8 @@ class PromiseStrategy implements SubscriptionStrategy {
onDestroy(subscription: any): void {}
}
var _promiseStrategy = new PromiseStrategy();
var _observableStrategy = new ObservableStrategy();
var __unused: Promise<any>; // avoid unused import when Promise union types are erased
const _promiseStrategy = new PromiseStrategy();
const _observableStrategy = new ObservableStrategy();
/**
* @ngModule CommonModule
@ -68,30 +67,24 @@ var __unused: Promise<any>; // avoid unused import when Promise union types are
*/
@Pipe({name: 'async', pure: false})
export class AsyncPipe implements OnDestroy {
/** @internal */
_latestValue: Object = null;
/** @internal */
_latestReturnedValue: Object = null;
private _latestValue: Object = null;
private _latestReturnedValue: Object = null;
/** @internal */
_subscription: Object = null;
/** @internal */
_obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
/** @internal */
_ref: ChangeDetectorRef;
private _subscription: Object = null;
private _obj: Observable<any>|Promise<any>|EventEmitter<any> = null;
private _strategy: SubscriptionStrategy = null;
constructor(_ref: ChangeDetectorRef) { this._ref = _ref; }
constructor(private _ref: ChangeDetectorRef) {}
ngOnDestroy(): void {
if (isPresent(this._subscription)) {
if (this._subscription) {
this._dispose();
}
}
transform(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
if (isBlank(this._obj)) {
if (isPresent(obj)) {
if (!this._obj) {
if (obj) {
this._subscribe(obj);
}
this._latestReturnedValue = this._latestValue;
@ -105,33 +98,32 @@ export class AsyncPipe implements OnDestroy {
if (this._latestValue === 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 */
_subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
private _subscribe(obj: Observable<any>|Promise<any>|EventEmitter<any>): void {
this._obj = obj;
this._strategy = this._selectStrategy(obj);
this._subscription = this._strategy.createSubscription(
obj, (value: Object) => this._updateLatestValue(obj, value));
}
/** @internal */
_selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
private _selectStrategy(obj: Observable<any>|Promise<any>|EventEmitter<any>): any {
if (isPromise(obj)) {
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 */
_dispose(): void {
private _dispose(): void {
this._strategy.dispose(this._subscription);
this._latestValue = null;
this._latestReturnedValue = null;
@ -139,8 +131,7 @@ export class AsyncPipe implements OnDestroy {
this._obj = null;
}
/** @internal */
_updateLatestValue(async: any, value: Object) {
private _updateLatestValue(async: any, value: Object) {
if (async === this._obj) {
this._latestValue = value;
this._ref.markForCheck();

View File

@ -7,10 +7,8 @@
*/
import {Inject, LOCALE_ID, Pipe, PipeTransform} from '@angular/core';
import {StringMapWrapper} from '../facade/collection';
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';
@ -83,7 +81,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@Pipe({name: 'date', pure: true})
export class DatePipe implements PipeTransform {
/** @internal */
static _ALIASES: {[key: string]: String} = {
static _ALIASES: {[key: string]: string} = {
'medium': 'yMMMdjms',
'short': 'yMdjm',
'fullDate': 'yMMMMEEEEd',
@ -104,23 +102,15 @@ export class DatePipe implements PipeTransform {
}
if (NumberWrapper.isNumeric(value)) {
value = DateWrapper.fromMillis(parseFloat(value));
} else if (isString(value)) {
value = DateWrapper.fromISOString(value);
value = parseFloat(value);
}
if (StringMapWrapper.contains(DatePipe._ALIASES, pattern)) {
pattern = <string>StringMapWrapper.get(DatePipe._ALIASES, pattern);
}
return DateFormatter.format(value, this._locale, pattern);
return DateFormatter.format(
new Date(value), this._locale, DatePipe._ALIASES[pattern] || pattern);
}
private supports(obj: any): boolean {
if (isDate(obj) || NumberWrapper.isNumeric(obj)) {
return true;
}
if (isString(obj) && isDate(DateWrapper.fromISOString(obj))) {
return true;
}
return false;
return isDate(obj) || NumberWrapper.isNumeric(obj) ||
(typeof obj === 'string' && isDate(new Date(obj)));
}
}

View File

@ -7,7 +7,7 @@
*/
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 {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -43,6 +43,6 @@ export class I18nPluralPipe implements PipeTransform {
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());
}
}

View File

@ -7,7 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isString} from '../facade/lang';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@ -29,7 +29,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
export class LowerCasePipe implements PipeTransform {
transform(value: string): string {
if (isBlank(value)) return value;
if (!isString(value)) {
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(LowerCasePipe, value);
}
return value.toLowerCase();

View File

@ -9,21 +9,23 @@
import {Inject, LOCALE_ID, Pipe, PipeTransform, Type} from '@angular/core';
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';
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
function formatNumber(
pipe: Type<any>, locale: string, value: number | string, style: NumberFormatStyle,
digits: string, currency: string = null, currencyAsSymbol: boolean = false): string {
if (isBlank(value)) return null;
// Convert strings to numbers
value = isString(value) && NumberWrapper.isNumeric(value) ? +value : value;
if (!isNumber(value)) {
value = typeof value === 'string' && NumberWrapper.isNumeric(value) ? +value : value;
if (typeof value !== 'number') {
throw new InvalidPipeArgumentError(pipe, value);
}
let minInt: number;
let minFraction: number;
let maxFraction: number;
@ -34,8 +36,8 @@ function formatNumber(
maxFraction = 3;
}
if (isPresent(digits)) {
var parts = digits.match(_NUMBER_FORMAT_REGEXP);
if (digits) {
let parts = digits.match(_NUMBER_FORMAT_REGEXP);
if (parts === null) {
throw new Error(`${digits} is not a valid digit info for number pipes`);
}
@ -49,12 +51,13 @@ function formatNumber(
maxFraction = NumberWrapper.parseIntAutoRadix(parts[5]);
}
}
return NumberFormatter.format(value as number, locale, style, {
minimumIntegerDigits: minInt,
minimumFractionDigits: minFraction,
maximumFractionDigits: maxFraction,
currency: currency,
currencyAsSymbol: currencyAsSymbol
currencyAsSymbol: currencyAsSymbol,
});
}

View File

@ -7,8 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {ListWrapper} from '../facade/collection';
import {StringWrapper, isArray, isBlank, isString} from '../facade/lang';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
@ -58,16 +57,15 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
@Pipe({name: 'slice', pure: false})
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 (!this.supports(value)) {
throw new InvalidPipeArgumentError(SlicePipe, value);
}
if (isString(value)) {
return StringWrapper.slice(value, start, end);
}
return ListWrapper.slice(value, start, end);
return value.slice(start, end);
}
private supports(obj: any): boolean { return isString(obj) || isArray(obj); }
private supports(obj: any): boolean { return typeof obj === 'string' || Array.isArray(obj); }
}

View File

@ -7,7 +7,7 @@
*/
import {Pipe, PipeTransform} from '@angular/core';
import {isBlank, isString} from '../facade/lang';
import {isBlank} from '../facade/lang';
import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
/**
@ -28,7 +28,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
export class UpperCasePipe implements PipeTransform {
transform(value: string): string {
if (isBlank(value)) return value;
if (!isString(value)) {
if (typeof value !== 'string') {
throw new InvalidPipeArgumentError(UpperCasePipe, value);
}
return value.toUpperCase();

View 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;

View File

@ -18,9 +18,7 @@ import {EventEmitter, Injectable} from '@angular/core';
@Injectable()
export class SpyLocation implements Location {
urlChanges: string[] = [];
/** @internal */
private _history: LocationState[] = [new LocationState('', '')];
/** @internal */
private _historyIndex: number = 0;
/** @internal */
_subject: EventEmitter<any> = new EventEmitter();

View File

@ -115,27 +115,27 @@ export class DirectiveNormalizer {
const templateStyles = this.normalizeStylesheet(new CompileStylesheetMetadata(
{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;
if (isBlank(encapsulation)) {
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;
}
return new CompileTemplateMetadata({
encapsulation,
template: template,
templateUrl: templateAbsUrl,
styles: allStyles,
styleUrls: allStyleUrls,
template,
templateUrl: templateAbsUrl, styles, styleUrls,
externalStylesheets: templateMeta.externalStylesheets,
ngContentSelectors: visitor.ngContentSelectors,
animations: templateMeta.animations,
interpolation: templateMeta.interpolation
interpolation: templateMeta.interpolation,
});
}
@ -251,7 +251,6 @@ function _cloneDirectiveWithTemplate(
viewProviders: directive.viewProviders,
queries: directive.queries,
viewQueries: directive.viewQueries,
entryComponents: directive.entryComponents,
template: template
entryComponents: directive.entryComponents, template,
});
}

View File

@ -376,7 +376,7 @@ export class AstTransformer implements AstVisitor {
}
visitAll(asts: any[]): any[] {
var res = ListWrapper.createFixedSize(asts.length);
var res = new Array(asts.length);
for (var i = 0; i < asts.length; ++i) {
res[i] = asts[i].visit(this);
}

View File

@ -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 {StringMapWrapper} from '../src/facade/collection';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
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 {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver';
@ -42,7 +40,7 @@ export class CompileMetadataResolver {
if (identifier.indexOf('(') >= 0) {
// case: anonymous functions!
let found = this._anonymousTypes.get(token);
if (isBlank(found)) {
if (!found) {
this._anonymousTypes.set(token, this._anonymousTypeIndex++);
found = this._anonymousTypes.get(token);
}
@ -67,18 +65,21 @@ export class CompileMetadataResolver {
}
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);
}
getAnimationStateMetadata(value: AnimationStateMetadata): cpl.CompileAnimationStateMetadata {
if (value instanceof AnimationStateDeclarationMetadata) {
var styles = this.getAnimationStyleMetadata(value.styles);
const styles = this.getAnimationStyleMetadata(value.styles);
return new cpl.CompileAnimationStateDeclarationMetadata(value.stateNameExpr, styles);
} else if (value instanceof AnimationStateTransitionMetadata) {
}
if (value instanceof AnimationStateTransitionMetadata) {
return new cpl.CompileAnimationStateTransitionMetadata(
value.stateChangeExpr, this.getAnimationMetadata(value.steps));
}
return null;
}
@ -89,21 +90,28 @@ export class CompileMetadataResolver {
getAnimationMetadata(value: AnimationMetadata): cpl.CompileAnimationMetadata {
if (value instanceof AnimationStyleMetadata) {
return this.getAnimationStyleMetadata(value);
} else if (value instanceof AnimationKeyframesSequenceMetadata) {
}
if (value instanceof AnimationKeyframesSequenceMetadata) {
return new cpl.CompileAnimationKeyframesSequenceMetadata(
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
.getAnimationMetadata(value.styles);
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) {
return new cpl.CompileAnimationGroupMetadata(steps);
} else {
return new cpl.CompileAnimationSequenceMetadata(steps);
}
return new cpl.CompileAnimationSequenceMetadata(steps);
}
return null;
}
@ -111,47 +119,49 @@ export class CompileMetadataResolver {
getDirectiveMetadata(directiveType: Type<any>, throwIfNotFound = true):
cpl.CompileDirectiveMetadata {
directiveType = resolveForwardRef(directiveType);
var meta = this._directiveCache.get(directiveType);
if (isBlank(meta)) {
var dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound);
let meta = this._directiveCache.get(directiveType);
if (!meta) {
const dirMeta = this._directiveResolver.resolve(directiveType, throwIfNotFound);
if (!dirMeta) {
return null;
}
var templateMeta: cpl.CompileTemplateMetadata = null;
var changeDetectionStrategy: ChangeDetectionStrategy = null;
var viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
var moduleUrl = staticTypeModuleUrl(directiveType);
var entryComponentMetadata: cpl.CompileTypeMetadata[] = [];
let templateMeta: cpl.CompileTemplateMetadata = null;
let changeDetectionStrategy: ChangeDetectionStrategy = null;
let viewProviders: Array<cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[]> = [];
let moduleUrl = staticTypeModuleUrl(directiveType);
let entryComponentMetadata: cpl.CompileTypeMetadata[] = [];
let selector = dirMeta.selector;
if (dirMeta instanceof Component) {
var cmpMeta = <Component>dirMeta;
assertArrayOfStrings('styles', cmpMeta.styles);
assertInterpolationSymbols('interpolation', cmpMeta.interpolation);
var animations = isPresent(cmpMeta.animations) ?
cmpMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
// Component
assertArrayOfStrings('styles', dirMeta.styles);
assertArrayOfStrings('styleUrls', dirMeta.styleUrls);
assertInterpolationSymbols('interpolation', dirMeta.interpolation);
const animations = dirMeta.animations ?
dirMeta.animations.map(e => this.getAnimationEntryMetadata(e)) :
null;
assertArrayOfStrings('styles', cmpMeta.styles);
assertArrayOfStrings('styleUrls', cmpMeta.styleUrls);
templateMeta = new cpl.CompileTemplateMetadata({
encapsulation: cmpMeta.encapsulation,
template: cmpMeta.template,
templateUrl: cmpMeta.templateUrl,
styles: cmpMeta.styles,
styleUrls: cmpMeta.styleUrls,
encapsulation: dirMeta.encapsulation,
template: dirMeta.template,
templateUrl: dirMeta.templateUrl,
styles: dirMeta.styles,
styleUrls: dirMeta.styleUrls,
animations: animations,
interpolation: cmpMeta.interpolation
interpolation: dirMeta.interpolation
});
changeDetectionStrategy = cmpMeta.changeDetection;
if (isPresent(dirMeta.viewProviders)) {
changeDetectionStrategy = dirMeta.changeDetection;
if (dirMeta.viewProviders) {
viewProviders = this.getProvidersMetadata(
dirMeta.viewProviders, entryComponentMetadata,
`viewProviders for "${stringify(directiveType)}"`);
}
moduleUrl = componentModuleUrl(this._reflector, directiveType, cmpMeta);
if (cmpMeta.entryComponents) {
moduleUrl = componentModuleUrl(this._reflector, directiveType, dirMeta);
if (dirMeta.entryComponents) {
entryComponentMetadata =
flattenArray(cmpMeta.entryComponents)
flattenArray(dirMeta.entryComponents)
.map((type) => this.getTypeMetadata(type, staticTypeModuleUrl(type)))
.concat(entryComponentMetadata);
}
@ -159,27 +169,29 @@ export class CompileMetadataResolver {
selector = this._schemaRegistry.getDefaultComponentElementName();
}
} else {
// Directive
if (!selector) {
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)) {
providers = this.getProvidersMetadata(
dirMeta.providers, entryComponentMetadata,
`providers for "${stringify(directiveType)}"`);
}
var queries: cpl.CompileQueryMetadata[] = [];
var viewQueries: cpl.CompileQueryMetadata[] = [];
let queries: cpl.CompileQueryMetadata[] = [];
let viewQueries: cpl.CompileQueryMetadata[] = [];
if (isPresent(dirMeta.queries)) {
queries = this.getQueriesMetadata(dirMeta.queries, false, directiveType);
viewQueries = this.getQueriesMetadata(dirMeta.queries, true, directiveType);
}
meta = cpl.CompileDirectiveMetadata.create({
selector: selector,
exportAs: dirMeta.exportAs,
isComponent: isPresent(templateMeta),
isComponent: !!templateMeta,
type: this.getTypeMetadata(directiveType, moduleUrl),
template: templateMeta,
changeDetection: changeDetectionStrategy,
@ -199,7 +211,7 @@ export class CompileMetadataResolver {
getNgModuleMetadata(moduleType: any, throwIfNotFound = true): cpl.CompileNgModuleMetadata {
moduleType = resolveForwardRef(moduleType);
var compileMeta = this._ngModuleCache.get(moduleType);
let compileMeta = this._ngModuleCache.get(moduleType);
if (!compileMeta) {
const meta = this._ngModuleResolver.resolve(moduleType, throwIfNotFound);
if (!meta) {
@ -230,8 +242,9 @@ export class CompileMetadataResolver {
`provider for the NgModule '${stringify(importedModuleType)}'`));
}
}
if (importedModuleType) {
let importedMeta = this.getNgModuleMetadata(importedModuleType, false);
const importedMeta = this.getNgModuleMetadata(importedModuleType, false);
if (importedMeta === null) {
throw new Error(
`Unexpected ${this._getTypeDescriptor(importedType)} '${stringify(importedType)}' imported by the module '${stringify(moduleType)}'`);
@ -298,11 +311,13 @@ export class CompileMetadataResolver {
meta.providers, entryComponents,
`provider for the NgModule '${stringify(moduleType)}'`));
}
if (meta.entryComponents) {
entryComponents.push(
...flattenArray(meta.entryComponents)
.map(type => this.getTypeMetadata(type, staticTypeModuleUrl(type))));
}
if (meta.bootstrap) {
const typeMetadata = flattenArray(meta.bootstrap).map(type => {
if (!isValidType(type)) {
@ -313,7 +328,9 @@ export class CompileMetadataResolver {
});
bootstrapComponents.push(...typeMetadata);
}
entryComponents.push(...bootstrapComponents);
if (meta.schemas) {
schemas.push(...flattenArray(meta.schemas));
}
@ -323,19 +340,20 @@ export class CompileMetadataResolver {
compileMeta = new cpl.CompileNgModuleMetadata({
type: this.getTypeMetadata(moduleType, staticTypeModuleUrl(moduleType)),
providers: providers,
entryComponents: entryComponents,
bootstrapComponents: bootstrapComponents,
schemas: schemas,
declaredDirectives: declaredDirectives,
exportedDirectives: exportedDirectives,
declaredPipes: declaredPipes,
exportedPipes: exportedPipes,
importedModules: importedModules,
exportedModules: exportedModules,
transitiveModule: transitiveModule,
providers,
entryComponents,
bootstrapComponents,
schemas,
declaredDirectives,
exportedDirectives,
declaredPipes,
exportedPipes,
importedModules,
exportedModules,
transitiveModule,
id: meta.id,
});
transitiveModule.modules.push(compileMeta);
this._verifyModule(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!`);
}
});
moduleMeta.exportedPipes.forEach((pipeMeta) => {
if (!moduleMeta.transitiveModule.pipesSet.has(pipeMeta.type.reference)) {
throw new Error(
@ -362,15 +381,21 @@ export class CompileMetadataResolver {
private _getTypeDescriptor(type: Type<any>): string {
if (this._directiveResolver.resolve(type, false) !== null) {
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>) {
@ -434,7 +459,7 @@ export class CompileMetadataResolver {
type = resolveForwardRef(type);
return new cpl.CompileTypeMetadata({
name: this.sanitizeTokenName(type),
moduleUrl: moduleUrl,
moduleUrl,
reference: type,
diDeps: this.getDependenciesMetadata(type, dependencies),
lifecycleHooks: LIFECYCLE_HOOKS_VALUES.filter(hook => hasLifecycleHook(hook, type)),
@ -446,7 +471,7 @@ export class CompileMetadataResolver {
factory = resolveForwardRef(factory);
return new cpl.CompileFactoryMetadata({
name: this.sanitizeTokenName(factory),
moduleUrl: moduleUrl,
moduleUrl,
reference: factory,
diDeps: this.getDependenciesMetadata(factory, dependencies)
});
@ -454,12 +479,13 @@ export class CompileMetadataResolver {
getPipeMetadata(pipeType: Type<any>, throwIfNotFound = true): cpl.CompilePipeMetadata {
pipeType = resolveForwardRef(pipeType);
var meta = this._pipeCache.get(pipeType);
if (isBlank(meta)) {
var pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound);
let meta = this._pipeCache.get(pipeType);
if (!meta) {
const pipeMeta = this._pipeResolver.resolve(pipeType, throwIfNotFound);
if (!pipeMeta) {
return null;
}
meta = new cpl.CompilePipeMetadata({
type: this.getTypeMetadata(pipeType, staticTypeModuleUrl(pipeType)),
name: pipeMeta.name,
@ -473,10 +499,8 @@ export class CompileMetadataResolver {
getDependenciesMetadata(typeOrFunc: Type<any>|Function, dependencies: any[]):
cpl.CompileDiDependencyMetadata[] {
let hasUnknownDeps = false;
let params = isPresent(dependencies) ? dependencies : this._reflector.parameters(typeOrFunc);
if (isBlank(params)) {
params = [];
}
let params = dependencies || this._reflector.parameters(typeOrFunc) || [];
let dependenciesMetadata: cpl.CompileDiDependencyMetadata[] = params.map((param) => {
let isAttribute = false;
let isHost = false;
@ -486,8 +510,8 @@ export class CompileMetadataResolver {
let query: Query = null;
let viewQuery: Query = null;
var token: any = null;
if (isArray(param)) {
(<any[]>param).forEach((paramEntry) => {
if (Array.isArray(param)) {
param.forEach((paramEntry) => {
if (paramEntry instanceof Host) {
isHost = true;
} else if (paramEntry instanceof Self) {
@ -518,14 +542,15 @@ export class CompileMetadataResolver {
hasUnknownDeps = true;
return null;
}
return new cpl.CompileDiDependencyMetadata({
isAttribute: isAttribute,
isHost: isHost,
isSelf: isSelf,
isSkipSelf: isSkipSelf,
isOptional: isOptional,
query: isPresent(query) ? this.getQueryMetadata(query, null, typeOrFunc) : null,
viewQuery: isPresent(viewQuery) ? this.getQueryMetadata(viewQuery, null, typeOrFunc) : null,
isAttribute,
isHost,
isSelf,
isSkipSelf,
isOptional,
query: query ? this.getQueryMetadata(query, null, typeOrFunc) : null,
viewQuery: viewQuery ? this.getQueryMetadata(viewQuery, null, typeOrFunc) : null,
token: this.getTokenMetadata(token)
});
@ -533,8 +558,7 @@ export class CompileMetadataResolver {
if (hasUnknownDeps) {
let depsTokens =
dependenciesMetadata.map((dep) => { return dep ? stringify(dep.token) : '?'; })
.join(', ');
dependenciesMetadata.map((dep) => dep ? stringify(dep.token) : '?').join(', ');
throw new Error(
`Can't resolve all parameters for ${stringify(typeOrFunc)}: (${depsTokens}).`);
}
@ -544,7 +568,7 @@ export class CompileMetadataResolver {
getTokenMetadata(token: any): cpl.CompileTokenMetadata {
token = resolveForwardRef(token);
var compileToken: any /** TODO #9100 */;
let compileToken: cpl.CompileTokenMetadata;
if (isString(token)) {
compileToken = new cpl.CompileTokenMetadata({value: token});
} else {
@ -569,7 +593,7 @@ export class CompileMetadataResolver {
provider = new cpl.ProviderMeta(provider.provide, provider);
}
let compileProvider: cpl.CompileProviderMetadata|cpl.CompileTypeMetadata|any[];
if (isArray(provider)) {
if (Array.isArray(provider)) {
compileProvider = this.getProvidersMetadata(provider, targetEntryComponents, debugInfo);
} else if (provider instanceof cpl.ProviderMeta) {
let tokenMeta = this.getTokenMetadata(provider.token);
@ -607,17 +631,20 @@ export class CompileMetadataResolver {
}
private _getEntryComponentsFromProvider(provider: cpl.ProviderMeta): cpl.CompileTypeMetadata[] {
let components: cpl.CompileTypeMetadata[] = [];
let collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
const components: cpl.CompileTypeMetadata[] = [];
const collectedIdentifiers: cpl.CompileIdentifierMetadata[] = [];
if (provider.useFactory || provider.useExisting || provider.useClass) {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports useValue!`);
}
if (!provider.multi) {
throw new Error(`The ANALYZE_FOR_ENTRY_COMPONENTS token only supports 'multi = true'!`);
}
convertToCompileValue(provider.useValue, collectedIdentifiers);
collectedIdentifiers.forEach((identifier) => {
let dirMeta = this.getDirectiveMetadata(identifier.reference, false);
const dirMeta = this.getDirectiveMetadata(identifier.reference, false);
if (dirMeta) {
components.push(dirMeta.type);
}
@ -626,15 +653,15 @@ export class CompileMetadataResolver {
}
getProviderMetadata(provider: cpl.ProviderMeta): cpl.CompileProviderMetadata {
var compileDeps: cpl.CompileDiDependencyMetadata[];
var compileTypeMetadata: cpl.CompileTypeMetadata = null;
var compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
let compileDeps: cpl.CompileDiDependencyMetadata[];
let compileTypeMetadata: cpl.CompileTypeMetadata = null;
let compileFactoryMetadata: cpl.CompileFactoryMetadata = null;
if (isPresent(provider.useClass)) {
if (provider.useClass) {
compileTypeMetadata = this.getTypeMetadata(
provider.useClass, staticTypeModuleUrl(provider.useClass), provider.dependencies);
compileDeps = compileTypeMetadata.diDeps;
} else if (isPresent(provider.useFactory)) {
} else if (provider.useFactory) {
compileFactoryMetadata = this.getFactoryMetadata(
provider.useFactory, staticTypeModuleUrl(provider.useFactory), provider.dependencies);
compileDeps = compileFactoryMetadata.diDeps;
@ -645,8 +672,7 @@ export class CompileMetadataResolver {
useClass: compileTypeMetadata,
useValue: convertToCompileValue(provider.useValue, []),
useFactory: compileFactoryMetadata,
useExisting: isPresent(provider.useExisting) ? this.getTokenMetadata(provider.useExisting) :
null,
useExisting: provider.useExisting ? this.getTokenMetadata(provider.useExisting) : null,
deps: compileDeps,
multi: provider.multi
});
@ -655,38 +681,38 @@ export class CompileMetadataResolver {
getQueriesMetadata(
queries: {[key: string]: Query}, isViewQuery: boolean,
directiveType: Type<any>): cpl.CompileQueryMetadata[] {
var res: cpl.CompileQueryMetadata[] = [];
StringMapWrapper.forEach(queries, (query: Query, propertyName: string) => {
const res: cpl.CompileQueryMetadata[] = [];
Object.keys(queries).forEach((propertyName: string) => {
const query = queries[propertyName];
if (query.isViewQuery === isViewQuery) {
res.push(this.getQueryMetadata(query, propertyName, directiveType));
}
});
return res;
}
private _queryVarBindings(selector: any): string[] {
return StringWrapper.split(selector, /\s*,\s*/g);
}
private _queryVarBindings(selector: any): string[] { return selector.split(/\s*,\s*/); }
getQueryMetadata(q: Query, propertyName: string, typeOrFunc: Type<any>|Function):
cpl.CompileQueryMetadata {
var selectors: cpl.CompileTokenMetadata[];
if (isString(q.selector)) {
if (typeof q.selector === 'string') {
selectors = this._queryVarBindings(q.selector).map(varName => this.getTokenMetadata(varName));
} else {
if (!isPresent(q.selector)) {
if (!q.selector) {
throw new Error(
`Can't construct a query for the property "${propertyName}" of "${stringify(typeOrFunc)}" since the query selector wasn't defined.`);
}
selectors = [this.getTokenMetadata(q.selector)];
}
return new cpl.CompileQueryMetadata({
selectors: selectors,
selectors,
first: q.first,
descendants: q.descendants,
propertyName: propertyName,
read: isPresent(q.read) ? this.getTokenMetadata(q.read) : null
descendants: q.descendants, propertyName,
read: q.read ? this.getTokenMetadata(q.read) : null
});
}
}
@ -713,9 +739,9 @@ function getTransitiveModules(
function flattenArray(tree: any[], out: Array<any> = []): Array<any> {
if (tree) {
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
for (let i = 0; i < tree.length; i++) {
const item = resolveForwardRef(tree[i]);
if (Array.isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);
@ -733,16 +759,21 @@ function staticTypeModuleUrl(value: any): string {
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)) {
return staticTypeModuleUrl(type);
}
if (isPresent(cmpMetadata.moduleId)) {
var moduleId = cmpMetadata.moduleId;
var scheme = getUrlScheme(moduleId);
return isPresent(scheme) && scheme.length > 0 ? moduleId :
`package:${moduleId}${MODULE_SUFFIX}`;
const moduleId = cmpMetadata.moduleId;
if (typeof moduleId === 'string') {
const scheme = getUrlScheme(moduleId);
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);

View File

@ -10,7 +10,6 @@ import {SchemaMetadata} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, CompileTokenMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {ListWrapper} from './facade/collection';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
@ -58,10 +57,10 @@ export class OfflineCompiler {
compile(
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
let fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
let statements: o.Statement[] = [];
let exportedVars: string[] = [];
let outputSourceModules: SourceModule[] = [];
const fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
const statements: o.Statement[] = [];
const exportedVars: string[] = [];
const outputSourceModules: SourceModule[] = [];
// compile all ng modules
exportedVars.push(
@ -75,12 +74,12 @@ export class OfflineCompiler {
if (!ngModule) {
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
}
return Promise
.all([compMeta, ...ngModule.transitiveModule.directives].map(
dirMeta => this._directiveNormalizer.normalizeDirective(dirMeta).asyncResult))
.then((normalizedCompWithDirectives) => {
const compMeta = normalizedCompWithDirectives[0];
const dirMetas = normalizedCompWithDirectives.slice(1);
const [compMeta, ...dirMetas] = normalizedCompWithDirectives;
_assertComponent(compMeta);
// compile styles
@ -90,10 +89,11 @@ export class OfflineCompiler {
});
// compile components
exportedVars.push(this._compileComponentFactory(compMeta, fileSuffix, statements));
exportedVars.push(this._compileComponent(
compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas,
stylesCompileResults.componentStylesheet, fileSuffix, statements));
exportedVars.push(
this._compileComponentFactory(compMeta, fileSuffix, statements),
this._compileComponent(
compMeta, dirMetas, ngModule.transitiveModule.pipes, ngModule.schemas,
stylesCompileResults.componentStylesheet, fileSuffix, statements));
});
}))
.then(() => {
@ -106,19 +106,30 @@ export class OfflineCompiler {
}
private _compileModule(ngModuleType: StaticSymbol, targetStatements: o.Statement[]): string {
const ngModule = this._metadataResolver.getNgModuleMetadata(<any>ngModuleType);
let appCompileResult = this._ngModuleCompiler.compile(ngModule, [
new CompileProviderMetadata(
{token: resolveIdentifierToken(Identifiers.LOCALE_ID), useValue: this._localeId}),
new CompileProviderMetadata({
const ngModule = this._metadataResolver.getNgModuleMetadata(ngModuleType);
const providers: CompileProviderMetadata[] = [];
if (this._localeId) {
providers.push(new CompileProviderMetadata({
token: resolveIdentifierToken(Identifiers.LOCALE_ID),
useValue: this._localeId,
}));
}
if (this._translationFormat) {
providers.push(new CompileProviderMetadata({
token: resolveIdentifierToken(Identifiers.TRANSLATIONS_FORMAT),
useValue: this._translationFormat
})
]);
}));
}
const appCompileResult = this._ngModuleCompiler.compile(ngModule, providers);
appCompileResult.dependencies.forEach((dep) => {
dep.placeholder.name = _componentFactoryName(dep.comp);
dep.placeholder.moduleUrl = _ngfactoryModuleUrl(dep.comp.moduleUrl);
});
targetStatements.push(...appCompileResult.statements);
return appCompileResult.ngModuleFactoryVar;
}
@ -126,18 +137,19 @@ export class OfflineCompiler {
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
var hostMeta = createHostComponentMeta(compMeta);
var hostViewFactoryVar =
const hostMeta = createHostComponentMeta(compMeta);
const hostViewFactoryVar =
this._compileComponent(hostMeta, [compMeta], [], [], null, fileSuffix, targetStatements);
var compFactoryVar = _componentFactoryName(compMeta.type);
const compFactoryVar = _componentFactoryName(compMeta.type);
targetStatements.push(
o.variable(compFactoryVar)
.set(o.importExpr(resolveIdentifier(Identifiers.ComponentFactory), [o.importType(
compMeta.type)])
.instantiate(
[
o.literal(compMeta.selector), o.variable(hostViewFactoryVar),
o.importExpr(compMeta.type)
o.literal(compMeta.selector),
o.variable(hostViewFactoryVar),
o.importExpr(compMeta.type),
],
o.importType(
resolveIdentifier(Identifiers.ComponentFactory),
@ -150,15 +162,15 @@ export class OfflineCompiler {
compMeta: CompileDirectiveMetadata, directives: CompileDirectiveMetadata[],
pipes: CompilePipeMetadata[], schemas: SchemaMetadata[], componentStyles: CompiledStylesheet,
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);
var stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
var viewResult =
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
const viewResult =
this._viewCompiler.compileComponent(compMeta, parsedTemplate, stylesExpr, pipes);
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;
}
@ -180,10 +192,10 @@ export class OfflineCompiler {
function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[] {
compileResult.dependencies.forEach((dep) => {
if (dep instanceof ViewFactoryDependency) {
let vfd = <ViewFactoryDependency>dep;
const vfd = <ViewFactoryDependency>dep;
vfd.placeholder.moduleUrl = _ngfactoryModuleUrl(vfd.comp.moduleUrl);
} else if (dep instanceof ComponentFactoryDependency) {
let cfd = <ComponentFactoryDependency>dep;
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.name = _componentFactoryName(cfd.comp);
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
}
@ -201,7 +213,7 @@ function _resolveStyleStatements(
}
function _ngfactoryModuleUrl(compUrl: string): string {
var urlWithSuffix = _splitTypescriptSuffix(compUrl);
const urlWithSuffix = _splitTypescriptSuffix(compUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}
@ -220,13 +232,15 @@ function _assertComponent(meta: CompileDirectiveMetadata) {
}
function _splitTypescriptSuffix(path: string): string[] {
if (/\.d\.ts$/.test(path)) {
return [path.substring(0, path.length - 5), '.ts'];
if (path.endsWith('.d.ts')) {
return [path.slice(0, -5), '.ts'];
}
let lastDot = path.lastIndexOf('.');
const lastDot = path.lastIndexOf('.');
if (lastDot !== -1) {
return [path.substring(0, lastDot), path.substring(lastDot)];
} else {
return [path, ''];
}
return [path, ''];
}

View File

@ -75,6 +75,22 @@ class _TsEmitterVisitor extends AbstractEmitterVisitor implements o.TypeVisitor
super.visitLiteralExpr(ast, ctx, '(null as any)');
}
// Temporary workaround to support strictNullCheck enabled consumers of ngc emit.
// In SNC mode, [] have the type never[], so we cast here to any[].
// TODO: narrow the cast to a more explicit type, or use a pattern that does not
// start with [].concat. see https://github.com/angular/angular/pull/11846
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, ctx: EmitterVisitorContext): any {
if (ast.entries.length === 0) {
ctx.print('(');
}
const result = super.visitLiteralArrayExpr(ast, ctx);
if (ast.entries.length === 0) {
ctx.print(' as any)');
}
return result;
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
this._visitIdentifier(ast.value, ast.typeParams, ctx);
return null;

View File

@ -426,7 +426,7 @@ export class ShadowCss {
return scopedP;
};
const sep = /( |>|\+|~)\s*/g;
const sep = /( |>|\+|~(?!=))\s*/g;
const scopeAfter = selector.indexOf(_polyfillHostNoCombinator);
let scoped = '';

View File

@ -854,14 +854,14 @@ class TemplateParseVisitor implements html.Visitor {
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
bindingType = PropertyBindingType.Property;
this._assertNoEventBinding(boundPropertyName, sourceSpan);
this._assertNoEventBinding(boundPropertyName, sourceSpan, false);
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName, this._schemas)) {
let errorMsg =
`Can't bind to '${boundPropertyName}' since it isn't a known property of '${elementName}'.`;
if (elementName.indexOf('-') > -1) {
errorMsg +=
`\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);
}
@ -869,7 +869,7 @@ class TemplateParseVisitor implements html.Visitor {
} else {
if (parts[0] == ATTRIBUTE_PREFIX) {
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.
const mapPropName = this._schemaRegistry.getMappedPropName(boundPropertyName);
securityContext = this._schemaRegistry.securityContext(elementName, mapPropName);
@ -902,12 +902,23 @@ class TemplateParseVisitor implements html.Visitor {
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')) {
this._reportError(
`Binding to event attribute '${propName}' is disallowed ` +
`for security reasons, please use (${propName.slice(2)})=...`,
sourceSpan, ParseErrorLevel.FATAL);
let msg = `Binding to event attribute '${propName}' is disallowed for security reasons, ` +
`please use (${propName.slice(2)})=...`;
if (!isAttr) {
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)) {
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` +
`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);
}
}

View File

@ -117,7 +117,7 @@ export class CompileElement extends CompileNode {
setComponentView(compViewExpr: o.Expression) {
this._compViewExpr = compViewExpr;
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++) {
this.contentNodesByNgContentIndex[i] = [];
}

View File

@ -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
// 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;
if (this.needsTemporary(leftMostSafe.receiver)) {
// 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.
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
// 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
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

View File

@ -7,7 +7,7 @@
*/
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 {TestBed, inject} from '@angular/core/testing';
@ -23,7 +23,7 @@ export function main() {
describe('getDirectiveMetadata', () => {
it('should read metadata',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
var meta = resolver.getDirectiveMetadata(ComponentWithEverything);
const meta = resolver.getDirectiveMetadata(ComponentWithEverything);
expect(meta.selector).toEqual('someSelector');
expect(meta.exportAs).toEqual('someExportAs');
expect(meta.isComponent).toBe(true);
@ -52,6 +52,17 @@ export function main() {
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',
inject([CompileMetadataResolver], (resolver: CompileMetadataResolver) => {
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: ''})
class ComponentWithoutModuleId {
}
@Component({selector: 'someComponent', template: '', moduleId: <any>0})
class ComponentWithInvalidModuleId {
}
@Component({
selector: 'someSelector',
inputs: ['someProp'],

View File

@ -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] {}', 'a')).toEqual('one[attr][a] {}');
expect(s('[is="one"] {}', 'a')).toEqual('[is="one"][a] {}');
});

View File

@ -262,7 +262,7 @@ export function main() {
.toThrowError(`Template parse errors:
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.
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`);
});
@ -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:
'unknown' is not a known element:
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', () => {
expect(() => parse('<un-known></un-known>', [])).toThrowError(`Template parse errors:
'un-known' is not a known element:
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`);
});
});

View File

@ -8,8 +8,7 @@
import {DirectiveResolver} from '@angular/compiler';
import {AnimationEntryMetadata, Compiler, Component, Directive, Injectable, Injector, Provider, Type, resolveForwardRef} from '@angular/core';
import {Map} from './facade/collection';
import {isArray, isPresent} from './facade/lang';
import {isPresent} from './facade/lang';
import {ViewMetadata} from './private_import_core';
@ -156,7 +155,7 @@ function flattenArray(tree: any[], out: Array<Type<any>|any[]>): void {
if (!isPresent(tree)) return;
for (var i = 0; i < tree.length; i++) {
var item = resolveForwardRef(tree[i]);
if (isArray(item)) {
if (Array.isArray(item)) {
flattenArray(item, out);
} else {
out.push(item);

View File

@ -9,18 +9,12 @@
import {NgModuleResolver} from '@angular/compiler';
import {Compiler, Injectable, Injector, NgModule, Type} from '@angular/core';
import {Map} from './facade/collection';
@Injectable()
export class MockNgModuleResolver extends NgModuleResolver {
private _ngModules = new Map<Type<any>, NgModule>();
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.
*/
@ -36,10 +30,10 @@ export class MockNgModuleResolver extends NgModuleResolver {
* `NgModuleResolver`, see `setNgModule`.
*/
resolve(type: Type<any>, throwIfNotFound = true): NgModule {
var metadata = this._ngModules.get(type);
if (!metadata) {
metadata = super.resolve(type, throwIfNotFound);
}
return metadata;
return this._ngModules.get(type) || super.resolve(type, throwIfNotFound);
}
private get _compiler(): Compiler { return this._injector.get(Compiler); }
private _clearCacheFor(component: Type<any>) { this._compiler.clearCacheFor(component); }
}

View File

@ -9,8 +9,6 @@
import {PipeResolver} from '@angular/compiler';
import {Compiler, Injectable, Injector, Pipe, Type} from '@angular/core';
import {Map} from './facade/collection';
@Injectable()
export class MockPipeResolver extends PipeResolver {
private _pipes = new Map<Type<any>, Pipe>();

View File

@ -7,11 +7,9 @@
*/
import {ResourceLoader} from '@angular/compiler';
import {ListWrapper, Map} from './facade/collection';
import {ListWrapper} from './facade/collection';
import {isBlank, normalizeBlank} from './facade/lang';
/**
* 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.

View File

@ -6,7 +6,7 @@
* 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 {AnimationPlayer} from './animation_player';
@ -47,12 +47,12 @@ export class ViewAnimationMap {
getAllPlayers(): AnimationPlayer[] { return this._allPlayers; }
remove(element: any, animationName: string): void {
var playersByAnimation = this._map.get(element);
if (isPresent(playersByAnimation)) {
var player = playersByAnimation[animationName];
const playersByAnimation = this._map.get(element);
if (playersByAnimation) {
const player = playersByAnimation[animationName];
delete playersByAnimation[animationName];
var index = this._allPlayers.indexOf(player);
ListWrapper.removeAt(this._allPlayers, index);
const index = this._allPlayers.indexOf(player);
this._allPlayers.splice(index, 1);
if (StringMapWrapper.isEmpty(playersByAnimation)) {
this._map.delete(element);

View File

@ -6,7 +6,7 @@
* 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';

View File

@ -14,7 +14,6 @@ import {LOCALE_ID} from './i18n/tokens';
import {Compiler} from './linker/compiler';
import {ViewUtils} from './linker/view_utils';
import {NgModule} from './metadata';
import {Type} from './type';
export function _iterableDiffersFactory() {
return defaultIterableDiffers;

View File

@ -9,7 +9,8 @@
import {ErrorHandler} from '../src/error_handler';
import {ListWrapper} from '../src/facade/collection';
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 {APP_BOOTSTRAP_LISTENER, PLATFORM_INITIALIZER} from './application_tokens';
@ -25,9 +26,9 @@ import {Testability, TestabilityRegistry} from './testability/testability';
import {Type} from './type';
import {NgZone} from './zone/ng_zone';
var _devMode: boolean = true;
var _runModeLocked: boolean = false;
var _platform: PlatformRef;
let _devMode: boolean = true;
let _runModeLocked: boolean = false;
let _platform: PlatformRef;
/**
* 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.
*/
export function createPlatform(injector: Injector): PlatformRef {
if (isPresent(_platform) && !_platform.destroyed) {
if (_platform && !_platform.destroyed) {
throw new Error(
'There can be only one platform. Destroy the previous one to create a new one.');
}
_platform = injector.get(PlatformRef);
const inits: Function[] = <Function[]>injector.get(PLATFORM_INITIALIZER, null);
if (isPresent(inits)) inits.forEach(init => init());
if (inits) inits.forEach(init => init());
return _platform;
}
@ -106,14 +107,17 @@ export function createPlatformFactory(
* @experimental APIs related to application bootstrap are currently under review.
*/
export function assertPlatform(requiredToken: any): PlatformRef {
var platform = getPlatform();
if (isBlank(platform)) {
const platform = getPlatform();
if (!platform) {
throw new Error('No platform exists!');
}
if (isPresent(platform) && isBlank(platform.injector.get(requiredToken, null))) {
if (!platform.injector.get(requiredToken, null)) {
throw new Error(
'A platform with a different configuration has been created. Please destroy it first.');
}
return platform;
}
@ -123,7 +127,7 @@ export function assertPlatform(requiredToken: any): PlatformRef {
* @experimental APIs related to application bootstrap are currently under review.
*/
export function destroyPlatform(): void {
if (isPresent(_platform) && !_platform.destroyed) {
if (_platform && !_platform.destroyed) {
_platform.destroy();
}
}
@ -134,7 +138,7 @@ export function destroyPlatform(): void {
* @experimental APIs related to application bootstrap are currently under review.
*/
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
throw e;
});
} else {
return result;
}
return result;
} catch (e) {
errorHandler.handleError(e);
// rethrow as the exception handler might not do it
@ -237,7 +241,6 @@ function _callAndReportToErrorHandler(errorHandler: ErrorHandler, callback: () =
export class PlatformRef_ extends PlatformRef {
private _modules: NgModuleRef<any>[] = [];
private _destroyListeners: Function[] = [];
private _destroyed: boolean = false;
constructor(private _injector: Injector) { super(); }
@ -252,8 +255,8 @@ export class PlatformRef_ extends PlatformRef {
if (this._destroyed) {
throw new Error('The platform has already been destroyed!');
}
ListWrapper.clone(this._modules).forEach((app) => app.destroy());
this._destroyListeners.forEach((dispose) => dispose());
this._modules.slice().forEach(module => module.destroy());
this._destroyListeners.forEach(listener => listener());
this._destroyed = true;
}
@ -300,7 +303,7 @@ export class PlatformRef_ extends PlatformRef {
componentFactoryCallback?: any): Promise<NgModuleRef<M>> {
const compilerFactory: CompilerFactory = this.injector.get(CompilerFactory);
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
// 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);
}
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); });
var testability = compRef.injector.get(Testability, null);
if (isPresent(testability)) {
const testability = compRef.injector.get(Testability, null);
if (testability) {
compRef.injector.get(TestabilityRegistry)
.registerApplication(compRef.location.nativeElement, testability);
}
@ -453,7 +456,7 @@ export class ApplicationRef_ extends ApplicationRef {
/** @internal */
_unloadComponent(componentRef: ComponentRef<any>): void {
if (!ListWrapper.contains(this._rootComponents, componentRef)) {
if (this._rootComponents.indexOf(componentRef) == -1) {
return;
}
this.unregisterChangeDetector(componentRef.changeDetectorRef);
@ -465,7 +468,7 @@ export class ApplicationRef_ extends ApplicationRef {
throw new Error('ApplicationRef.tick is called recursively');
}
var s = ApplicationRef_._tickScope();
const scope = ApplicationRef_._tickScope();
try {
this._runningTick = true;
this._changeDetectorRefs.forEach((detector) => detector.detectChanges());
@ -474,13 +477,13 @@ export class ApplicationRef_ extends ApplicationRef {
}
} finally {
this._runningTick = false;
wtfLeave(s);
wtfLeave(scope);
}
}
ngOnDestroy() {
// 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; }

View File

@ -34,7 +34,7 @@ export function _appIdRandomProviderFactory() {
export const APP_ID_RANDOM_PROVIDER = {
provide: APP_ID,
useFactory: _appIdRandomProviderFactory,
deps: <any[]>[]
deps: <any[]>[],
};
function _randomChar(): string {

View File

@ -39,6 +39,7 @@ import * as reflector_reader from './reflection/reflector_reader';
import * as reflection_types from './reflection/types';
import * as api from './render/api';
import * as decorators from './util/decorators';
import {isPromise} from './util/lang';
export var __core_private__: {
isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy,
@ -118,9 +119,11 @@ export var __core_private__: {
ANY_STATE: typeof ANY_STATE_,
DEFAULT_STATE: typeof DEFAULT_STATE_,
EMPTY_STATE: typeof EMPTY_STATE_,
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_, _ComponentStillLoadingError?: ComponentStillLoadingError
ComponentStillLoadingError: typeof ComponentStillLoadingError
} = {
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_,
_ComponentStillLoadingError?: ComponentStillLoadingError,
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise
} = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus,
CHANGE_DETECTION_STRATEGY_VALUES: constants.CHANGE_DETECTION_STRATEGY_VALUES,
@ -185,5 +188,6 @@ export var __core_private__: {
DEFAULT_STATE: DEFAULT_STATE_,
EMPTY_STATE: EMPTY_STATE_,
FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
ComponentStillLoadingError: ComponentStillLoadingError
ComponentStillLoadingError: ComponentStillLoadingError,
isPromise: isPromise
};

View File

@ -51,7 +51,6 @@ export abstract class Injector {
* - Throws {@link NoProviderError} if no `notFoundValue` that is not equal to
* Injector.THROW_IF_NOT_FOUND is given
* - Returns the `notFoundValue` otherwise
* ```
*/
get(token: any, notFoundValue?: any): any { return unimplemented(); }
}

View File

@ -6,7 +6,6 @@
* found in the LICENSE file at https://angular.io/license
*/
import {stringify} from '../facade/lang';
import {makeParamDecorator} from '../util/decorators';
/**
@ -250,8 +249,7 @@ export const SkipSelf: SkipSelfDecorator = makeParamDecorator('SkipSelf', []);
export interface HostDecorator {
/**
* @whatItDoes Specifies that an injector should retrieve a dependency from any injector until
* reaching the
* host element of the current component.
* reaching the host element of the current component.
* @howToUse
* ```
* @Injectable()

View File

@ -121,7 +121,7 @@ export class ReflectiveProtoInjectorDynamicStrategy implements ReflectiveProtoIn
constructor(protoInj: ReflectiveProtoInjector, public providers: ResolvedReflectiveProvider[]) {
var len = providers.length;
this.keyIds = ListWrapper.createFixedSize(len);
this.keyIds = new Array(len);
for (var i = 0; i < len; i++) {
this.keyIds[i] = providers[i].key.id;
@ -286,7 +286,7 @@ export class ReflectiveInjectorDynamicStrategy implements ReflectiveInjectorStra
constructor(
public protoStrategy: ReflectiveProtoInjectorDynamicStrategy,
public injector: ReflectiveInjector_) {
this.objs = ListWrapper.createFixedSize(protoStrategy.providers.length);
this.objs = new Array(protoStrategy.providers.length);
ListWrapper.fill(this.objs, UNDEFINED);
}
@ -648,7 +648,7 @@ export class ReflectiveInjector_ implements ReflectiveInjector {
private _instantiateProvider(provider: ResolvedReflectiveProvider): any {
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) {
res[i] = this._instantiate(provider, provider.resolvedFactories[i]);
}

View File

@ -10,7 +10,6 @@ import {APP_ID} from '../application_tokens';
import {devModeEqual} from '../change_detection/change_detection';
import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {Inject, Injectable} from '../di';
import {ListWrapper} from '../facade/collection';
import {isBlank, isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
@ -77,7 +76,7 @@ export function ensureSlotCount(projectableNodes: any[][], expectedSlotCount: nu
res = EMPTY_ARR;
} else if (projectableNodes.length < expectedSlotCount) {
var givenSlotCount = projectableNodes.length;
res = ListWrapper.createFixedSize(expectedSlotCount);
res = new Array(expectedSlotCount);
for (var i = 0; i < expectedSlotCount; i++) {
res[i] = (i < givenSlotCount) ? projectableNodes[i] : EMPTY_ARR;
}

View File

@ -15,8 +15,6 @@ import {Attribute, ContentChild, ContentChildren, Query, ViewChild, ViewChildren
import {Component, Directive, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';
import {ModuleWithProviders, NgModule, SchemaMetadata} from './metadata/ng_module';
import {ViewEncapsulation} from './metadata/view';
import {Type} from './type';
import {TypeDecorator, makeParamDecorator, makePropDecorator} from './util/decorators';
export {ANALYZE_FOR_ENTRY_COMPONENTS, Attribute, ContentChild, ContentChildDecorator, ContentChildren, ContentChildrenDecorator, Query, ViewChild, ViewChildDecorator, ViewChildren, ViewChildrenDecorator} from './metadata/di';
export {Component, ComponentDecorator, Directive, DirectiveDecorator, HostBinding, HostListener, Input, Output, Pipe} from './metadata/directives';

View File

@ -6,9 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {resolveForwardRef} from '../di/forward_ref';
import {OpaqueToken} from '../di/opaque_token';
import {StringWrapper, isString, stringify} from '../facade/lang';
import {Type} from '../type';
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');
/**
* Type of the Attribute decorator / constructor function.
*
@ -56,55 +53,56 @@ export const ANALYZE_FOR_ENTRY_COMPONENTS = new OpaqueToken('AnalyzeForEntryComp
*/
export interface AttributeDecorator {
/**
* Specifies that a constant attribute value should be injected.
*
* The directive can inject constant string literals of host element attributes.
*
* ### Example
*
* Suppose we have an `<input>` element and want to know its `type`.
*
* ```html
* <input type="text">
* ```
*
* A decorator can inject string literal `text` like so:
*
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
*
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
*
* ### Example as ES5 DSL
*
* ```
* var MyComponent = ng
* .Component({...})
* .Class({
* constructor: [new ng.Attribute('title'), function(title) {
* ...
* }]
* })
* ```
*
* ### Example as ES5 annotation
*
* ```
* var MyComponent = function(title) {
* ...
* };
*
* MyComponent.annotations = [
* new ng.Component({...})
* ]
* MyComponent.parameters = [
* [new ng.Attribute('title')]
* ]
* ```
*
* @stable
*/ (name: string): any;
* Specifies that a constant attribute value should be injected.
*
* The directive can inject constant string literals of host element attributes.
*
* ### Example
*
* Suppose we have an `<input>` element and want to know its `type`.
*
* ```html
* <input type="text">
* ```
*
* A decorator can inject string literal `text` like so:
*
* {@example core/ts/metadata/metadata.ts region='attributeMetadata'}
*
* ### Example as TypeScript Decorator
*
* {@example core/ts/metadata/metadata.ts region='attributeFactory'}
*
* ### Example as ES5 DSL
*
* ```
* var MyComponent = ng
* .Component({...})
* .Class({
* constructor: [new ng.Attribute('title'), function(title) {
* ...
* }]
* })
* ```
*
* ### Example as ES5 annotation
*
* ```
* var MyComponent = function(title) {
* ...
* };
*
* MyComponent.annotations = [
* new ng.Component({...})
* ]
* MyComponent.parameters = [
* [new ng.Attribute('title')]
* ]
* ```
*
* @stable
*/
(name: string): any;
new (name: string): Attribute;
}
@ -210,8 +208,12 @@ export const ContentChildren: ContentChildrenDecorator =
<ContentChildrenDecorator>makePropDecorator(
'ContentChildren',
[
['selector', undefined],
{first: false, isViewQuery: false, descendants: false, read: undefined}
['selector', undefined], {
first: false,
isViewQuery: false,
descendants: false,
read: undefined,
}
],
Query);
@ -273,7 +275,7 @@ export const ContentChild: ContentChildDecorator = makePropDecorator(
['selector', undefined], {
first: true,
isViewQuery: false,
descendants: false,
descendants: true,
read: undefined,
}
],
@ -341,7 +343,6 @@ export const ViewChildren: ViewChildrenDecorator = makePropDecorator(
],
Query);
/**
* Type of the ViewChild decorator / constructor function.
*
@ -370,8 +371,6 @@ export interface ViewChildDecorator {
* * **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/viewChild/view_child_example.ts region='Component'}
*
* **npm package**: `@angular/core`

View File

@ -448,15 +448,15 @@ export interface ComponentDecorator {
* * **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
* 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
* 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
* 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
@ -655,20 +655,33 @@ export interface Component extends Directive {
animations?: AnimationEntryMetadata[];
/**
* 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`}.
* Specifies how the template and the styles should be encapsulated:
* - {@link ViewEncapsulation#Native `ViewEncapsulation.Native`} to use shadow roots - only works
* if natively available on the platform,
* - {@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;
/**
* Overrides the default encapsulation start and end delimiters (respectively `{{` and `}}`)
*/
interpolation?: [string, string];
/**
* Defines the components that should be compiled as well when
* this component is defined. For each components listed here,
* Angular will create a {@link ComponentFactory ComponentFactory} and store it in the
* {@link ComponentFactoryResolver ComponentFactoryResolver}.
* Angular will create a {@link ComponentFactory} and store it in the
* {@link ComponentFactoryResolver}.
*/
entryComponents?: Array<Type<any>|any[]>;
}

View File

@ -7,8 +7,6 @@
*/
import {AnimationEntryMetadata} from '../animation/metadata';
import {Type} from '../type';
/**
* 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.
*
* 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}.
*
* ### Example
@ -61,7 +52,6 @@ export var VIEW_ENCAPSULATION_VALUES =
* @Component({
* selector: 'greet',
* template: 'Hello {{name}}!',
* directives: [GreetUser, Bold]
* })
* class Greet {
* name: string;
@ -73,46 +63,23 @@ export var VIEW_ENCAPSULATION_VALUES =
* ```
*
* @deprecated Use Component instead.
*
* {@link Component}
*/
export class ViewMetadata {
/**
* 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? -->
*/
/** {@link Component.templateUrl} */
templateUrl: string;
/**
* Specifies an inline template for an Angular component.
*
* NOTE: Only one of `templateUrl` or `template` can be defined per View.
*/
/** {@link Component.template} */
template: string;
/**
* Specifies stylesheet URLs for an Angular component.
*
* <!-- TODO: what's the url relative to? -->
*/
/** {@link Component.stylesUrl} */
styleUrls: string[];
/**
* Specifies an inline stylesheet for an Angular component.
*/
/** {@link Component.styles} */
styles: string[];
/**
* 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`}.
*/
/** {@link Component.encapsulation} */
encapsulation: ViewEncapsulation;
/** {@link Component.animation} */
animations: AnimationEntryMetadata[];
/** {@link Component.interpolation} */
interpolation: [string, string];
constructor(

View File

@ -8,7 +8,7 @@
import {PlatformRef, PlatformRef_, createPlatformFactory} from './application_ref';
import {Console} from './console';
import {ClassProvider, ExistingProvider, FactoryProvider, Provider, TypeProvider, ValueProvider} from './di';
import {Provider} from './di';
import {Reflector, reflector} from './reflection/reflection';
import {ReflectorReader} from './reflection/reflector_reader';
import {TestabilityRegistry} from './testability/testability';
@ -18,9 +18,12 @@ function _reflector(): Reflector {
}
const _CORE_PLATFORM_PROVIDERS: Provider[] = [
PlatformRef_, {provide: PlatformRef, useExisting: PlatformRef_},
PlatformRef_,
{provide: PlatformRef, useExisting: PlatformRef_},
{provide: Reflector, useFactory: _reflector, deps: []},
{provide: ReflectorReader, useExisting: Reflector}, TestabilityRegistry, Console
{provide: ReflectorReader, useExisting: Reflector},
TestabilityRegistry,
Console,
];
/**

View File

@ -6,7 +6,7 @@
* 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 {Type} from '../type';
import {PlatformReflectionCapabilities} from './platform_reflection_capabilities';
@ -40,14 +40,9 @@ export class Reflector extends ReflectorReader {
/** @internal */
_methods = new Map<string, MethodFn>();
/** @internal */
_usedKeys: Set<any>;
reflectionCapabilities: PlatformReflectionCapabilities;
_usedKeys: Set<any> = null;
constructor(reflectionCapabilities: PlatformReflectionCapabilities) {
super();
this._usedKeys = null;
this.reflectionCapabilities = reflectionCapabilities;
}
constructor(public reflectionCapabilities: PlatformReflectionCapabilities) { super(); }
updateCapabilities(caps: PlatformReflectionCapabilities) { this.reflectionCapabilities = caps; }

View File

@ -7,7 +7,7 @@
*/
import {Injectable} from '../di';
import {Map, MapWrapper} from '../facade/collection';
import {MapWrapper} from '../facade/collection';
import {scheduleMicroTask} from '../facade/lang';
import {NgZone} from '../zone/ng_zone';
@ -110,6 +110,7 @@ export class Testability implements PublicTestability {
getPendingRequestCount(): number { return this._pendingCount; }
/** @deprecated use findProviders */
findBindings(using: any, provider: string, exactMatch: boolean): any[] {
// TODO(juliemr): implement.
return [];

View File

@ -16,7 +16,7 @@
*
* @stable
*/
export var Type = Function;
export const Type = Function;
export interface Type<T> extends Function { new (...args: any[]): T; }

View 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';
}

View File

@ -6,11 +6,11 @@
* 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 {TestBed, async} from '@angular/core/testing';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, Component, ContentChild, ContentChildren, Directive, QueryList, TemplateRef, Type, ViewChild, ViewChildren, ViewContainerRef, asNativeElements} from '@angular/core';
import {ComponentFixture, TestBed, async} from '@angular/core/testing';
import {expect} from '@angular/platform-browser/testing/matchers';
import {isPresent, stringify} from '../../src/facade/lang';
import {stringify} from '../../src/facade/lang';
export function main() {
describe('Query API', () => {
@ -54,10 +54,7 @@ export function main() {
'<div text="too-deep"></div>' +
'</div></needs-query>' +
'<div text="4"></div>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
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', () => {
const template =
'<needs-content-children #q><div text="foo"></div></needs-content-children>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
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.numberOfChildrenAfterContentInit).toEqual(1);
});
@ -81,19 +73,14 @@ export function main() {
it('should contain the first content child', () => {
const template =
'<needs-content-child #q><div *ngIf="shouldShow" text="foo"></div></needs-content-child>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmp(MyComp0, template);
view.componentInstance.shouldShow = true;
view.detectChanges();
var q: NeedsContentChild = view.debugElement.children[0].references['q'];
const q: NeedsContentChild = view.debugElement.children[0].references['q'];
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
view.componentInstance.shouldShow = false;
view.detectChanges();
expect(q.logs).toEqual([
['setter', 'foo'], ['init', 'foo'], ['check', 'foo'], ['setter', null], ['check', null]
]);
@ -101,17 +88,13 @@ export function main() {
it('should contain the first view child', () => {
const template = '<needs-view-child #q></needs-view-child>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
var q: NeedsViewChild = view.debugElement.children[0].references['q'];
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewChild = view.debugElement.children[0].references['q'];
expect(q.logs).toEqual([['setter', 'foo'], ['init', 'foo'], ['check', 'foo']]);
q.shouldShow = false;
view.detectChanges();
expect(q.logs).toEqual([
['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', () => {
const template =
'<needs-static-content-view-child #q><div text="contentFoo"></div></needs-static-content-view-child>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references['q'];
const view = createTestCmp(MyComp0, template);
const q: NeedsStaticContentAndViewChild = view.debugElement.children[0].references['q'];
expect(q.contentChild.text).toBeFalsy();
expect(q.viewChild.text).toBeFalsy();
view.detectChanges();
expect(q.contentChild.text).toEqual('contentFoo');
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(
MyComp0, {set: {template: '<needs-view-child #q></needs-view-child>'}});
TestBed.overrideComponent(NeedsViewChild, {
@ -145,22 +125,19 @@ export function main() {
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']]);
q.shouldShow = false;
q.shouldShow2 = true;
q.logs = [];
view.detectChanges();
expect(q.logs).toEqual([['setter', 'bar'], ['check', 'bar']]);
q.shouldShow = false;
q.shouldShow2 = false;
q.logs = [];
view.detectChanges();
expect(q.logs).toEqual([['setter', null], ['check', null]]);
});
@ -170,10 +147,8 @@ export function main() {
'<div text="4"></div>' +
'</div></needs-query-desc>' +
'<div text="5"></div>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.detectChanges();
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|4|');
});
@ -181,10 +156,8 @@ export function main() {
const template = '<div text="1"></div>' +
'<needs-query text="2"><div text="3"></div></needs-query>' +
'<div text="4"></div>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.detectChanges();
expect(asNativeElements(view.debugElement.children)).toHaveText('2|3|');
});
@ -192,11 +165,7 @@ export function main() {
const template = '<div text="1"></div>' +
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
'<div text="4"></div>';
;
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
expect(asNativeElements(view.debugElement.children)).toHaveText('2|');
view.componentInstance.shouldShow = true;
@ -208,24 +177,18 @@ export function main() {
const template = '<div text="1"></div>' +
'<needs-query text="2"><div *ngIf="shouldShow" [text]="\'3\'"></div></needs-query>' +
'<div text="4"></div>';
;
TestBed.overrideComponent(MyComp0, {set: {template}});
const fixture = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
fixture.componentInstance.shouldShow = true;
fixture.detectChanges();
fixture.destroy();
view.componentInstance.shouldShow = true;
view.detectChanges();
view.destroy();
});
it('should reflect moved directives', () => {
const template = '<div text="1"></div>' +
'<needs-query text="2"><div *ngFor="let i of list" [text]="i"></div></needs-query>' +
'<div text="4"></div>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
expect(asNativeElements(view.debugElement.children)).toHaveText('2|1d|2d|3d|');
view.componentInstance.list = ['3d', '2d'];
@ -246,11 +209,8 @@ export function main() {
describe('query for TemplateRef', () => {
it('should find TemplateRefs in the light and shadow dom', () => {
const template = '<needs-tpl><template><div>light</div></template></needs-tpl>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
var needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl);
const view = createTestCmpAndDetectChanges(MyComp0, template);
const needsTpl: NeedsTpl = view.debugElement.children[0].injector.get(NeedsTpl);
expect(needsTpl.vc.createEmbeddedView(needsTpl.query.first).rootNodes[0])
.toHaveText('light');
@ -261,11 +221,8 @@ export function main() {
it('should find named TemplateRefs', () => {
const template =
'<needs-named-tpl><template #tpl><div>light</div></template></needs-named-tpl>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
var needsTpl: NeedsNamedTpl = view.debugElement.children[0].injector.get(NeedsNamedTpl);
const view = createTestCmpAndDetectChanges(MyComp0, template);
const needsTpl: NeedsNamedTpl = view.debugElement.children[0].injector.get(NeedsNamedTpl);
expect(needsTpl.vc.createEmbeddedView(needsTpl.contentTpl).rootNodes[0])
.toHaveText('light');
expect(needsTpl.vc.createEmbeddedView(needsTpl.viewTpl).rootNodes[0]).toHaveText('shadow');
@ -276,12 +233,9 @@ export function main() {
it('should contain all content children', () => {
const template =
'<needs-content-children-read #q text="ca"><div #q text="cb"></div></needs-content-children-read>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.detectChanges();
var comp: NeedsContentChildrenWithRead =
const comp: NeedsContentChildrenWithRead =
view.debugElement.children[0].injector.get(NeedsContentChildrenWithRead);
expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['ca', 'cb']);
});
@ -289,36 +243,38 @@ export function main() {
it('should contain the first content child', () => {
const template =
'<needs-content-child-read><div #q text="ca"></div></needs-content-child-read>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
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);
expect(comp.textDirChild.text).toEqual('ca');
});
it('should contain the first view child', () => {
const template = '<needs-view-child-read></needs-view-child-read>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.detectChanges();
var comp: NeedsViewChildWithRead =
const comp: NeedsViewChildWithRead =
view.debugElement.children[0].injector.get(NeedsViewChildWithRead);
expect(comp.textDirChild.text).toEqual('va');
});
it('should contain all child directives in the view', () => {
const template = '<needs-view-children-read></needs-view-children-read>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.detectChanges();
var comp: NeedsViewChildrenWithRead =
const comp: NeedsViewChildrenWithRead =
view.debugElement.children[0].injector.get(NeedsViewChildrenWithRead);
expect(comp.textDirChildren.map(textDirective => textDirective.text)).toEqual(['va', 'vb']);
});
@ -326,12 +282,9 @@ export function main() {
it('should support reading a ViewContainer', () => {
const template =
'<needs-viewcontainer-read><template>hello</template></needs-viewcontainer-read>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.detectChanges();
var comp: NeedsViewContainerWithRead =
const comp: NeedsViewContainerWithRead =
view.debugElement.children[0].injector.get(NeedsViewContainerWithRead);
comp.createView();
expect(view.debugElement.children[0].nativeElement).toHaveText('hello');
@ -344,11 +297,9 @@ export function main() {
'<div text="1"></div>' +
'<div *ngIf="shouldShow" text="2"></div>' +
'</needs-query>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
var q = view.debugElement.children[0].references['q'];
view.detectChanges();
const q = view.debugElement.children[0].references['q'];
q.query.changes.subscribe({
next: () => {
@ -365,23 +316,18 @@ export function main() {
() => {
const template =
'<needs-query #q *ngIf="shouldShow"><div text="foo"></div></needs-query>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
const view = createTestCmpAndDetectChanges(MyComp0, template);
view.componentInstance.shouldShow = true;
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);
view.componentInstance.shouldShow = false;
view.detectChanges();
view.componentInstance.shouldShow = true;
view.detectChanges();
var q2: NeedsQuery = view.debugElement.children[0].references['q'];
const q2: NeedsQuery = view.debugElement.children[0].references['q'];
expect(q2.query.length).toEqual(1);
});
@ -393,15 +339,11 @@ export function main() {
const template = '<needs-query-by-ref-binding #q>' +
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
'</needs-query-by-ref-binding>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q = view.debugElement.children[0].references['q'];
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q'];
view.componentInstance.list = ['1d', '2d'];
view.detectChanges();
expect(q.query.first.text).toEqual('1d');
expect(q.query.last.text).toEqual('2d');
});
@ -411,11 +353,8 @@ export function main() {
'<div text="one" #textLabel1="textDir"></div>' +
'<div text="two" #textLabel2="textDir"></div>' +
'</needs-query-by-ref-bindings>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q'];
expect(q.query.first.text).toEqual('one');
expect(q.query.last.text).toEqual('two');
@ -425,19 +364,13 @@ export function main() {
const template = '<needs-query-by-ref-binding #q>' +
'<div *ngFor="let item of list" [text]="item" #textLabel="textDir"></div>' +
'</needs-query-by-ref-binding>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q = view.debugElement.children[0].references['q'];
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q'];
view.componentInstance.list = ['1d', '2d'];
view.detectChanges();
view.componentInstance.list = ['2d', '1d'];
view.detectChanges();
expect(q.query.last.text).toEqual('1d');
});
@ -447,15 +380,11 @@ export function main() {
'<div #textLabel>{{item}}</div>' +
'</div>' +
'</needs-query-by-ref-binding>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q = view.debugElement.children[0].references['q'];
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q'];
view.componentInstance.list = ['1d', '2d'];
view.detectChanges();
expect(q.query.first.nativeElement).toHaveText('1d');
expect(q.query.last.nativeElement).toHaveText('2d');
});
@ -464,36 +393,23 @@ export function main() {
const template = '<needs-query-and-project #q>' +
'<div text="hello"></div><div text="world"></div>' +
'</needs-query-and-project>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
expect(asNativeElements(view.debugElement.children)).toHaveText('hello|world|');
});
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>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryByLabel = view.debugElement.children[0].references['q'];
expect(q.query.first.nativeElement).toHaveText('text');
});
it('should contain all child directives in the view dom', () => {
const template = '<needs-view-children #q></needs-view-children>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
var q = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q'];
expect(q.textDirChildren.length).toEqual(1);
expect(q.numberOfChildrenAfterViewInit).toEqual(1);
});
@ -502,61 +418,40 @@ export function main() {
describe('querying in the view', () => {
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>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQuery = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQuery = view.debugElement.children[0].references['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
});
it('should not include directive present on the host element', () => {
const template = '<needs-view-query #q text="self"></needs-view-query>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQuery = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQuery = view.debugElement.children[0].references['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
});
it('should reflect changes in the component', () => {
const template = '<needs-view-query-if #q></needs-view-query-if>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQueryIf = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryIf = view.debugElement.children[0].references['q'];
expect(q.query.length).toBe(0);
q.show = true;
view.detectChanges();
expect(q.query.length).toBe(1);
expect(q.query.first.text).toEqual('1');
});
it('should not be affected by other changes in the component', () => {
const template = '<needs-view-query-nested-if #q></needs-view-query-nested-if>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryNestedIf = view.debugElement.children[0].references['q'];
expect(q.query.length).toEqual(1);
expect(q.query.first.text).toEqual('1');
q.show = false;
view.detectChanges();
expect(q.query.length).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',
() => {
const template = '<needs-view-query-order #q></needs-view-query-order>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
q.list = ['-3', '2'];
view.detectChanges();
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',
() => {
const template = '<needs-view-query-order-with-p #q></needs-view-query-order-with-p>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references['q'];
view.detectChanges();
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryOrderWithParent = view.debugElement.children[0].references['q'];
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '2', '3', '4']);
q.list = ['-3', '2'];
view.detectChanges();
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', '-3', '2', '4']);
});
it('should handle long ngFor cycles', () => {
const template = '<needs-view-query-order #q></needs-view-query-order>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
var q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q: NeedsViewQueryOrder = view.debugElement.children[0].references['q'];
// no significance to 50, just a reasonably large cycle.
for (var i = 0; i < 50; i++) {
var newString = i.toString();
for (let i = 0; i < 50; i++) {
const newString = i.toString();
q.list = [newString];
view.detectChanges();
expect(q.query.map((d: TextDirective) => d.text)).toEqual(['1', newString, '4']);
}
});
it('should support more than three queries', () => {
const template = '<needs-four-queries #q><div text="1"></div></needs-four-queries>';
TestBed.overrideComponent(MyComp0, {set: {template}});
const view = TestBed.createComponent(MyComp0);
view.detectChanges();
var q = view.debugElement.children[0].references['q'];
const view = createTestCmpAndDetectChanges(MyComp0, template);
const q = view.debugElement.children[0].references['q'];
expect(q.query1).toBeDefined();
expect(q.query2).toBeDefined();
expect(q.query3).toBeDefined();
@ -663,27 +538,19 @@ class NeedsContentChild implements AfterContentInit, AfterContentChecked {
@ContentChild(TextDirective)
set 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; }
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() {
this.logs.push(['check', isPresent(this.child) ? this.child.text : null]);
}
ngAfterContentChecked() { this.logs.push(['check', this.child ? this.child.text : null]); }
}
@Component({
selector: 'needs-view-child',
template: `
<div *ngIf="shouldShow" text="foo"></div>
`
})
class NeedsViewChild implements AfterViewInit,
AfterViewChecked {
@Component({selector: 'needs-view-child', template: `<div *ngIf="shouldShow" text="foo"></div>`})
class NeedsViewChild implements AfterViewInit, AfterViewChecked {
shouldShow: boolean = true;
shouldShow2: boolean = false;
/** @internal */
@ -692,34 +559,37 @@ class NeedsViewChild implements AfterViewInit,
@ViewChild(TextDirective)
set 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; }
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() {
this.logs.push(['check', isPresent(this.child) ? this.child.text : null]);
}
ngAfterViewChecked() { this.logs.push(['check', this.child ? this.child.text : null]); }
}
@Component({
selector: 'needs-static-content-view-child',
template: `
<div text="viewFoo"></div>
`
})
function createTestCmp<T>(type: Type<T>, template: string): ComponentFixture<T> {
const view = TestBed.overrideComponent(type, {set: {template}}).createComponent(type);
return view;
}
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 {
@ContentChild(TextDirective) contentChild: TextDirective;
@ViewChild(TextDirective) viewChild: TextDirective;
}
@Directive({selector: '[dir]'})
class InertDirective {
constructor() {}
}
@Component({
@ -780,9 +650,8 @@ class NeedsViewQuery {
@Component({selector: 'needs-view-query-if', template: '<div *ngIf="show" text="1"></div>'})
class NeedsViewQueryIf {
show: boolean;
show: boolean = false;
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
constructor() { this.show = false; }
}
@Component({
@ -790,9 +659,8 @@ class NeedsViewQueryIf {
template: '<div text="1"><div *ngIf="show"><div dir></div></div></div>'
})
class NeedsViewQueryNestedIf {
show: boolean;
show: boolean = true;
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
constructor() { this.show = true; }
}
@Component({
@ -803,8 +671,7 @@ class NeedsViewQueryNestedIf {
})
class NeedsViewQueryOrder {
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
list: string[];
constructor() { this.list = ['2', '3']; }
list: string[] = ['2', '3'];
}
@Component({
@ -815,8 +682,7 @@ class NeedsViewQueryOrder {
})
class NeedsViewQueryOrderWithParent {
@ViewChildren(TextDirective) query: QueryList<TextDirective>;
list: string[];
constructor() { this.list = ['2', '3']; }
list: string[] = ['2', '3'];
}
@Component({selector: 'needs-tpl', template: '<template><div>shadow</div></template>'})
@ -879,12 +745,8 @@ class HasNullQueryCondition {
@Component({selector: 'my-comp', template: ''})
class MyComp0 {
shouldShow: boolean;
list: any /** TODO #9100 */;
constructor() {
this.shouldShow = false;
this.list = ['1d', '2d', '3d'];
}
shouldShow: boolean = false;
list: string[] = ['1d', '2d', '3d'];
}
@Component({selector: 'my-comp', template: ''})

View File

@ -79,6 +79,24 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(fixture.nativeElement).toHaveText('counting method value');
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', () => {

View File

@ -6,9 +6,8 @@
* found in the LICENSE file at https://angular.io/license
*/
import {CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA} from '@angular/core';
import {Component} from '@angular/core/src/metadata';
import {TestBed, getTestBed} from '@angular/core/testing';
import {Component, Directive, Input, NO_ERRORS_SCHEMA} from '@angular/core';
import {ComponentFixture, TestBed, getTestBed} from '@angular/core/testing';
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 {DomSanitizer} from '@angular/platform-browser/src/security/dom_sanitization_service';
@ -24,12 +23,22 @@ class SecuredComponent {
ctxProp: any = 'some value';
}
@Directive({selector: '[onPrefixedProp]'})
class OnPrefixDir {
@Input() onPrefixedProp: any;
@Input() onclick: any;
}
function declareTests({useJit}: {useJit: boolean}) {
describe('security integration tests', function() {
beforeEach(() => {
TestBed.configureCompiler({useJit: useJit});
TestBed.configureTestingModule({declarations: [SecuredComponent]});
TestBed.configureCompiler({useJit: useJit}).configureTestingModule({
declarations: [
SecuredComponent,
OnPrefixDir,
]
});
});
let originalLog: (msg: any) => any;
@ -43,15 +52,10 @@ function declareTests({useJit}: {useJit: boolean}) {
it('should disallow binding to attr.on*', () => {
const template = `<div [attr.onclick]="ctxProp"></div>`;
TestBed.overrideComponent(SecuredComponent, {set: {template}});
try {
TestBed.createComponent(SecuredComponent);
throw 'Should throw';
} catch (e) {
expect(e.message).toContain(
`Template parse errors:\n` +
`Binding to event attribute 'onclick' is disallowed ` +
`for security reasons, please use (click)=... `);
}
expect(() => TestBed.createComponent(SecuredComponent))
.toThrowError(
/Binding to event attribute 'onclick' is disallowed for security reasons, please use \(click\)=.../);
});
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({
schemas: [NO_ERRORS_SCHEMA]
});
;
try {
TestBed.createComponent(SecuredComponent);
throw 'Should throw';
} catch (e) {
expect(e.message).toContain(
`Template parse errors:\n` +
`Binding to event attribute 'onclick' is disallowed ` +
`for security reasons, please use (click)=... `);
}
expect(() => TestBed.createComponent(SecuredComponent))
.toThrowError(
/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() {
@ -157,12 +175,8 @@ function declareTests({useJit}: {useJit: boolean}) {
const template = `<svg:circle [xlink:href]="ctxProp">Text</svg:circle>`;
TestBed.overrideComponent(SecuredComponent, {set: {template}});
try {
TestBed.createComponent(SecuredComponent);
throw 'Should throw';
} catch (e) {
expect(e.message).toContain(`Can't bind to 'xlink:href'`);
}
expect(() => TestBed.createComponent(SecuredComponent))
.toThrowError(/Can't bind to 'xlink:href'/);
});
it('should escape unsafe HTML values', () => {

View 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);
});
});
}

View 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;

View File

@ -9,8 +9,7 @@
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 {ComponentFixture} from './component_fixture';
import {ListWrapper} from './facade/collection';
import {FunctionWrapper, stringify} from './facade/lang';
import {stringify} from './facade/lang';
import {MetadataOverride} from './metadata_override';
import {TestingCompiler, TestingCompilerFactory} from './test_compiler';
@ -25,27 +24,36 @@ export class TestComponentRenderer {
insertRootElement(rootElementId: string) {}
}
var _nextRootElementId = 0;
let _nextRootElementId = 0;
/**
* @experimental
*/
export var ComponentFixtureAutoDetect = new OpaqueToken('ComponentFixtureAutoDetect');
export const ComponentFixtureAutoDetect = new OpaqueToken('ComponentFixtureAutoDetect');
/**
* @experimental
*/
export var ComponentFixtureNoNgZone = new OpaqueToken('ComponentFixtureNoNgZone');
export const ComponentFixtureNoNgZone = new OpaqueToken('ComponentFixtureNoNgZone');
/**
* @experimental
*/
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 {
/**
@ -63,7 +71,7 @@ export class TestBed implements Injector {
*/
static initTestEnvironment(ngModule: Type<any>, platform: PlatformRef): TestBed {
const testBed = getTestBed();
getTestBed().initTestEnvironment(ngModule, platform);
testBed.initTestEnvironment(ngModule, platform);
return testBed;
}
@ -216,16 +224,16 @@ export class TestBed implements Injector {
configureTestingModule(moduleDef: TestModuleMetadata) {
this._assertNotInstantiated('TestBed.configureTestingModule', 'configure the test module');
if (moduleDef.providers) {
this._providers = ListWrapper.concat(this._providers, moduleDef.providers);
this._providers.push(...moduleDef.providers);
}
if (moduleDef.declarations) {
this._declarations = ListWrapper.concat(this._declarations, moduleDef.declarations);
this._declarations.push(...moduleDef.declarations);
}
if (moduleDef.imports) {
this._imports = ListWrapper.concat(this._imports, moduleDef.imports);
this._imports.push(...moduleDef.imports);
}
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,
// 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;
}
execute(tokens: any[], fn: Function): any {
this._initIfNeeded();
var params = tokens.map(t => this.get(t));
return FunctionWrapper.apply(fn, params);
const params = tokens.map(t => this.get(t));
return fn(...params);
}
overrideModule(ngModule: Type<any>, override: MetadataOverride<NgModule>): void {
@ -350,26 +358,23 @@ export class TestBed implements Injector {
testComponentRenderer.insertRootElement(rootElId);
const initComponent = () => {
var componentRef = componentFactory.create(this, [], `#${rootElId}`);
const componentRef = componentFactory.create(this, [], `#${rootElId}`);
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);
return fixture;
}
}
var _testBed: TestBed = null;
let _testBed: TestBed = null;
/**
* @experimental
*/
export function getTestBed() {
if (_testBed == null) {
_testBed = new TestBed();
}
return _testBed;
return _testBed = _testBed || new TestBed();
}
/**
@ -397,14 +402,14 @@ export function getTestBed() {
* @stable
*/
export function inject(tokens: any[], fn: Function): () => any {
let testBed = getTestBed();
const testBed = getTestBed();
if (tokens.indexOf(AsyncTestCompleter) >= 0) {
return () =>
// Return an async test method that returns a Promise if AsyncTestCompleter is one of
// the
// injected tokens.
testBed.compileComponents().then(() => {
let completer: AsyncTestCompleter = testBed.get(AsyncTestCompleter);
const completer: AsyncTestCompleter = testBed.get(AsyncTestCompleter);
testBed.execute(tokens, fn);
return completer.promise;
});

View File

@ -8,7 +8,8 @@
import {AsyncTestCompleter} from './async_test_completer';
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';
export {AsyncTestCompleter} from './async_test_completer';

View File

@ -7,7 +7,7 @@
*/
import {Component, Directive, Host, Inject, Injectable, Optional, ReflectiveInjector, Self, SkipSelf} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {ComponentFixture, TestBed} from '@angular/core/testing';
export function main() {
describe('di metadata examples', () => {
@ -140,26 +140,28 @@ export function main() {
@Directive({selector: 'child-directive'})
class ChildDirective {
logs: string[] = [];
constructor(@Optional() @Host() os: OtherService, @Optional() @Host() hs: HostService) {
console.log('os is null', os);
console.log('hs is NOT null', hs);
// os is null: true
this.logs.push(`os is null: ${os === null}`);
// hs is an instance of HostService: true
this.logs.push(`hs is an instance of HostService: ${hs instanceof HostService}`);
}
}
@Component({
selector: 'parent-cmp',
providers: [HostService],
template: `
Dir: <child-directive></child-directive>
`
viewProviders: [HostService],
template: '<child-directive></child-directive>',
})
class ParentCmp {
}
@Component({
selector: 'app',
providers: [OtherService],
template: `Parent: <parent-cmp></parent-cmp>`
viewProviders: [OtherService],
template: '<parent-cmp></parent-cmp>',
})
class App {
}
@ -168,7 +170,14 @@ export function main() {
TestBed.configureTestingModule({
declarations: [App, ParentCmp, ChildDirective],
});
expect(() => TestBed.createComponent(App)).not.toThrow();
let cmp: ComponentFixture<App>;
expect(() => cmp = TestBed.createComponent(App)).not.toThrow();
expect(cmp.debugElement.children[0].children[0].injector.get(ChildDirective).logs).toEqual([
'os is null: true',
'hs is an instance of HostService: true',
]);
});
});

View File

@ -0,0 +1,41 @@
/**
* @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';
describe('radioButtons example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: protractor.ElementArrayFinder;
let paragraphs: protractor.ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/radioButtons/index.html');
inputs = element.all(by.css('input'));
paragraphs = element.all(by.css('p'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('checked')).toEqual(null);
expect(inputs.get(1).getAttribute('checked')).toEqual('true');
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(paragraphs.get(0).getText()).toEqual('Form value: { "food": "lamb" }');
expect(paragraphs.get(1).getText()).toEqual('myFood value: lamb');
});
it('update model and other buttons as the UI value changes', () => {
inputs.get(0).click();
expect(inputs.get(0).getAttribute('checked')).toEqual('true');
expect(inputs.get(1).getAttribute('checked')).toEqual(null);
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(paragraphs.get(0).getText()).toEqual('Form value: { "food": "beef" }');
expect(paragraphs.get(1).getText()).toEqual('myFood value: beef');
});
});

View File

@ -0,0 +1,20 @@
/**
* @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 {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {RadioButtonComp} from './radio_button_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [RadioButtonComp],
bootstrap: [RadioButtonComp]
})
export class AppModule {
}

View 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
*/
// #docregion TemplateDriven
import {Component} from '@angular/core';
@Component({
selector: 'example-app',
template: `
<form #f="ngForm">
<input type="radio" value="beef" name="food" [(ngModel)]="myFood"> Beef
<input type="radio" value="lamb" name="food" [(ngModel)]="myFood"> Lamb
<input type="radio" value="fish" name="food" [(ngModel)]="myFood"> Fish
</form>
<p>Form value: {{ f.value | json }}</p> <!-- {food: 'lamb' } -->
<p>myFood value: {{ myFood }}</p> <!-- 'lamb' -->
`,
})
export class RadioButtonComp {
myFood = 'lamb';
}
// #enddocregion

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('radioButtons example', () => {
afterEach(verifyNoBrowserErrors);
let inputs: protractor.ElementArrayFinder;
beforeEach(() => {
browser.get('/forms/ts/reactiveRadioButtons/index.html');
inputs = element.all(by.css('input'));
});
it('should populate the UI with initial values', () => {
expect(inputs.get(0).getAttribute('checked')).toEqual(null);
expect(inputs.get(1).getAttribute('checked')).toEqual('true');
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(element(by.css('p')).getText()).toEqual('Form value: { "food": "lamb" }');
});
it('update model and other buttons as the UI value changes', () => {
inputs.get(0).click();
expect(inputs.get(0).getAttribute('checked')).toEqual('true');
expect(inputs.get(1).getAttribute('checked')).toEqual(null);
expect(inputs.get(2).getAttribute('checked')).toEqual(null);
expect(element(by.css('p')).getText()).toEqual('Form value: { "food": "beef" }');
});
});

View File

@ -0,0 +1,20 @@
/**
* @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 {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {ReactiveRadioButtonComp} from './reactive_radio_button_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [ReactiveRadioButtonComp],
bootstrap: [ReactiveRadioButtonComp]
})
export class AppModule {
}

View File

@ -0,0 +1,30 @@
/**
* @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
*/
// #docregion Reactive
import {Component} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form">
<input type="radio" formControlName="food" value="beef" > Beef
<input type="radio" formControlName="food" value="lamb"> Lamb
<input type="radio" formControlName="food" value="fish"> Fish
</form>
<p>Form value: {{ form.value | json }}</p> <!-- {food: 'lamb' } -->
`,
})
export class ReactiveRadioButtonComp {
form = new FormGroup({
food: new FormControl('lamb'),
});
}
// #enddocregion

View File

@ -0,0 +1,36 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('reactiveSelectControl example', () => {
afterEach(verifyNoBrowserErrors);
let select: protractor.ElementFinder;
let options: protractor.ElementArrayFinder;
let p: protractor.ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/reactiveSelectControl/index.html');
select = element(by.css('select'));
options = element.all(by.css('option'));
p = element(by.css('p'));
});
it('should populate the initial selection', () => {
expect(select.getAttribute('value')).toEqual('3: Object');
expect(options.get(3).getAttribute('selected')).toBe('true');
});
it('should update the model when the value changes in the UI', () => {
select.click();
options.get(0).click();
expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }');
});
});

View File

@ -0,0 +1,20 @@
/**
* @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 {NgModule} from '@angular/core';
import {ReactiveFormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {ReactiveSelectComp} from './reactive_select_control_example';
@NgModule({
imports: [BrowserModule, ReactiveFormsModule],
declarations: [ReactiveSelectComp],
bootstrap: [ReactiveSelectComp]
})
export class AppModule {
}

View File

@ -0,0 +1,41 @@
/**
* @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
*/
// #docregion Component
import {Component} from '@angular/core';
import {FormControl, FormGroup} from '@angular/forms';
@Component({
selector: 'example-app',
template: `
<form [formGroup]="form">
<select formControlName="state">
<option *ngFor="let state of states" [ngValue]="state">
{{ state.abbrev }}
</option>
</select>
</form>
<p>Form value: {{ form.value | json }}</p>
<!-- {state: {name: 'New York', abbrev: 'NY'} } -->
`,
})
export class ReactiveSelectComp {
states = [
{name: 'Arizona', abbrev: 'AZ'},
{name: 'California', abbrev: 'CA'},
{name: 'Colorado', abbrev: 'CO'},
{name: 'New York', abbrev: 'NY'},
{name: 'Pennsylvania', abbrev: 'PA'},
];
form = new FormGroup({
state: new FormControl(this.states[3]),
});
}
// #enddocregion

View 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 {verifyNoBrowserErrors} from '../../../../_common/e2e_util';
describe('selectControl example', () => {
afterEach(verifyNoBrowserErrors);
let select: protractor.ElementFinder;
let options: protractor.ElementArrayFinder;
let p: protractor.ElementFinder;
beforeEach(() => {
browser.get('/forms/ts/selectControl/index.html');
select = element(by.css('select'));
options = element.all(by.css('option'));
p = element(by.css('p'));
});
it('should initially select the placeholder option',
() => { expect(options.get(0).getAttribute('selected')).toBe('true'); });
it('should update the model when the value changes in the UI', () => {
select.click();
options.get(1).click();
expect(p.getText()).toEqual('Form value: { "state": { "name": "Arizona", "abbrev": "AZ" } }');
});
});

View File

@ -0,0 +1,20 @@
/**
* @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 {NgModule} from '@angular/core';
import {FormsModule} from '@angular/forms';
import {BrowserModule} from '@angular/platform-browser';
import {SelectControlComp} from './select_control_example';
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [SelectControlComp],
bootstrap: [SelectControlComp]
})
export class AppModule {
}

View File

@ -0,0 +1,37 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
// #docregion Component
import {Component} from '@angular/core';
@Component({
selector: 'example-app',
template: `
<form #f="ngForm">
<select name="state" ngModel>
<option value="" disabled>Choose a state</option>
<option *ngFor="let state of states" [ngValue]="state">
{{ state.abbrev }}
</option>
</select>
</form>
<p>Form value: {{ f.value | json }}</p>
<!-- example value: {state: {name: 'New York', abbrev: 'NY'} } -->
`,
})
export class SelectControlComp {
states = [
{name: 'Arizona', abbrev: 'AZ'},
{name: 'California', abbrev: 'CA'},
{name: 'Colorado', abbrev: 'CO'},
{name: 'New York', abbrev: 'NY'},
{name: 'Pennsylvania', abbrev: 'PA'},
];
}
// #enddocregion

View File

@ -8,12 +8,9 @@
import {getSymbolIterator, global, isArray, isBlank, isJsObject, isPresent} from './lang';
export var Map = global.Map;
export var Set = global.Set;
// Safari and Internet Explorer do not support the iterable parameter to the
// Map constructor. We work around that by manually adding the items.
var createMapFromPairs: {(pairs: any[]): Map<any, any>} = (function() {
const createMapFromPairs: {(pairs: any[]): Map<any, any>} = (function() {
try {
if (new Map(<any>[[1, 2]]).size === 1) {
return function createMapFromPairs(pairs: any[]): Map<any, any> { return new Map(pairs); };
@ -29,7 +26,7 @@ var createMapFromPairs: {(pairs: any[]): Map<any, any>} = (function() {
return map;
};
})();
var createMapFromMap: {(m: Map<any, any>): Map<any, any>} = (function() {
const createMapFromMap: {(m: Map<any, any>): Map<any, any>} = (function() {
try {
if (new Map(<any>new Map())) {
return function createMapFromMap(m: Map<any, any>): Map<any, any> { return new Map(<any>m); };
@ -42,7 +39,7 @@ var createMapFromMap: {(m: Map<any, any>): Map<any, any>} = (function() {
return map;
};
})();
var _clearValues: {(m: Map<any, any>): void} = (function() {
const _clearValues: {(m: Map<any, any>): void} = (function() {
if ((<any>(new Map()).keys()).next) {
return function _clearValues(m: Map<any, any>) {
var keyIterator = m.keys();
@ -69,7 +66,7 @@ var _arrayFromMap: {(m: Map<any, any>, getValues: boolean): any[]} = (function()
} catch (e) {
}
return function createArrayFromMapWithForeach(m: Map<any, any>, getValues: boolean): any[] {
var res = ListWrapper.createFixedSize(m.size), i = 0;
var res = new Array(m.size), i = 0;
m.forEach((v, k) => {
res[i] = getValues ? v : k;
i++;
@ -79,7 +76,6 @@ var _arrayFromMap: {(m: Map<any, any>, getValues: boolean): any[]} = (function()
})();
export class MapWrapper {
static clone<K, V>(m: Map<K, V>): Map<K, V> { return createMapFromMap(m); }
static createFromStringMap<T>(stringMap: {[key: string]: T}): Map<string, T> {
var result = new Map<string, T>();
for (var prop in stringMap) {
@ -93,7 +89,6 @@ export class MapWrapper {
return r;
}
static createFromPairs(pairs: any[]): Map<any, any> { return createMapFromPairs(pairs); }
static clearValues(m: Map<any, any>) { _clearValues(m); }
static iterable<T>(m: T): T { return m; }
static keys<K>(m: Map<K, any>): K[] { return _arrayFromMap(m, false); }
static values<V>(m: Map<any, V>): V[] { return _arrayFromMap(m, true); }
@ -103,15 +98,6 @@ export class MapWrapper {
* Wraps Javascript Objects
*/
export class StringMapWrapper {
static create(): {[k: /*any*/ string]: any} {
// Note: We are not using Object.create(null) here due to
// performance!
// http://jsperf.com/ng2-object-create-null
return {};
}
static contains(map: {[key: string]: any}, key: string): boolean {
return map.hasOwnProperty(key);
}
static get<V>(map: {[key: string]: V}, key: string): V {
return map.hasOwnProperty(key) ? map[key] : undefined;
}
@ -127,7 +113,6 @@ export class StringMapWrapper {
}
return true;
}
static delete (map: {[key: string]: any}, key: string) { delete map[key]; }
static forEach<K, V>(map: {[key: string]: V}, callback: (v: V, K: string) => void) {
for (let k of Object.keys(map)) {
callback(map[k], k);

View File

@ -14,15 +14,13 @@ export function unimplemented(): any {
* @stable
*/
export class BaseError extends Error {
/**
* @internal
*/
/** @internal **/
_nativeError: Error;
constructor(message: string) {
// Errors don't use current this, instead they create a new instance.
// We have to do forward all of our api to the nativeInstance.
var nativeError = super(message) as any as Error;
const nativeError = super(message) as any as Error;
this._nativeError = nativeError;
}
@ -40,11 +38,6 @@ export class BaseError extends Error {
export class WrappedError extends BaseError {
originalError: any;
/**
* @internal
*/
_nativeError: Error;
constructor(message: string, error: any) {
super(`${message} caused by: ${error instanceof Error ? error.message: error }`);
this.originalError = error;

View File

@ -113,12 +113,6 @@ export function isStrictStringMap(obj: any): boolean {
return isStringMap(obj) && Object.getPrototypeOf(obj) === STRING_MAP_PROTO;
}
export function isPromise(obj: any): boolean {
// allow any Promise/A+ compliant thenable.
// It's up to the caller to ensure that obj.then conforms to the spec
return isPresent(obj) && isFunction(obj.then);
}
export function isArray(obj: any): boolean {
return Array.isArray(obj);
}

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {NumberWrapper, StringWrapper, escapeRegExp, hasConstructor, isPresent, isPromise, resolveEnumToken} from '../src/lang';
import {NumberWrapper, StringWrapper, escapeRegExp, hasConstructor, isPresent, resolveEnumToken} from '../src/lang';
enum UsefulEnum {
MyToken,
@ -153,22 +153,4 @@ export function main() {
() => { expect(hasConstructor(new MySubclass(), MySuperclass)).toEqual(false); });
});
});
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: function() {}})).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);
});
});
}

View File

@ -76,6 +76,11 @@ const resolvedPromise = Promise.resolve(null);
*
* {@example forms/ts/simpleForm/simple_form_example.ts region='Component'}
*
* To see `ngModel` examples with different form control types, see:
*
* * Radio buttons: {@link RadioControlValueAccessor}
* * Selects: {@link SelectControlValueAccessor}
*
* **npm package**: `@angular/forms`
*
* **NgModule**: `FormsModule`
@ -195,7 +200,9 @@ export class NgModel extends NgControl implements OnChanges,
private _updateDisabled(changes: SimpleChanges) {
const disabledValue = changes['isDisabled'].currentValue;
const isDisabled = disabledValue != null && disabledValue != false;
const isDisabled =
disabledValue === '' || (disabledValue && disabledValue !== 'false');
resolvedPromise.then(() => {
if (isDisabled && !this.control.disabled) {

View File

@ -59,21 +59,33 @@ export class RadioControlRegistry {
}
/**
* The accessor for writing a radio control value and listening to changes that is used by the
* {@link NgModel}, {@link FormControlDirective}, and {@link FormControlName} directives.
* @whatItDoes Writes radio control values and listens to radio control changes.
*
* ### Example
* ```
* @Component({
* template: `
* <input type="radio" name="food" [(ngModel)]="food" value="chicken">
* <input type="radio" name="food" [(ngModel)]="food" value="fish">
* `
* })
* class FoodCmp {
* food = 'chicken';
* }
* ```
* Used by {@link NgModel}, {@link FormControlDirective}, and {@link FormControlName}
* to keep the view synced with the {@link FormControl} model.
*
* @howToUse
*
* If you have imported the {@link FormsModule} or the {@link ReactiveFormsModule}, this
* value accessor will be active on any radio control that has a form directive. You do
* **not** need to add a special selector to activate it.
*
* ### How to use radio buttons with form directives
*
* To use radio buttons in a template-driven form, you'll want to ensure that radio buttons
* in the same group have the same `name` attribute. Radio buttons with different `name`
* attributes do not affect each other.
*
* {@example forms/ts/radioButtons/radio_button_example.ts region='TemplateDriven'}
*
* When using radio buttons in a reactive form, radio buttons in the same group should have the
* same `formControlName`. You can also add a `name` attribute, but it's optional.
*
* {@example forms/ts/reactiveRadioButtons/reactive_radio_button_example.ts region='Reactive'}
*
* * **npm package**: `@angular/forms`
*
* @stable
*/
@Directive({
selector:

View File

@ -9,7 +9,6 @@
import {Directive, Inject, Input, OnChanges, Optional, Output, Self, SimpleChanges, forwardRef} from '@angular/core';
import {EventEmitter} from '../../facade/async';
import {StringMapWrapper} from '../../facade/collection';
import {FormControl} from '../../model';
import {NG_ASYNC_VALIDATORS, NG_VALIDATORS} from '../../validators';
import {ControlValueAccessor, NG_VALUE_ACCESSOR} from '../control_value_accessor';
@ -90,7 +89,9 @@ export class FormControlDirective extends NgControl implements OnChanges {
ngOnChanges(changes: SimpleChanges): void {
if (this._isControlChanged(changes)) {
setUpControl(this.form, this);
if (this.control.disabled) this.valueAccessor.setDisabledState(true);
if (this.control.disabled && this.valueAccessor.setDisabledState) {
this.valueAccessor.setDisabledState(true);
}
this.form.updateValueAndValidity({emitEvent: false});
}
if (isPropertyUpdated(changes, this.viewModel)) {
@ -115,6 +116,6 @@ export class FormControlDirective extends NgControl implements OnChanges {
}
private _isControlChanged(changes: {[key: string]: any}): boolean {
return StringMapWrapper.contains(changes, 'form');
return changes.hasOwnProperty('form');
}
}

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